Order of controls in a form's Control property in C#

I am having a peculiar problem with the order in which FlowLayoutPanels are added in to the form's controls property. This is what I tried,

I added 7 FlowLayoutPanels in to a C# window application from left to right in vertical strips. Then I tagged the flow layouts as 1, 2, 3, ... 7 again from left to right. Now in the load handler of the form, I wrote the following snippet,

    foreach (FlowLayoutPanel aDaysControl in this.Controls)
    {
        MessageBox.Show(aDaysControl.Tag.ToString());
    }

I expected messages to appear in the order of 1, 2, ... 7. But I got it in the reverse order (7, 6, ...1). Could some one help me out with the mistake I did ??

Reason behind preserving the order,

I am trying to make a calendar control with each row representing a day. If a month starts from Wednesday, then I need to add a empty label to the first(Monday) and the second(Tuesday) row. So the order matters a bit

Upvotes: 6

Views: 21348

Answers (6)

TobyB
TobyB

Reputation: 1

I use this method to set the TabIndex to reflect the visual flow - i.e. the position of the controls.

private void ChangeTabIndexByPosition(ControlCollection controlsCollection)
{
    List<Control> sorted = controlsCollection.OfType<Control>().ToList();
    sorted.Sort((c1, c2) => c1.Top == c2.Top ? c1.Left.CompareTo(c2.Left) : c1.Top.CompareTo(c2.Top));

    for (int i = 0; i < sorted.Count; i++)
    {
        sorted[i].TabIndex = i;
    }
}

Upvotes: 0

nawfal
nawfal

Reputation: 73311

What if in future some other designer removed the controls, added back etc? Checking the designer always is a mess. What would be better is to sort the controls in the container control before you enumerate. I use this extension method (if you have Linq):

public static List<Control> ToControlsSorted(this Control panel)
{
    var controls = panel.Controls.OfType<Control>().ToList();
    controls.Sort((c1, c2) => c1.TabIndex.CompareTo(c2.TabIndex));
    return controls;
}

And you can:

foreach (FlowLayoutPanel aDaysControl in this.ToControlsSorted())
{
    MessageBox.Show(aDaysControl.TabIndex.ToString());
}

(Above is for TabIndex). Would be trivial to sort according to Tag from that.

Upvotes: 1

ispiro
ispiro

Reputation: 27743

I know this is quite an old question, but...

You might want to use SetChildIndex. e.g. this.Controls.SetChildIndex(button1, 0);

Upvotes: 11

Steve Morgan
Steve Morgan

Reputation: 13091

Is it really a problem?

As long as the UI operates correctly (in terms of tab order, for example), I'd recommend that you don't make any assumptions about the order in which they're enumerated.

EDIT: Thanks for explaining your requirement in more detail. I think I'd still recommend against using the order that they're stored in the Controls collection. It's always best to consider these implementation details to be 'opaque'. You have a tag associated with each control, so you can use this to identify the correct control. In order to speed up the processing, you could build a 7-element array that references the controls by ordinal:

FlowLayoutPanel[] panels = new FlowLayoutPanel[7];

foreach(FlowLayoutPanel panel in this.Controls)
{
    panels[(int)panel.Tag] = panel;
}

// Now, you can reference the panels directly by subscript:

panels[2].BackColor = Color.Aquamarine;

Though I'd put some type-checking in to make this code a bit more robust!

Upvotes: 0

Hath
Hath

Reputation: 12779

if you look at the code generated by the designer Form1.designer.cs it will look something like this:

        // 
        // Form1
        // 
        this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(658, 160);
        this.Controls.Add(this.flowLayoutPanel7);
        this.Controls.Add(this.flowLayoutPanel6);
        this.Controls.Add(this.flowLayoutPanel5);
        this.Controls.Add(this.flowLayoutPanel4);
        this.Controls.Add(this.flowLayoutPanel3);
        this.Controls.Add(this.flowLayoutPanel2);
        this.Controls.Add(this.flowLayoutPanel1);
        this.Name = "Form1";
        this.Text = "Form1";
        this.ResumeLayout(false);

note how it was built up you added panel 1 first then 2 etc. but as the code runs through it will add 7 first then 6.

this code will be in the InitializeComponent() function generated by the designer.

Why do you need them to run in a certain order?

I wouldn't rely on the designer to keep the order you want.. i would sort the controls my self:

        var flowpanelinOrder = from n in this.Controls.Cast<Control>()
                               where n is FlowLayoutPanel
                               orderby int.Parse(n.Tag.ToString())
                               select n;

        /* non linq
        List<Control> flowpanelinOrder = new List<Control>();
        foreach (Control c in this.Controls)
        {
            if (c is FlowLayoutPanel) flowpanelinOrder.Add(c);                
        }
        flowpanelinOrder.Sort();
         * */

        foreach (FlowLayoutPanel aDaysControl in flowpanelinOrder)
        {
            MessageBox.Show(aDaysControl.Tag.ToString());
        }

Upvotes: 1

Mladen Prajdic
Mladen Prajdic

Reputation: 15685

look at the order in which they are added to the form in the yourForm.designer.cs

Upvotes: 4

Related Questions