I saw a couple of unrelated questions on vbCity recently - one was about PropertyInfo and the other was in regard to Colors - and seeing them reminded me about how easy it is to use PropertyInfo to get access to and list all the 141 named Colors. There are of course other ways of doing this, but I just thought it would be a fun approach to use.
The following example is based on a Windows Form that contains a Button and a ListBox for demo purposes.
First create an array to hold the Colors once we've dug them out of the Colors structure:
Dim Colorprops() As Reflection.PropertyInfo
Next, in the button click event:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
' To get hold of the Members of the Color Structure
' the GetProperties method returns an array of properties in the Color structure
' and we store them in the ColorProps array
ColorProps = GetType(System.Drawing.Color).GetProperties
' For demo purposes, display the contents of the array in a listbox
' We use the Name property to return the string assigned to each property in turn
For x As Integer = 0 To ColorProps.Length - 1
ListBox1.Items.Add((ColorProps(x).Name))
Next
End Sub
As you can see from the code comments, thanks to the magic of Reflection it's very easy to get an array that contains all the Colors, which are stored as properties in the Color structure. This is achieved in the first code line. Then just for demo purposes I've displayed all the names of the Colors in a listbox.
Of course, if you've gone to the trouble of creating this list you have probably done so because you actually want to do something with the colors. For example, you might want to let the user change the BackColor of the current form.
The potential problem here is that the listbox now contains a list of strings. I know we tend to look at a list like that and can visualise them as colors, but the compiler isn't quite that clever and needs a little human help. So what we can do is to instruct it to take the selected string from the listbox (which represents the desired color) and turn that back into an actual Color object - and this can then be assigned as the Form's BackColor.
You might consider using this code to do the job :
Private Sub ListBox1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles ListBox1.SelectedIndexChanged
BackColor = Color.FromName(ListBox1.SelectedItem.ToString)
End Sub
and it will work ..... up to a point. However, the first item in the list is the Transparent property, which is a valid Color in many situations but sadly not one that a Windows Form will accept as its BackColor.
At the other end of the list you will see other Color properties, such as R, G, B, A, IsKnownColor and so on. Again these more obviously are not going to be valid BackColor properties for the Form.
As always, alternative solutions are available. Perhaps the easiest one would simply be to wrap the code in an Exception Handler which will then gracefully and silently ignore any non-valid choice:
Private Sub ListBox1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles ListBox1.SelectedIndexChanged
Try
BackColor = Color.FromName(ListBox1.SelectedItem.ToString)
Catch ex As Exception
End Try
End Sub
Is this a good fix? Probably depends on the scenario you're using it in. If you're planning on exposing this Form to a non-tech savvy user then I'd say it's a pretty poor workaround. They might not understand or be happy that the choice they're making is unacceptable and nothing is happening. Of course you could slip a message box into the Catch block, but personally I think it would be better not to show them unusable choices at all in the first place and so avoid the problem.
So let's look at how we might do that.
I suppose you could exclude the first Color (Transparent) and the final nine ("R" to "Name" inclusive) simply by changing the first line of the loop to:
For x As Integer = 1 To 140
but I'm not crazy about that approach, even though it would work just fine.
My preference would be to try and filter in only those Colors that are system defined Public Shared Colors - that is, the named Colors themselves and effectively excluding the final nine entries in the list which are instance members. Once again Reflection makes this a very easy task.
The GetProperties method takes BindingFlags as parameters and you can use these to filter the search. In our case we will use the Public and Static BindingFlags. (You don't have to remember all the available Flags as the full enumeration of them will be displayed as soon as you type an opening bracket after "GetProperties"): -
ColorProps = GetType(System.Drawing.Color).GetProperties(BindingFlags.Public Or BindingFlags.Static)
If you run the code again you will now only offer the 141 Colors to the user via the ListBox. "Transparent" is of course a valid Color so it's right that it appears in the list of Colors. But because we have refined the task to assign the Colors to the Form's BackColor, we still have to resolve this.
Again, a quick and acceptable fix would be to use a Try Catch Exception Handler.
But what if our scenario really was one where we want to allow the user to change the Form's BackColor to Transparent? Well we quite easily do this by intercepting the user's choice and if it turns out to be a choice of "Transparent" then we can manually make the whole form transparent by changing its Opacity property.
Now, I know this isn't a 100% fix for making the BackColor transparent because the transparency effect is applied to the whole form, not just the client area. Unfortunately the ClientRectangle doesn't have its own Opacity property, so I think that this code will be the nearest we can get:
Dim col As New Color
col = Color.FromName(ListBox1.SelectedItem.ToString)
If col.Name = "Transparent" Then
Me.Opacity = 0.15
Else
BackColor = col
End If
The allowed values for Opacity range from 0 (invisible) to 1 (fully visible). I purposely didn't set the value to 0, because of course this would mean that the user would lose the total form from view and wouldn't even be able to access the title bar to, for example, close the form.
In fact, I'd be inclined to make the whole deal a bit more user friendly by showing the user a one-time message about how to remove the Transparent setting. So the ListBox selection code would now be:
Dim msgshown As Boolean
Private Sub ListBox1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles ListBox1.SelectedIndexChanged
Dim col As New Color
col = Color.FromName(ListBox1.SelectedItem.ToString)
If col.Name = "Transparent" Then
Me.Opacity = 0.15
If Me.msgshown = False Then
MessageBox.Show("Double Click the form to undo the Transparent setting", "Info", MessageBoxButtons.OK)
msgshown = True
End If
Else
BackColor = col
End If
End Sub
and in order to implement that reinstatement of full Opacity, you would just need :-
Private Sub Form1_DoubleClick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.DoubleClick
Me.Opacity = 1
End Sub
So that's it. Dealing with colors (as color objects and not as names that we visualise in our heads as actual colors) can be a bit tricky until you get used to how it works. I hope that this relatively simple walkthrough has helped clarify things for you.