Home > Software engineering >  TextBox Not using Readonly color if background has been changed
TextBox Not using Readonly color if background has been changed

Time:06-23

It seems that when you set the BackColor of a TextBox, Readonly ceases to affect the BackColor from then on. I have user control where, a decoration engine tracks validation, and highlights textboxes a certain color when the value has been validated. However, once I've assigned Color.Yellow or SystemColors.Window to the TextBox.BackColor, setting TextBox.Readonly does nothing.

This is extremely easy to reproduce:

        public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }



        private void button1_Click(object sender, EventArgs e)
        {
            textBox1.ReadOnly = !textBox1.ReadOnly;
        }


        private void button2_Click(object sender, EventArgs e)
        {
            textBox2.ReadOnly = !textBox2.ReadOnly;
        }

        private void button3_Click(object sender, EventArgs e)
        {
            textBox2.BackColor = Color.Blue;
        }

        private void textBox1_ReadOnlyChanged(object sender, EventArgs e)
        {
            label1.Text = $"Readonly: {textBox1.ReadOnly}{Environment.NewLine}Color: {textBox1.BackColor}";
        }

        private void textBox2_ReadOnlyChanged(object sender, EventArgs e)
        {
            label2.Text = $"Readonly: {textBox2.ReadOnly}{Environment.NewLine}Color: {textBox2.BackColor}";
        }

        private void InitializeComponent()
        {
            this.button1 = new System.Windows.Forms.Button();
            this.button2 = new System.Windows.Forms.Button();
            this.button3 = new System.Windows.Forms.Button();
            this.textBox1 = new System.Windows.Forms.TextBox();
            this.textBox2 = new System.Windows.Forms.TextBox();
            this.label1 = new System.Windows.Forms.Label();
            this.label2 = new System.Windows.Forms.Label();
            this.SuspendLayout();
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(138, 286);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(75, 23);
            this.button1.TabIndex = 1;
            this.button1.Text = "button1";
            this.button1.UseVisualStyleBackColor = true;
            this.button1.Click  = new System.EventHandler(this.button1_Click);
            // 
            // button2
            // 
            this.button2.Location = new System.Drawing.Point(219, 286);
            this.button2.Name = "button2";
            this.button2.Size = new System.Drawing.Size(75, 23);
            this.button2.TabIndex = 3;
            this.button2.Text = "button2";
            this.button2.UseVisualStyleBackColor = true;
            this.button2.Click  = new System.EventHandler(this.button2_Click);
            // 
            // button3
            // 
            this.button3.Location = new System.Drawing.Point(300, 286);
            this.button3.Name = "button3";
            this.button3.Size = new System.Drawing.Size(75, 23);
            this.button3.TabIndex = 4;
            this.button3.Text = "button3";
            this.button3.UseVisualStyleBackColor = true;
            this.button3.Click  = new System.EventHandler(this.button3_Click);
            // 
            // textBox1
            // 
            this.textBox1.Location = new System.Drawing.Point(108, 190);
            this.textBox1.Name = "textBox1";
            this.textBox1.Size = new System.Drawing.Size(100, 22);
            this.textBox1.TabIndex = 5;
            this.textBox1.ReadOnlyChanged  = new System.EventHandler(this.textBox1_ReadOnlyChanged);
            // 
            // textBox2
            // 
            this.textBox2.Location = new System.Drawing.Point(308, 190);
            this.textBox2.Name = "textBox2";
            this.textBox2.Size = new System.Drawing.Size(100, 22);
            this.textBox2.TabIndex = 6;
            this.textBox2.ReadOnlyChanged  = new System.EventHandler(this.textBox2_ReadOnlyChanged);
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(105, 215);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(76, 17);
            this.label1.TabIndex = 7;
            this.label1.Text = "Readonly: ";
            // 
            // label2
            // 
            this.label2.AutoSize = true;
            this.label2.Location = new System.Drawing.Point(305, 215);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(72, 17);
            this.label2.TabIndex = 8;
            this.label2.Text = "Readonly:";
            // 
            // Form1
            // 
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
            this.ClientSize = new System.Drawing.Size(800, 450);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.textBox2);
            this.Controls.Add(this.textBox1);
            this.Controls.Add(this.button3);
            this.Controls.Add(this.button2);
            this.Controls.Add(this.button1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);
            this.PerformLayout();

        }

#endregion
        private System.Windows.Forms.Button button1;
        private System.Windows.Forms.Button button2;
        private System.Windows.Forms.Button button3;
        private System.Windows.Forms.TextBox textBox1;
        private System.Windows.Forms.TextBox textBox2;
        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.Label label2;
    }

Simply run that form, click button1 and button2 repeatedly, and both textboxes will cycle from default Window to Control background colors as the readonly property changes. Button3, sets the textbox2 backcolor to Blue, and from then on, clicking button2 has no affect on the backcolor.

I Do NOT want this behavior. I want to affect the backcolor, but I want readonly to supersede any custom backcolor i have assigned.

How do i achieve this?

CodePudding user response:

Your question as I understand it is how to achieve a visual state where the textbox color uses the default value of SystemColors.Control when the TextBox is in the ReadOnly state and a different custom color when it's not.

The easiest way I know of to do that is to handle the ReadOnlyChanged event of the TextBox controls. For example:

public Form1()
{
    InitializeComponent();
    textBox2.ReadOnlyChanged  = (sender, e) =>
    {
        textBox2.BackColor = textBox2.ReadOnly ? SystemColors.Control : Color.Blue;
    };
}

and also checking the state first when you set the custom background color:

private void button3_Click(object sender, EventArgs e)
{
    textBox2.BackColor = textBox2.ReadOnly ? SystemColors.Control : Color.Blue;
}

CodePudding user response:

Well, apparently, because of "reasons", there is not "slick" way to achieve this, but i did find that if you set the BackColor = Color.Empty, the readonly behavior returns. THis is due to the unchangeable code in TextBoxBase:

       public override Color BackColor {
        get {
            if (ShouldSerializeBackColor()) {
                return base.BackColor;
            }
            else if (this.ReadOnly) {
                return SystemColors.Control;
            } else {
                return SystemColors.Window;
            }
        }
        set {
            base.BackColor = value;
        }
    }

The ShouldSerializeBackColor() will return false if you set the backcolor to Color.Empty, allowing the second part of that if-else condition to process, and voila. It ain't pretty, but at least it works.

  • Related