tags:

views:

91

answers:

2

I was wondering why it isn't possible to set some properties in the constructor of a winform. For example, I have an Expandable splitter (devcomponents library) and when i set the property Expanded to false, in the constructor nothing happens, but when I put the same code in the form load event, it works like expected. I am putting this code after the InitializeComponent() method.

Another situation, slighty different though, is this. When you dynamically create a combobox in a class and you set the selected index other then the first item and then return this combobox, the selected index is set back to 1 on the form.

Why is this?

Code snippet on the first situation (updated):

// This works in the form_load event but not in the form constructor (after InitializeComponent())
if (_tabId != -1)
{
    this.SuspendLayout();
    expandableSplitter1.SuspendLayout();

    expandableSplitter1.Expanded = false;
    expandableSplitter1.Enabled = false;

    // Hide all tabs, except the selected tab
    tabControl1.Tabs.Clear();

    QuestionTab tab = new QuestionTab(_tabId);

    TabItem tabItem = tabControl1.CreateTab(tab.Description);
    tabItem.Tag = tab;

    tabControl1.SelectedTabIndex = 0;

    TabItem_Click(tabItem, null);

    expandableSplitter1.ResumeLayout(true);
    this.ResumeLayout(true);
}

Code snippet for the second situation:

public Control GenerateList(Question question)
{
    // some code is omitted

    ComboBox cmb = new ComboBox();

    cmb.SuspendLayout();

    cmb.Name = "cmb";
    cmb.DisplayMember = "Answer";
    cmb.ValueMember = "Id";
    cmb.DataSource = answers;
    cmb.Dock = DockStyle.Top;

    cmb.SelectedValue = 3; // not the first index


    cmb.DropDownStyle = ComboBoxStyle.DropDownList;
    cmb.SelectedIndexChanged += new EventHandler(cmb_SelectedIndexChanged);

    cmb.ResumeLayout(true);

    return cmb;
}
A: 

I'm guessing the one of the events of the ExpandableSplitter defaults it to true. DevComponents stuff is funny. Are you setting it before or after InitializeComponent() is called?

No idea on the second one.

DaveShaw
I've updated my question. I've set the code after InitializeComponent();
Martijn
+1  A: 

The problem is, that these properties have an side effect. This will lead to an invalidation of the control, which tries to redraw itself but this doesn't work cause actually we're in the constructor and there is nothing we can paint on. Thus leading to a switch back to the previous or a default value (depending on the implementation of the concrete control).

To get this to work you should take a closer look into the InitializeComponent() function. Before it starts to set anything within the controls it calls a Control.SuspendLayout() and afterwards a Control.ResumeLayout(false).

You should do the same, but not only on the control itself, also on all parent controls (maybe a TableLayoutPanel or any other container control) till you reach the top by calling this.SuspendLayout().

Also you should take care of the order. Suspend from top control (this) down to your exact one you like and resume in the reverse order. In case of the standard windows controls this should work. If you use any 3rd party control than it's up to the vendor to take care of that.

Update

After reading your updates and comments i made a new project put a TabControl with some pages on it. Also i put a ComboBox on the third page and added some items to it.

Then i changed my code as follows:

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

        tabControl1.SelectedIndex = 1;
        comboBox1.SelectedIndex = 2;
    }
}

and it works as expected (without using SuspendLayout() or ResumeLayout()).

So to really find the bug in your application, start a new empty one and start filling it up with your existing code or just use the OnLoad() approach.

Oliver
Well-written control classes shouldn't do this; they should arrange so that assigning to a property doesn't have visible side effects. All the built-in WinForms controls should let you do this. However, I've seen plenty of third-party controls that are broken in this way, so it wouldn't surprise me...
Tim Robinson
@Oliver: thanks for the explanation. So I first have to set every control from 'this' to top to `Control.SuspendLayout` then set the selected index of the combo and then I have to set `Control.ResumeLayout(true)` from top to 'this'?
Martijn
@Martijn: I think this should do the job (but didn't test it)
Oliver
I've tried it, but I don't get it to work. I've updated my startpost with the code.
Martijn
And how about the combo items? At the point I create the combobox I don't know it's parent control. I've use the two methods on the combobox, but the selected item ain't set.
Martijn
In my case I have a class (ControlGenerater) with a method (GenerateList) which creates and returns a combobox. In a second class (Manager) I call that method and my form calls the method from the Manager class and then place it on the form. I hope I make myself clear.
Martijn