Search code examples
c#winformsmdichild

Why does hiding and showing a mdi child move the child?


I have a very basic out the box mdiparent which has a number of mdichildren and a menu item. Each button on the menu item hides all the forms and then shows the one respective to that button.

When I do it this way:

        //dontHide is the Form we want to show.
        for(int i = 0; i < this.MdiChildren.Length; i++)
        {
            if (this.MdiChildren[i] != dontHide)
            {
                this.MdiChildren[i].Visible = false;
            }
        }
        dontHide.Visible = true;

Switching forms causes the new form opened to be positioned bit lower and to the right of the old form, but clicking the menu item for the currently displayed form does nothing (as expected).

But, when I do this:

        //dontHide is the Form we want to show.
        for(int i = 0; i < this.MdiChildren.Length; i++)
        {
            this.MdiChildren[i].Visible = false;
        } 
        dontHide.Visible = true;

Even clicking the menu item for the currently visible form causes it to shift to the lower right, same as opening a new form. Why is that?

Edit:

I've also noticed when centering the form and then displaying it (so you don't risk having someone glimpse it right before it is moved), setting visible to true completely resets any centering I've done.


Solution

  • This is caused by an obscure implementation detail in Winforms. The native MDI support built into Windows does not support hiding child windows. Winforms works around this restriction by destroying the child window when you set its Visible property to false. And re-creating it when you set it back to true.

    This can have various side-effects, the state of the native window is lost when this happens of course. Winforms has fairly decent support for restoring the window again from its properties. But one thing it doesn't do is recreating the window in the same location. So you'll see it getting recreated in the location that new MDI child windows get, staggered from the previous window. Whether that was an oversight or intentional isn't that clear to me, 95% odds for the latter.

    Otherwise simple to work around, you can assign the Location property yourself to get it back where it was:

      var loc = dontHide.Location;        
      dontHide.Visible = true;
      dontHide.Location = loc;
    

    Or just set the MDI child form's StartPosition to Manual.