problems with restoring minimized MDI child forms

G

Guest

Hi Everybody,
I've got a strange problem:

While searching the newsgroups for something else,
I've come across a post that discusses a bug with restoring MDI child forms.
(for any body who is interested, the post is in
microsoft.public.dotnet.framework.windowsforms
by Scott Abel, the title is 'Form Message Question', from 2003-08-12
09:43:42 PST )

Briefly, the problem is that if you use Form.WindowStyle to restore a
minimized MDI child that was maximized when it was minimized, the form will
be restored as maximized (as it should) but then when you click on the
restore button of the from it will be restored to the minimized size.
(Yes, I know this is not clear :)
That post also included a workaround for this bug (to use SendMessage to
send a SC_RESTORE message to the form instead of using Form.WindowState).

I have implemented this workaround in my application.

However, I've run into a strange problem:

My application has several buttons on the main form (the MDI parent)
that are positioned over a toolbar strip, but are NORMAL buttons, NOT
toolbar buttons. (the guy who wrote the original program didn't like the way
the toolbar buttons looked, so he did it this way).
These buttons can open the form, change it's WindowState (if you uncomment
that
statement) , but it seems like the SendMessage() function does not work
properly if called from the click event handler of that Button.
the SendMessage() does work if it is called from the event handler of the
menu or from the click event handle of a toolbar button (NOT included in the
appended example).

this is very strange!

I've reproduced the problem in a small application (the application is
appended to the end of the post)

If anybody have any ideas why this happens or how to fix it, please let me
know.

Thanks,
Nadav

PS, I can fix this problem by changing the buttons to toolbar buttons,
but this would change the way the application looks (which I would rather
not do)
and anyway I'm really curious about this strange bug.

=============== appended code =============


using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Runtime.InteropServices;

namespace test_MDI_bug
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.MainMenu mainMenu1;
private System.Windows.Forms.MenuItem menuItem1;
private System.Windows.Forms.MenuItem menuItem2;
private System.Windows.Forms.Button button1;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;

public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

//
// TODO: Add any constructor code after InitializeComponent call
//
}

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.mainMenu1 = new System.Windows.Forms.MainMenu();
this.menuItem1 = new System.Windows.Forms.MenuItem();
this.menuItem2 = new System.Windows.Forms.MenuItem();
this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// mainMenu1
//
this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.menuItem1});
//
// menuItem1
//
this.menuItem1.Index = 0;
this.menuItem1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.menuItem2});
this.menuItem1.Text = "test";
//
// menuItem2
//
this.menuItem2.Index = 0;
this.menuItem2.Text = "test1";
this.menuItem2.Click += new System.EventHandler(this.menuItem2_Click);
//
// button1
//
this.button1.Location = new System.Drawing.Point(48, 8);
this.button1.Name = "button1";
this.button1.TabIndex = 2;
this.button1.Text = "button1";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 272);
this.Controls.Add(this.button1);
this.IsMdiContainer = true;
this.Menu = this.mainMenu1;
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}


//
// In MDI Parent form class declaration
//
[DllImport("User32")]
private static extern bool SendMessage(IntPtr hWnd, int msg, int
wParam, int lParam);

private const int SC_RESTORE = 0xF120;
private const int WM_SYSCOMMAND = 0x0112;

Form f=null;
//
// This is the menu click event to display the form.
//
//
private void Restore() {
//
// If it is already loaded, set it, otherwise set it to null
// so we can load it next.
//

if (f == null) {
f = new Form();
f.MdiParent = this;
f.Show();
}

else {
if (f.WindowState == FormWindowState.Minimized) {
SendMessage(f.Handle,WM_SYSCOMMAND, SC_RESTORE, 0);
//f.WindowState=FormWindowState.Normal;
}
f.Activate();
}

}

private void menuItem2_Click(object sender, System.EventArgs e) {
Restore();
}

private void toolBar1_ButtonClick(object sender,
System.Windows.Forms.ToolBarButtonClickEventArgs e) {
Restore();
}

private void button1_Click(object sender, System.EventArgs e) {
Restore();
}
}
}
 
J

Jeffrey Tan[MSFT]

Hi BacSoftDev,

Thanks for your post.

