Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C#.NET MDI bugs when programmatically hiding and showing again a maximized child form and when maximized, child form's icon cannot be changed

Tags:

c#

.net

mdi

Basically I am having two problems with C#.NET MDI. You can download VS2010 solution which reproduces bugs here.

1) When programmatically hiding and showing again a maximized child form, it is not maximized properly again and becomes neither maximized or in normal state.

childForm = new Form();
childForm.Text = "Child Form";
childForm.MdiParent = this;

...

private void showButton_Click(object sender, EventArgs e)
{
    childForm.Visible = true;
}

...

private void hideButton_Click(object sender, EventArgs e)
{
    childForm.Visible = false;
}

When child form is maximized, then programicaly hidden and shown again, it becomes something like this (please notice the menu bar - child form's control box appears, but child form is not maximized):

alt text

At this stage, child form cannot be moved around. However, I found a workaround for that, simply by showing and hiding a dummy child form, which forces the actual child form to become properly maximized. But this makes MDI area to flicker. Tried Invalidate, Refresh, Update methods, but they don't help. Maybe there are other workarounds to overcome this bug and not to make MDI area flicker with dummy child form?

private void workaround1Button_Click(object sender, EventArgs e)
{
    dummyForm.Visible = true;
    dummyForm.Visible = false;
}

2) When child form is maximized, the icon of the child form is displayed on menu bar. However, if you have to change the icon while the child form is maximized, the icon on the menu bar is not being refreshed (see the image above). I found a workaround for that too, which basically hides and shows menu bar. Icon gets refreshed, but it makes everything below menu bar to flicker. Tried Invalidate, Refresh, Update methods, but they don't help. Is there any other way to make menu bar to refresh the child form's icon?

private void workaround2Button_Click(object sender, EventArgs e)
{
    menuStrip.Visible = false;
    menuStrip.Visible = true;
}

Also I noticed that when parent form is in normal window state mode (not maximized) and you change the width or height of the form by 1 pixel, child form becomes maximized as it should be and child form's icon on menu bar gets refreshed properly and you don't need other workaround I described above. If I change the size of the form programicaly, form flickers by 1 pixel and I cannot do that, when parent form is maximized. Is there any way how I could invoke the repaint/refresh functionality which is called when you resize a form and which makes child form become maximized properly and the icon on the menu bar refreshed?

like image 315
Povilas Avatar asked Dec 13 '10 12:12

Povilas


4 Answers

There's a bug in the implementation of the internal MdiControlStrip class, the control that displays the icon and the min/max/restore glyphs in the parent window. I haven't characterized it as yet, the code isn't that easy. A classic side effect of the bug is that the glyphs get doubled up, you found some other side-effects. The fix is simple though, delay creating the child windows until after the constructor is completed. Like this:

    public MainForm()
    {
        InitializeComponent();
    }
    protected override void OnLoad(EventArgs e) {
        childForm = new Form();
        childForm.Text = "Child Form";
        childForm.MdiParent = this;

        dummyForm = new Form();
        dummyForm.MdiParent = this;
        dummyForm.WindowState = FormWindowState.Maximized;
        base.OnLoad(e);
    }
like image 70
Hans Passant Avatar answered Oct 09 '22 15:10

Hans Passant


Have you tired using Hide/Show instead of setting visible to true/false?

Try:

private void showButton_Click(object sender, EventArgs e)
{
    childForm.Show();
}

private void hideButton_Click(object sender, EventArgs e)
{
    childForm.Hide();
}
like image 33
Tony Abrams Avatar answered Oct 09 '22 15:10

Tony Abrams


How about this workaround?

private void showButton_Click(object sender, EventArgs e)
{
    childForm.Visible = true;
    childForm.WindowState = (FormWindowState)childForm.Tag;
}

private void hideButton_Click(object sender, EventArgs e)
{
    childForm.Visible = false;
    childForm.Tag = childForm.WindowState;
    childForm.WindowState = FormWindowState.Normal;
}

UPDATE

I just gave you the idea how you could do. A better solution using the same idea as above would be a new base form which saves the windows state. See below. Derive your forms from FixedForm instead of Form:

public partial class FixedForm : Form
{
    private FormWindowState lastWindowState;

    public FixedForm()
    {
        InitializeComponent();
    }

    protected override void OnVisibleChanged(EventArgs e)
    {
        base.OnVisibleChanged(e);

        if (Visible)
        {
            WindowState = lastWindowState;
        }
        else
        {
            lastWindowState = WindowState;
            WindowState = FormWindowState.Normal;
        }
    }
}
like image 30
Scoregraphic Avatar answered Oct 09 '22 16:10

Scoregraphic


Found a way how to come around those bugs.

First of all you need to suspend painting for a form and its children. I found a very helpful thread here, which describes how to do it.

After suspending painting, you need call UpdateBounds method of the control and increase ClientRectangle Width or Height by one and then decrease it back to the same value it was before. This invokes layout functionality which makes everything to update/repaint. Last step is to enable painting. Not very nice solution I guess, but it works.

StopDrawing();
UpdateBounds(Location.X, Location.Y, Width, Height, ClientRectangle.Width, ClientRectangle.Height + 1);
UpdateBounds(Location.X, Location.Y, Width, Height, ClientRectangle.Width, ClientRectangle.Height - 1);
StartDrawing();

I find suspending painting very helpful not only for working around those two bugs, but also in general to make GUI work more smoothly. I guess this can help to remove any kind of flickering. However, this solution requires P/Invokes, which should be avoided in general.

like image 38
Povilas Avatar answered Oct 09 '22 16:10

Povilas