3
votes

My application has the following UI configuration:

The main form is an MDI container. Its child forms are attached to a TabStrip.

Each user has his set of child forms. Depending on the active user, only that user's child forms are displayed, together with tabs.

This is achieved by going through the main form's MdiChildren and setting their Visible property to false/true depending on the active user.

        foreach (Form item in MdiChildren)
        {
            if (((OfficeFormEx)item).UserID == (int)e.NewTab.Tag)
            {
                item.Visible = true;
            }
            else
            {
                item.Visible = false;
            }
        }

This has two undesired effects. One is that every child form gets redrawn in succession, which is ugly and slow. The other is that for some reason the forms go from maximized to normal, effectively undocking them from the main form.

Is there any way to display just one of the child forms, such as the one the user was previously looking at, and get the others to stay in the background? The maximize/normal thing is not that big a deal because I can maximize them again manually.

3
+1 Your question conducted to learn something that @Hans Passant has clearly explained. Thanks! =)Will Marcouiller

3 Answers

2
votes

Your question isn't very clear without a code snippet. You are however doing battle with the Windows MDI implementation. One thing it doesn't support is hiding a child window, it can only be minimized at best. Windows Forms implements the Visible property by destroying the Window handle, recreating it when the Visible property is set to True again. That new instance of the window won't be maximized.

It also doesn't support switching the focus to a child window when the current one is maximized. WF's workaround for that is to force the active child window back to the Normal state.

The MDI model is simply not very suitable for displaying child windows in the maximized state. To get a tabbed interface, use a TabControl and display UserControls on its tab pages.

2
votes

At first sight, I would take a look at the Form.WindowsState property, if you haven't already. I doubt that if you happen to set this property to FormWindowState.Maximized on design, this would be changed when setting their Visible property true/false.

For the "[...] every child form gets redrawn in succession [...]"-thing, have you tried to use the SuspendLayout() method on the beginning of the active user's forms verification, and calling ResumeLayout() afterwards?

EDIT #1

  • I would advise you to load only the required Form for the current user.

Doing so will reduce the amount of memory used by your application, plus, it shall reduce considerably the number of forms contained within the MdiChildren collection property. Then, iterating through the collection, if still required, will be faster.

If this isn't an option for you, then perhaps using Linq might help:

var visibleForms = from f in MdiChildren
                   where (((OfficeFormEx)f).UserID == (int)e.NewTab.Tag)
                   select f;

var invisibleForms = from f in MdiChildren
                     where (((OfficeFormEx)f).UserID != (int)e.NewTab.Tag)
                     select f

visibleForms.ToList().ForEach(f => f.Visible = true);
invisibleForms.ToList().ForEach(f => f.Visible = false);

If you're using .NET 4.0, perhaps would this be a good candidate for PLINQ

Please provide feedback so that we can come to a solution. =)

1
votes

I eventually solved this one so here's a belated write-up.

Will Marcouiller suggested SuspendLayout() and ResumeLayout(), which did not work. This led me to investigate what these two methods actually do and reach the conclusion that what I needed was a way to stop the main form from redrawing while operations on the MDI children were in progress.

This in turn resulted in the following two static utility methods which suspend redrawing for a given control. In my case, suspending redrawing of the main form resulted in a massive speed up.

/// <summary>
/// suspends drawing on a control and its children
/// </summary>
/// <param name="parent"></param>
public static void SuspendDrawing(Control control)
{
    SendMessage(control.Handle, WM_SETREDRAW, false, 0);
}

/// <summary>
/// resumes drawing on a control and its children
/// </summary>
/// <param name="parent"></param>
public static void ResumeDrawing(Control control)
{
    SendMessage(control.Handle, WM_SETREDRAW, true, 0);
    control.Refresh();
}