I know this should be easy, but I am stumped at this point. I have a custom TextBox that has a property ControlState. ControlState points to an Enum with values like Normal, Warning, Error, etc. When ControlState gets changed, it paints the BackColor accordingly. That works great. Where my problem comes in is when the user has changed the BackColor at design time to say Green. When a Warning state occurs and the BackColor is set to say Yellow and then the Warning state is fixed, I want to paint it back to Green. However, I cannot figure out where to pickup the color that was selected at design time (or the default color if nothing was set at design time).
Here are some things I have tried:
Code that applies to all of the scenarios:
Code:
<System.ComponentModel.Description("Sets the control state to Warning or Error. Changes the border color based upon the selection."), System.ComponentModel.Category("Validation")> _
Property ControlState() As ControlStates
Get
Return _ControlState
End Get
Set(ByVal value As ControlStates)
_ControlState = value
Me.BackColor = GetBackgroundColor()
End Set
End Property
Private Function GetBackgroundColor() As Color
Dim RetVal As Color
If Not Me.Enabled Then
RetVal = System.Drawing.SystemColors.Control
Else
Select Case True
Case Me.ControlState = ControlStates.Warning
RetVal = Color.PaleGoldenrod
Case Me.ControlState = ControlStates.Error
RetVal = Color.LightCoral
Case Me.IsRequired
RetVal = Color.OldLace
Case Me.ReadOnly
RetVal = Color.FromArgb(245, 245, 245)
Case Else
RetVal = _BackColor
End Select
End If
Return RetVal
End Function
In this scenario, I was trying to catch the BackColor the first time it was changed (Static) and then set MyBase.BackColor. This would never change the BackColor of the control. I am guessing that since I am changing the color of MyBase and not Me, that is creating my situation.
Code:
Private _BackColor As Color
Overrides Property BackColor As Color
Get
Return _BackColor
End Get
Set(value As Color)
Static bDone As Boolean
If Not bDone Then
_BackColor = value
bDone = True
End If
MyBase.BackColor = value
End Set
End Property
I tried to override the OnBackColorChanged event to catch it first being set. This worked if you set it to something other than default at design time. If you left it at default, the static variable didn't get set until the first time the BackColor was changed in the app. That would mean that it has the wrong color to set it back to when set back to Normal.
Code:
Protected Overrides Sub OnBackColorChanged(e As EventArgs)
MyBase.OnBackColorChanged(e)
Static bDone As Boolean
If Not bDone Then
_BackColor = Me.BackColor
bDone = True
End If
End Sub
The easiest way I can think of to handle this is when you change the backcolour of your control use MyBase.BackColor as the property to change.
Then shadow the current BackColor property and provide your own internal variable, that way when it's set back to "normal" you can use your own variable value.
From the form perspective it will look like it's the regular BackColor property still, even though it's not.
Currently using VS 2015 Enterprise on Win10 Enterprise x64.
It sounds like what you are proposing is to Override the BackColor property and set MyBase.BackColor = Value. That is what I attempted to do in the second code box above. It never changed the BackColor of the control. I put in a break point and that line is getting processed and the value is getting set. However, the backcolor is still white.
It wasn't clear if the state could go from warning to error, then back to warning, and then normal. This should give you some ideas. It should also take care of the normal color being the color the user chose at design time.
Code:
Public Enum ControlStates
Normal
Warning
Err
PreviousState
End Enum
Public Class myTB : Inherits TextBox
Private _BackColor As New List(Of Color)
Private _ControlState As ControlStates
Public Sub New()
MyBase.New()
Me._BackColor.Add(MyBase.BackColor) 'this is the normal color
End Sub
<System.ComponentModel.Description("Sets the control state to Warning or Error. Changes the border color based upon the selection."), System.ComponentModel.Category("Validation")> _
Property ControlState() As ControlStates
Get
Return Me._ControlState
End Get
Set(ByVal value As ControlStates)
Me._ControlState = value
Me.BackColor = GetBackgroundColor()
End Set
End Property
Private Function GetBackgroundColor() As Color
Select Case True
Case Me.ControlState = ControlStates.Normal
Me.normalColor()
Case Me.ControlState = ControlStates.Warning
Me._BackColor.Add(Color.PaleGoldenrod)
Case Me.ControlState = ControlStates.Err
Me._BackColor.Add(Color.LightCoral)
Case Me.ControlState = ControlStates.PreviousState
If Me._BackColor.Count > 1 Then
Me._BackColor.RemoveAt(Me._BackColor.Count - 1)
End If
Case Else 'back to normal
Me.normalColor()
End Select
Return Me._BackColor(Me._BackColor.Count - 1)
End Function
Private Sub normalColor()
Me._BackColor = (From bc In Me._BackColor Take 1).ToList
End Sub
End Class
It sounds like what you are proposing is to Override the BackColor property and set MyBase.BackColor = Value. That is what I attempted to do in the second code box above. It never changed the BackColor of the control. I put in a break point and that line is getting processed and the value is getting set. However, the backcolor is still white.
After some playing with it, I've realized that we can't Override the BackColor of your Inherited TextBox, it causes the whole thing to not work, so instead I Shadowed the current BackColor to make it readonly and added a 2nd BackColor (BackColor2) property to allow you to know/change what the "Normal" backcolor is. Here's the class:
vb.net Code:
Imports System.ComponentModel
Public Class BackgorundTextBox
Inherits TextBox
Private _backColor As Color
Private _controlState As ControlStates
Private _isRequired As Boolean
Public Sub New
MyBase.New()
_backColor = MyBase.BackColor
_controlState = ControlStates.Normal
_isRequired = False
End Sub
Public Shadows ReadOnly Property BackColor As Color
Get
Return MyBase.BackColor
End Get
End Property
Public Property BackColor2 As Color
Get
Return _backColor
End Get
Set(value As Color)
If Not _backColor.Equals(Value)
_backColor = Value
MyBase.BackColor = GetBackgroundColor()
End If
End Set
End Property
Public Property IsRequired As Boolean
Get
Return _isRequired
End Get
Set(value As Boolean)
If _isRequired <> value Then
_isRequired = value
MyBase.BackColor = GetBackgroundColor()
End If
End Set
End Property
<Description("Sets the control state to Normal, Warning, or Error. Changes the back color based upon the selection."),
Category("Validation")>
Property ControlState() As ControlStates
Get
Return _ControlState
End Get
Set(value As ControlStates)
If _ControlState <> Value Then
_ControlState = value
MyBase.BackColor = GetBackgroundColor()
End If
End Set
End Property
Private Function GetBackgroundColor() As Color
Dim retVal As Color
If Not Me.Enabled Then
retVal = SystemColors.Control
Else
Select Case True
Case _controlState = ControlStates.Warning
retVal = Color.PaleGoldenrod
Case _controlState = ControlStates.Error
retVal = Color.LightCoral
Case _isRequired
retVal = Color.OldLace
Case Me.ReadOnly
retVal = Color.WhiteSmoke
Case Else
retVal = _backColor
End Select
End If
Return retVal
End Function
End Class
Public Enum ControlStates
Normal = 0
Warning = 1
[Error] = 2
End Enum
I've also uploaded the example project I made to play with this.
Currently using VS 2015 Enterprise on Win10 Enterprise x64.
Public Sub New()
MyBase.New()
Me._BackColor.Add(MyBase.BackColor) 'this is the normal color
End Sub
I was hoping that it would be this easy as I had always tried to grab Me.BackColor in the New() method. However, MyBase.BackColor returns White even when I set it to Green at design time.
OK. I created a small app to play with this. I added a form with my TextBox and set the BackColor = Green. I set a break point on the first line of code under New() and ran the app. When it stopped at the break point, I looked at the form that had my TextBox on it and the BackColor was White. I realized then that some other code must be running prior to New(). It turned out to be the code from my properties. My properties call GetBackgroundColor to set the BackColor when a user sets ControlState. At runtime, this code was firing before the New() ran and the Case Else was setting RetVal = _BackColor which has a default value of White.
I thought New() was supposed to run before any properties were set. I did some digging on the event firing order of a control, but didn't find anything about this.
To get around this, I added the following code to my GetBackgroundColor() routine.
Code:
Static bDone As Boolean
If Not bDone Then
_BackColor = MyBase.BackColor
bDone = True
End If
Now I have everything resolved!
Thanks everyone for the assistance. I initially thought I must be missing something so simple. Turns out to have been a simple fix, it was just a little more involved.