In Windows MDI application, its MDI child windows is managed by the MDI
client window, which is the child control of MDI parent window.(it is the
grayed background color control filling the MDI parent's client area).
Normally for MDI child window, we should manipuate them through sending MDI
app specific message to MDI client control, then MDI client control will
manipulate it well for us.

Now for your requirement, we should send WM_MDIRESTORE message to the MDI
client control. Also, we can loop through MDI parent form's Controls
collection to find the MDI client window's reference. The modified
Restore() is listed below:

[DllImport("User32")]
private static extern bool SendMessage(IntPtr hWnd, int msg, IntPtr
wParam, int lParam);

private const int SC_RESTORE = 0xF120;
private const int WM_SYSCOMMAND = 0x0112;
private const int WM_MDIRESTORE=0x0223;

Form f=null;
private void Restore()
{
if (f == null)
{
f = new Form();
f.MdiParent = this;
f.Show();
}
else
{
if (f.WindowState == FormWindowState.Minimized)
{
for(int i=0;i<this.Controls.Count;i++)
{
if(this.Controls is MdiClient)
{
SendMessage(this.Controls.Handle,WM_MDIRESTORE, f.Handle, 0);
}
}
}
f.Activate();
}
}
Also, please note that I have changed SendMessage's wParam declaration from
int to IntPtr, so that we can pass MDi child form's handle to it.

After the modification, your sample project works well on my side.
===================================================================
Thank you for your patience and cooperation. If you have any questions or
concerns, please feel free to post it in the group. I am standing by to be
of assistance.

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 
G

Guest

Hi Jeffrey,

Thank you for your reply.
However, this is not exactly what I want.
I want my Restore function to work like the restore button on the MDI child
window (or the restore system menu entry),
i.e. I want it to restore the window to the state it was before it was
minimized.
(your code restores the window to it non-maximized size, the same as setting
f.WindowState=FormWindowState.Normal)

Anyway, I think I solved the problem:

As I understand it SendMessage(f.Handle,WM_SYSCOMMAND, (IntPtr)SC_RESTORE,0)
sends the same message the operating system sends when the mouse clicks on
the restore button, causing the application to think that the button was
pressed and so the same code is run.
This worked for menu items & toolbar buttons, but for some reason, when the
normal button was pressed the message was not working.
I started to think what might be different in this case (it SHOULD not
matter where SendMessage() is called from, should it?) AND I think I found
out what it was:

this Restore() function works:

private void Restore() {
if (f == null) {
f = new Form();
f.MdiParent = this;
f.Closed+=new EventHandler(closed);
f.Show();
}
else {
if (f.WindowState == FormWindowState.Minimized) {
this.Focus(); // this == the MDI parent.
SendMessage(f.Handle,WM_SYSCOMMAND, (IntPtr)SC_RESTORE,0);
}
f.Activate();
}
}

Apparently, when the menu entry & the toolbar button are pressed the MDI
parent has the focus,
while when the normal button is pressed it has the focus.
So, setting the focus to the MDI parent before sending the message ensures
that the message is handled properly.

Thanks anyway,
Nadav
 
J

Jeffrey Tan[MSFT]

Hi Nadav,

I am glad you have got what you want.

However, if you view the WM_MDIRESTORE in MSDN, you will see that:
"An application sends the WM_MDIRESTORE message to a multiple-document
interface (MDI) client window to restore an MDI child window from maximized
or minimized size. "
I think this should be the same function as "I want it to restore the
window to the state it was before it was minimized."

If I misunderstand you, please feel free to tell me.

Anyway, you may go with your solution. Thanks

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 
G

Guest

Hi Jeffrey,

Sending WM_MDIRESTORE to a MDI child window restores a window to a NORMAL
state (regardless of what state it was before).
(This is the same as setting form.WindowState=FormWindowState.Normal)
i.e. if you have a maximized window and you minimize it and you then send it
WM_MDIRESTORE it will be unminimized to *normal* state(NOT maximized).

However, the restore button on the window frame (the left most button of the
3 buttons on the right) works differently:
if you have a maximized windows and you minimize it and then click on the
restore button the window will be unminimized back to *maximized* state.

This is the behavior I was trying to reproduce.

Nadav
 
J

Jeffrey Tan[MSFT]

Hi Nadav,

Thanks for your feedback.

With your further feedback, I think I understand your request. Yes,
WM_MDIRESTORE only restore the form to the "normal" state, not its
"original" state.
However, form.WindowState=FormWindowState.Normal also will give what you
want. Then I see your original issue, that is: when we click on the
restore button of the from it will be restored to the minimized size.

Currently, I see that you are going with WM_SYSCOMMAND message to
workaround this issue. Actually, we can also use ShowWindow API with
SW_RESTORE parameter to get the same result, like this:

[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

private int SW_RESTORE=9;
private void Restore()
{
if (f == null)
{
f = new Form();
f.MdiParent = this;
//f.Closed+=new EventHandler(closed);
f.Show();
}
else
{
if (f.WindowState == FormWindowState.Minimized)
{
ShowWindow(this.f.Handle, SW_RESTORE);
}
f.Activate();
}
}
This works well on my side without the original restoring minizied form
issue. For your information.
==================================================================
Thank you for your patience and cooperation. If you have any questions or
concerns, please feel free to post it in the group. I am standing by to be
of assistance.

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 
G

Guest

Hi Jeffrey,

Thanks for your response,

I've tried using SW_RESTORE and it's working.

Which is very strange, because I could have sworn that I tried it before and
it was restoring maximized window to it's "normal" state.

Never mind, the problem is solved and that is the important thing.

Thanks for your help
Nadav
 
J

Jeffrey Tan[MSFT]

Hi Nadav,

Yes, I am glad that your problem is resolved. Anyway, if you need further
help, please feel free to post. Thanks

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top