views:

212

answers:

2

Okay, I was able to create a simple WinForms project that reproduces some strange behavior I found. In the designer, make a Form with a ListBox (named lbx) Anchored Top,Left,Right,Bottom, and a Button (button1). Now, the Form's code is here:

using System;
using System.Windows.Forms;

namespace ListBoxKaboom
{
    public partial class Form1 : Form
    {
        private bool _initFinished = false;

        public Form1()
        {
            InitializeComponent();

            this._initFinished = true;

            this.Height += 100;
            this.Height -= 50;
            this.Height += 50;
        }

        private void lbx_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.button1.Enabled = (this.lbx.SelectedItem != null);
        }

        protected override void OnLayout(LayoutEventArgs e)
        {
            if (_initFinished)
            {
                int lines = (this.lbx.Height - 4) / this.lbx.ItemHeight;

                this.SuspendLayout();
                while (lines < this.lbx.Items.Count)
                {
                    this.lbx.Items.RemoveAt(this.lbx.Items.Count - 1);
                }

                while (lines > this.lbx.Items.Count)
                {
                    this.lbx.Items.Add("Item " + (this.lbx.Items.Count + 1).ToString());
                }
                this.ResumeLayout();
            }

            base.OnLayout(e);
        }
    }
}

PLEASE NOTE THE FOLLOWING INSTRUCTIONS: Run this, click any of the items in the list box, and use the arrow keys to move down far enough to cause the list box to scroll. Kaboom. Exception (sometimes NullReferenceException, sometimes IndexOutOfBoundsException). Any ideas why? Also, I would think that the items would be in order, but they're not. Is this just a goofy corner case that didn't get handled properly by WinForms or am I doing something wrong?

Stack Trace:

at System.Windows.Forms.ListBox.NativeUpdateSelection()

at System.Windows.Forms.ListBox.SelectedObjectCollection.EnsureUpToDate()

at System.Windows.Forms.ListBox.SelectedObjectCollection.get_InnerArray()

at System.Windows.Forms.ListBox.SelectedObjectCollection.get_Item(Int32 index)

at System.Windows.Forms.ListBox.get_SelectedItem()

+1  A: 
Henk Holterman
Well, considering the Layout that's being handled isn't the ListBox's, but the Form's, I don't understand how that's even possible (the stack overflow). Modifying the contents of the ListBox shouldn't affect the parent Form's layout anyway. See comments on initial post.
FMM
FMM: Modifying the layout of a form child can cause the parent form's layout to change. I can see how a stack overflow could occur.
Jeff Yates
Granted, but modifying the contents of a list box doesn't affect its bounds.
FMM
FMM: That doesn't necessarily matter. You can't rely on it NOT to tell its parent that its layout has changed - that's an implementation detail and it could well do so even if its bounds haven't changed.
Jeff Yates
Jeff: still irrelevant - I'm not getting a stack overflow.
FMM
Using the Resize event appears to work in my sample app; let me plug that into my larger app and see how it does. Stand by...
FMM
OnResize seems to be the cure; I'm still not sure why. If I understand correctly, SuspendLayout() only suspends layout for the control it's called on, not for its child controls. For tall hierarchies of Controls, this is an important distinction. At any rate, the fix works and I've marked yours as the answer. Thank you so very much for your help!
FMM
mackenir: Give me a frickin' break - it's a thrown-together proof-of-concept app. As such, I have no choice but to at least expect a stab at fixing the problem rather than criticizing the code. The fix that worked was exactly what you prescribed - do it differently. If you say what I'm doing is "unusual" then show me a method to achieve the same goal that's less "unusual". At any rate, the problem is fixed, and I find your advice less than useful. Must every person who posts on StackOverflow preface themselves with "suggestions for better methods are welcome/not welcome"?
FMM
Sorry dude.''''''''''''
mackenir
A: 

You should not manipulate any GUI elements in the constructor, e.g. this.Height += 100; in your example. Strange things can happen. I have been bitten by this several times in legacy code.

Wait until form load time - handle the base.Load event and do the height manipulation there.


From "When does Form.Load event get raised?":

Q: "... I need to find out basically what the difference is between putting code in Load event's handler, versus putting code in the Form's constructor after the InitializeComponents() line ..."

A: "The Load event is fired once the control/form has been fully initialized and has a window handle created. Therefore once this event has fired it is a fully usable user interface control. Remember that inside the constructor the actual window handle for the control/form has not yet been created, you are only creating C# objects here and inside the InitializeComponent call."

Peter Mortensen
Sorry, not buying this. That's exactly what the Forms Designer does in InitializeComponent().
FMM
Noticed the edit; I still don't buy it.
FMM