Home » Microsoft TechnologiesRSS

Right Clicking a MDI child form does not change the focus to the form (MDIChildActivate event is not

My main program is a Multiple Document Interface (MDI). I create and show 2 or more child forms inside the MDI parent.  If I left click on the child forms, the focus and Z-index will change correctly. (E.G. I have form1 and form2 as the child forms, form1 is active.  I left click anywhere on form2 and the focus and z-index changes). This works correctly.

However, if I right click on the non-active form the focus and z-index does not change.  In my program I have context menus for the forms; these context menus appear correctly, but again, the focus and z-index does not change. 

I created a simple test program: a MDI parent form with the containing area and a button to create new forms.  The new button does:

Form

 

childForm = newForm();

 

childForm.MdiParent =

this;

childForm.Show();

With this, the same issue arises.  I can switch focus and z-index by left clicking between the child forms but I cannot switch by right clicking. I also checked the MDIChildActivate event.  This event is fired when I left click, but not when I right click.

I checked Excel (2003) and right clicking does change the focus and z-index.  So my assumption is that it is a bug. My hope is that I can find a workaround that doesn't involve adding code to everyone of my child forms.

Thanks in advance for your help.

 

8 Answers Found

 

Answer 1

I know that if .Show() is called on a child  form (or any of its components) before its parent  is set, a bug happens were the user can only switch  between the child forms  by clicking  on their titlebars or form  edges.  I discovered this bug a while back, but did not realize that Right clicking had the same symptoms and was not corrected when I moved any .Show() events in the form to code after .MdiParent was set.

Would this question be more applicable in either the c# forum or the Visual Studios 2008 .Net forum? I'm not sure if this bug happens in Visual Basic or if I was developing in another IDE.

 

Answer 2

Was anyone able to duplicate this bug?

 

Answer 3

I can reproduce it and it looks like a bug, but the best workaround I can suggest involves coding each child  form.

You could create  a base child form  and derive existing forms  from it (so you would have to change  base class for each child form). Use it just to handle right click  :

 

publicclass BaseChildForm : Form
	{
		protectedoverridevoid OnMouseDown(MouseEventArgs e)
		{
			if (e.Button == MouseButtons.Right)
			{
				this.Activate();
			}
			base.OnMouseDown(e);
		}
	}

 

Or you could achieve the same by handling the event  on the MDI parent  form, without changing the child forms:

 

privatevoid ShowNewForm(object sender, EventArgs e)
	{
		FormChild childForm = new FormChild();
		childForm.MdiParent = this;
		//childForm.Text = "Window " + (++childFormNumber);
		childForm.MouseDown += new MouseEventHandler(childForm_MouseDown);
		childForm.Show();
	}

	privatevoid childForm_MouseDown(object sender, MouseEventArgs e)
	{
		Form childForm = sender as Form;
		if (childForm != null)
		{
			if (e.Button == MouseButtons.Right)
			{
				childForm.Activate();
			}
			base.OnMouseDown(e);
		}
	}

 

 

Or you can go a little further and attach an event handler to all the child controls on the form so that the form becomes active  even if you right click on a button  or textbox.

 

publicclass BaseChildForm : Form
	{
		protectedvirtualvoid mouseDown(object sender, MouseEventArgs e)
		{
			if (e.Button == MouseButtons.Right)
			{
				this.Activate();
			}
		}
		protectedoverridevoid OnLoad(EventArgs e)
		{
			base.OnLoad(e);

			AddMouseDownHandler(this);
		}

		protectedvoid AddMouseDownHandler(Control ctrl)
		{
			ctrl.MouseDown += new MouseEventHandler(mouseDown);
			for (int i = 0; i < ctrl.Controls.Count; i++)
			{
				AddMouseDownHandler(ctrl.Controls[i]);
			}
		}
	}

EDIT: c# to vb converter

 

best regards,
Vladimir

 

 

 

Answer 4

Thank you for your help.

Implementing your 1st solution would not work for my project.  I already use several base classes that I have designed as the base for many other forms  in my application, so I would not be able to derive another class.  I did implement your 2nd solution and it works  if the user right clicks the form  itself.  However, if the user right clicks on any of the controls on the form, the control catches the fired  event, not the form, so the overridden method does not fire.  Im assuming that solution 1 and 2 have the same outcome, so I did not try to implement solution 1 in a test  program.  Overall, this does solve the problem some of the time and is easy to implement, but theres a geater probability the user will right click  one of the controls.

I also implemented solution 3 in a test program  and it worked perfectly.  However, the test program only had 5 controls on the form.  My application has hundreds of controls across several forms, so adding an event  handler to every control would not be feasible or smart. 

I spent some time rechecking all of the form events and seeing if any of them fire for right clicking  on a control.  However, I again  hit a wall and cannot find a better solution.

 

If anyone else has any ideas, I would greatly appreciate it.  I don't know them that well, but I'm wondering if any windows messages could be overridden to catch the right click.

Thanks.

 

Answer 5

Hi joseph,

I also implemented solution 3 in a test program  and it worked perfectly.  However, the test  program only had 5 controls on the form.  My application has hundreds of controls across several forms, so adding an event  handler to every control would not be feasible or smart. 

I don't see how would adding an event handler to each control could a have a significant effect on performance, even with hundreds of controls on the form. It's just a reference. It only gets executed once per click.

You can intercept windows message on the child  form but it will still not get the messages when a child control is clicked, cause those messages are handled by the controls themselves.

You can add a message filter but I don't really see how that would be more efficient then the solution 3 posted above. Anyway, here is the code: 

 

publicclass MessageFilter : IMessageFilter
	{
		constint WM_NCRBUTTONDOWN = 0x00A4;
		constint WM_RBUTTONDOWN = 0x0204;

		publicbool PreFilterMessage(ref Message msg)
		{
			switch (msg.Msg)
			{
				case WM_NCRBUTTONDOWN:
				case WM_RBUTTONDOWN:
					Control ctrl = Control.FromHandle(msg.HWnd);
					if (ctrl != null)
					{
						Form frm = null;
						if (ctrl is Form)
						{
							frm = ctrl as Form;
						}
						else
						{
							// frm = ctrl.TopLevelControl as Form;// TopLevelControl returns MDIParent so we need to search for the BaseChildFrom or just a form  ...// maybe a better idea on how to find the form ?while (ctrl.Parent != null)
							{
								frm = ctrl.Parent as BaseChildForm;
								if (frm != null)
									break;

								ctrl = ctrl.Parent;
							}
						}

						if (frm != null)
							frm.Activate();
					}
					break;
			}
			
			returnfalse;
		}
	}

 

and you need to add the message filter in the Main procedure.

 

staticclass Program
	{
		[STAThread]
		staticvoid Main()
		{
			Application.EnableVisualStyles();
			Application.SetCompatibleTextRenderingDefault(false);
			Application.AddMessageFilter(new MessageFilter());
			Application.Run(new MDIParent1());
		}
	}

 

If you are not satisfied with the solution it would be a good idea to unmark the thread As Answered. That way you will most likely get more attention from people who are here to help. When a thread is solved a good portion of them may skip it all together and focus  their time to unsolved threads . You can always mark all the posts you found helpful as Answers when you are done and want to close the thread.

best regards,
Vladimir

 

Answer 6

Vladimir is right. A message filter is probably less efficient than event  handlers because your message filter code will be executed for every message received by your entire application. Whereas event handlers are limited to only the events you have registered for those controls.
 

Answer 7

I went ahead and unmarked your answer just in case someone else may have another solution. I'm still hoping that theres a solution that doesn't involve making multiple  changes (even in a loop) to my child  forms. 

So far it looks like your third solution does work the best. I understand both you and Shawn's point that an event  handler would make more sense to use instead of a message filter. 

Ideally, I would love to be able to just add one event handler to each child form  when it is created  to solve this problem, but I guess there isn't some simple  fix.

 

Thanks again  for all of your help.

Joe

 

Answer 8

Hi,

 

Use event  handler is same. Find the message in the form  and call a method.

 

The following thread shows an example of creating message event.

http://social.msdn.microsoft.com/forums/en-US/Vsexpressvcs/thread/46d8cba4-1266-4f39-a27b-5e86a4cf3583/

 

Hope this helps.

 

Best regards,

Ling Wang

 
 
 

<< Previous      Next >>


Microsoft   |   Windows   |   Visual Studio   |   Follow us on Twitter