trying to control an array of button colors

cncmachineguy

Member
Joined
Aug 3, 2013
Messages
8
Programming Experience
10+
I have done some searching for this both here and google. As you can imagine using array and control or button gets LOTS of hits. So if I am re-hashing an already answered question please be kind.

I am trying to create a GUI as a front end to an I/O card I have built. It enumerates over USB as a HID device. All works fine there. What I have so far is 2 groups of 14 buttons that represent pins on my board. My firmware sends back the state of all pins and VB parses to decide on what color a button should be. I like using the buttons as they work well as dual purpose in my app. I can set a pin or read it using just the button.

Here is where I need help-

I have an array called PinColor() as Color.
Once I have all the colors set, I then have 28 lines of code:
VB.NET:
button1.backcolor = pincolor(0)

now it occurs to me there must be a better way to assign the color value to each button. I can live with it for 28 pins, but I have 85 total and just can't imagine there is not a better way.

this is what I would like to do but assume it won't work since I can't name the buttons with an index (at least I don't know how):
VB.NET:
dim i as integer
for i = 0 to 27
     button(i).BackColor = PinColor(i)
next
 
You can put the Buttons in an array like this:
Dim buttons = Me.Controls.OfType(Of Button)().ToArray()
The Buttons will appear in the array in the z-order they appear on the form. You can then access each Button by index from zero.
 
Thank you for the quick response. I tried this, but get the error:

Error 1 'oftype' is not a member of 'System.Windows.Forms.Control.ControlCollection'.

maybe I should have said I am using VB 2010 express. Or maybe that is assumed based on where I post.
 
Your profile says .NET 4.0 so that's generally enough.

That error is valid in the situation and that's my fault for typing code straight into a post instead of testing in an IDE first. It should have been:
Dim buttons = Me.Controls.Cast(Of Control)().OfType(Of Button)().ToArray()
This might not mean anything to you now but that's because the OfType(Of T) method extends the IEnumerable(Of T) interface while the ControlCollection exposed by the Controls property only implements the IEnumerable interface. The Cast(Of T) method extends the IEnumerable interface and returns an IEnumerable(Of T) reference, which you can then call call OfType on.
 
Random background colors

Thought you might like to see how each button in your array can show a random backcolor. Here are four examples. The first two start with a random number and increment sequentially. The next two use random colors for each button. Two of each display the color number, and two display the color name. Enjoy.

VB.NET:
    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        Dim buttons = Me.Controls.Cast(Of Control)().OfType(Of Button)().ToArray()
        Dim nextcolor, butnum, max As Integer
        Dim randnum As New Random()
        butnum = buttons.Length
        max = 167 - butnum    'number of known colors - number of buttons
        nextcolor = randnum.Next(28, max)  'starting color selected randomly
        For x As Integer = 0 To butnum - 1
            nextcolor += 1
            buttons(x).BackColor = Color.FromKnownColor(CType(nextcolor, KnownColor))
            buttons(x).Text = "Color " & nextcolor.ToString()   'display color number
        Next
    End Sub

    Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
        Dim buttons = Me.Controls.Cast(Of Control)().OfType(Of Button)().ToArray()
        Dim nextcolor, butnum, max As Integer
        Dim randnum As New Random()
        Dim colorname As String
        butnum = buttons.Length
        max = 167 - butnum    'number of known colors - number of buttons
        nextcolor = randnum.Next(28, max)  'starting color selected randomly
        For x As Integer = 0 To butnum - 1
            nextcolor += 1
            buttons(x).BackColor = Color.FromKnownColor(CType(nextcolor, KnownColor))
            colorname = [Enum].GetName(GetType(KnownColor), nextcolor)
            buttons(x).Text = colorname     'display color name
        Next
    End Sub

    Private Sub Button3_Click(sender As System.Object, e As System.EventArgs) Handles Button3.Click
        Dim buttons = Me.Controls.Cast(Of Control)().OfType(Of Button)().ToArray()
        Dim nextcolor, butnum, max As Integer
        Dim randnum As New Random()
        butnum = buttons.Length
        max = 167 - butnum    'number of known colors - number of buttons
        For x As Integer = 0 To butnum - 1
            nextcolor = randnum.Next(28, max)  'each color selected randomly
            buttons(x).BackColor = Color.FromKnownColor(CType(nextcolor, KnownColor))
            buttons(x).Text = "Color " & nextcolor.ToString()   'display color number
        Next
    End Sub

    Private Sub Button4_Click(sender As System.Object, e As System.EventArgs) Handles Button4.Click
        Dim buttons = Me.Controls.Cast(Of Control)().OfType(Of Button)().ToArray()
        Dim nextcolor, butnum, max As Integer
        Dim randnum As New Random()
        Dim colorname As String
        butnum = buttons.Length
        max = 167 - butnum    'number of known colors - number of buttons
        For x As Integer = 0 To butnum - 1
            nextcolor = randnum.Next(28, max)  'each color selected randomly
            buttons(x).BackColor = Color.FromKnownColor(CType(nextcolor, KnownColor))
            colorname = [Enum].GetName(GetType(KnownColor), nextcolor)
            buttons(x).Text = colorname     'display color name
        Next
    End Sub
 
This works fine here:

        Dim buttons = Controls.OfType(Of Button)().OrderBy(Function(b) b.Name)
        For thisIndex = 0 To buttons.Count - 1
            buttons(thisIndex).BackColor = myColors(thisIndex)
        Next
 
This works fine here:

        Dim buttons = Controls.OfType(Of Button)().OrderBy(Function(b) b.Name)
        For thisIndex = 0 To buttons.Count - 1
            buttons(thisIndex).BackColor = myColors(thisIndex)
        Next

Hey! It does too. I should have trusted in my original judgement. Despite what I said, OfType does actually extend IEnumerable and not IEnumerable(Of T), so it can be called directly on Controls. In that case, the issue mentioned in post #3 must have another cause. The Enumerable.OfType(Of TResult) method is declared in the System.Core.dll assembly and the System.Linq namespace. I can only conclude that the original project does not reference that assembly and/or import that namespace. Fix that and the original code should work.
 
Making some progress here. Thank you all for the help, It is very nice to get helpful answers when the OP (me) really has no idea how to do what he asks. Then my troubleshooting skills are still lower than I want - But I will get better trust me.

I am not sure what/how to fix my project. I did not create it from scratch, it started out as demo code from Microchip. So with the knowledge that you guys know way more then I do, I started a new project, added a couple buttons to a form, and copied/pasted the random button code from post 5 to button 1. Works great! So then I added a button to my original project and get almost the same error, but this time "Cast" is not a member. I started a new project and copied everything to it. Now it works!

As for the index returned for each button, how can I predict it so I can address the correct button? Right now 14 buttons are in a group box and 14 more are in a second group box. Of course these were not collected and indexed. So I moved 14 of them from the group box. I changed the button1 code from post 5 to put the index number (x) on the button instead of the color. This seems to work fine, but I do not see the rhyme or reason for how they are who they are.

I know there is a way to deal with them in the group boxes, but for now I am happy to move them out and only learn how the array's are working first.
 
Last edited:
You apparently don't have access to LINQ methods for some reason. What version of the .NET Framework are you targeting? Assuming that it's 3.5 or later, have you done what I said in post #7?
 
I attempted what you suggested but did not figure out how. Instead I started a new project and copied/pasted all code in it and now it works. My newbie shining bright here. At this point I am happy to learn and understand what has been shown to me. Later (much later) I may try to figure out how to get things to work with imported projects from older (2008) targeted code.

Now I am trying to understand how the buttons get indexed as they seem to have no order to them, ie placement on the form / order created / button name. once that is understood by me I think I can make this work how I would like it to.

If you can shed some light on that for me, it will be most helpful.
 
As I said, it follows the z-order. Controls with a lower z-index are behind controls with a higher z-index. If your controls don't overlap then you won't see it but the z-order is there. By default, each control you add to the form will have a higher z-index than those already on the form. You can make coarse-grained changes to the z-order by right-clicking a control and and selecting 'Bring to Front' or 'Send to Back'. To make fine-grained changes, open the Document Outline window and then you can drag controls up and down the z-order and into and out of containers.
 
THAT IS PERFECT!!!!!!!!!!!!!!! Sorry to yell but I am a little excited about this!!

Thank you all both for helping me with this and for making my first post a good experience.

While I do not understand everything shown to me yet, consider this question answered. I will work to understand it all better.
 
You can actually change the z-order of controls on your form as follows:

From the menu bar: View >> Other Windows >> Document Outline

Select control to move and click up or down arrows to rearrange the order.
The code will select them in order from bottom up (last to first).
Put them in reverse order to get code to access them from first to last.
 
Or, you could make the index part of the button name. For example if Button0 has to have the color in the the array at position 0, and Button14 the color in the array at position 14, just extract the index from the name.

        Dim buttons = Controls.OfType(Of Button)()
        For Each thisButton In buttons
            thisButton.BackColor = myColors(CInt(thisButton.Name.ToLower.Replace("button", "")))
        Next
 
well that is really good to know. Ultimately I would like to have sets of buttons, labeled RA0-RA15, RB0-RB15, and so forth. If I can get their names as Herman suggests, and keep them in seperate arrays, that would be really nice. If not I can deal with 1 array as long as I can keep straight who is where. And Thank you solitaire for further explanation on z-reordering.
 
Back
Top