Solved

What is the best way to activate a form when clicking on any part of it in Winforms MDI App

Posted on 2013-11-17
20
320 Views
Last Modified: 2014-05-07
Hi Guys,

I am getting complaints from users of an application I am working on that they have to click on the title bar or the border of the form to bring the form to the front when it is partially obscured by other forms.  Other applications seem to accept clicks anywhere and provide this functionality.

I cannot find anything in the framework that handles this for WinForms, and modifying every form to add a handler for every control is not practical so I am considering  a simple method to iterate the controls and attach a handler; something like this (untested and off the top of my head):

    private void HookMouseEvents() 
    {
        foreach (Control ctl in this.Controls) 
        {
            ctl.MouseClick += new MouseEventHandler(HandleMouseClick);
        }
    } 

    void HandleMouseClick(object sender, MouseEventArgs e)
    {
        this.Activate();
    }

Open in new window


If this is a valid approach I am considering implementing it in a base form so that it percolates across the entire application.

Is there a better way to handle this requirement?  Any other comments or suggestions?
0
Comment
Question by:chrisbray
  • 12
  • 6
  • 2
20 Comments
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 39655025
>>Other applications seem to accept clicks anywhere and provide this functionality.

I thought that was normal behaviour - do you have code somewhere that would stop it such as hooking the mouse elsewhere in the app?

ps.  Is it only your app on these PC's, maybe they have some malware running in the background.
0
 
LVL 3

Author Comment

by:chrisbray
ID: 39655085
Hi Andy,

No, it is standard behaviour easily reproducible by knocking up a quick MDI app.  If you click on any control (e.g. a panel docked to Fill on the form) then the click is ignored...

To get it to come to the front you have to click on any border or the title bar.

Chris Bray
0
 
LVL 40
ID: 39655416
Look carefully at your code. Child forms with a docked Panel behave as you would expect. You can click anywhere on the Form title bar, the Panel or the controls contained in the Panel, and it gets activated.

There is something in your code that traps a Click somewhere.

I would put a break in the MouseClick events of the Panel and the child form, and in the Activated event of the child Form, and follow from there to see what happens. Might also do it in these events on the MDI form.
0
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 39655588
I still stand by my first comment (which James agrees with) which is you experience abnormal behaviour.

I have made a small MDI based app, added a panel to the child form and click on the panel does bring the window to the foreground.
0
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 39655612
>>No, it is standard behaviour easily reproducible by knocking up a quick MDI app.

Do you mean this behaves this way on your PC ?  
Could you do just that, zip the project and upload it to EE so others can test it - that way it would be easy to tell if it works correctly on other PC's.
0
 
LVL 3

Author Comment

by:chrisbray
ID: 39655697
Hi Guys,

Thanks for the input.  In light of your comments I have built another test MDI project and find that as you all said it operates as I would expect.  The same thing in the original project (i.e. adding a child form identical to that in the test project) does *not* behave in the same way.

This suggests something in the main application affecting all forms, so I am investigating further on that basis.  I will report back.
0
 
LVL 3

Author Comment

by:chrisbray
ID: 39655708
Hi Guys,

Next level of testing shows that if I add a call to Activate() in the form click event of the child form in the main application it reverts to acting as expected.  However, doing the same to the base form of the application does not have the same effect.  

Further investigation shows that not all forms inherit from the base form provided for the application, some inherit directly from Form.  However, adding a handler to click event on those forms makes no difference.   Further tests show that the click event does not fire at all...

As you all suggested, this indicates that something could be swallowing the click but I have no idea how that could be happening given that forms of all types with different ancestors and controls are affected.  Any further suggestions as to where I could look would be appreciated!
0
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 39655760
>>However, doing the same to the base form of the application does not have the same effect.  

Check what this base form is doing, specifically anything to do with mouse events.  (Or search your complete solution for the word mouse - maybe that helps track things down.)
0
 
LVL 40
ID: 39655775
It can be anything.

Search for e.Cancel through your code. You might be cancelling an event such as Validating on a Control or a Form.

You might want to put a breakpoint in the MouseCaptureChanged event of the main and child forms. If it is hit, the Call Stack might indicate that something is taking the Mouse events away from the form.

Step through the code from a breakpoint in the MouseDown event of the Panel or one of the controls it contains.

Maybe the same thing from the MdiChildActivate of the MDI form. It might give you a clue.

Tracing through all the events with Debug.WriteLine is a pain, but sometimes it's the only way to diagnose a problem such as yours.
0
 
LVL 3

Author Comment

by:chrisbray
ID: 39656294
Hi Guys,

Working through everything I can think of, and have determined that if I place controls on the form but leave part of the form visible (e.g. add a panel but instead of Dock | Fill I give it a visible border 20px from the form edge) then clicking on the form works as expected but clicking on the panel or any control that it contains does not bring the form to the front.

What is more, the FormClick event does not fire when the panel or the controls it contains  are clicked but it does when the form is clicked.

I can find nothing that is cancelling anything, and I have tested with an empty form and a standard .Net panel where I have added no code whatsoever but still get the same results...
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 3

Author Comment

by:chrisbray
ID: 39656391
Hi Guys,

Replicated the exact same scenario in a new MDI project, and that works exactly as expected with the child forms coming to the front regardless of the control that is clicked upon.  I have tried both with framework controls and with my own custom controls with no difference being apparent.  Therefore it would appear that I have just not found the issue in my code that is preventing the click from propagating, but it would appear not to be related to the controls themselves.
0
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 39656583
>>I can find nothing that is cancelling anything, and I have tested with an empty form and a standard .Net panel where I have added no code whatsoever but still get the same results...

Wierd.  You must have something somewhere that is trapping the mouse events, especially as you do not get this behaviour with the 'new MDI project'.

Have you searched the project for words such as mouse ?
0
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 39656587
ps.  When you run your app does it use any third party logging/tracking tools ?
0
 
LVL 3

Author Comment

by:chrisbray
ID: 39656610
Hi Andy,

No, no third party logging or tracking tools.

I have searched, but not got through them all.  This is a major application with over 100 projects and several million lines of code, so the number of hits is huge!  I am trying to limit the results by restricting searches to the likely areas e.g. Forms based stuff and skip the modules, presenters, services, etc. but it is still a mammoth task to check them all.
0
 
LVL 3

Author Comment

by:chrisbray
ID: 39734093
Hi Guys.

I believe that I have found the problem.  By Googling I was able to find quite a few people who also have this issue, but very few references that would solve the problem.  I did eventually find this example of how to reproduce the issue here - http://www.pcreview.co.uk/forums/mdi-child-forms-functionality-does-not-work-correctly-windows-f-t2894221.html:

1) Start a C# Windows Form Application in .NET

2) For the Default Form1 that is created set the "IsMdiContainer" property
to true in the properties window.

3) Add another form "Form2" to the application and add a textBox to it

4) Now add a Button to the Mdi Parent Form (Form1), Change the caption of
the button to "Child" and add the following code in the click event of this
button :
Form2 frm = new Form2();
frm.MdiParent = this;
frm.Show();

Open in new window

5) Now Run the application. When the MDI Parent Form Shows up, click the
button on it Twice. Two MDI Child windows will show up in the mdi container.
Switch between the two by clicking in their textboxes (Not the titlebars) and
you will see that the input focus changes. Note that the child form that has
the input focus has an active title bar, i.e the two child forms toggle back
and forth in their Zorder

6) Now stop the application and change the code written in step 4 above as
follows:
Form2 frm = new Form2();
IntPtr handle = frm.Handle; // This will force the handle
creation on the form
frm.MdiParent = this;
frm.Show();

Open in new window


7) Now run the application again. Once again click the button on the MDI
Parent twice. Two child windows show up. Try switching between them by
clicking in their textboxes. The Input focus moves, but the highlight on the
Mdi child windows does not. In fact if you cascade them you will see that the
forms do NOT change their Z Order. But if you click on their titlebars then
they work fine.


Looking at the code I am working with part of the initialisation of all MDI Child forms inserts the form handle into the tag so that it can be read by other parts of the code when needed:

Tag = Handle;

Open in new window


This exactly replicates the test example by forcing the handle before the MDI Parent has been set, and therefore presumably explains the issue. I do need to modify some of the other code for final proof, but I am fairly sure that this is the cause.  I will report back for the benefit of others.
0
 
LVL 3

Author Comment

by:chrisbray
ID: 39734114
OK.  I can confirm that transposing the idea from example code to the MDI test solution I created does indeed produce *exactly* the results I have experienced with the larger application.  However, initial tests with the app suggest that simply moving the code that sets the Tag to the value of the Handle into the Load event does not on its own resolve the problem.

Still, we now know *why* it happens and we can reproduce the issue so all we need to do now is resolve it within the application code...
0
 
LVL 3

Author Comment

by:chrisbray
ID: 39734145
Further tests show that if you open ANY child form with a call to the handle prior to setting the MDI parent it breaks ALL the forms including any that are currently open.  This break remains extant until the application is restarted.  Here you go for example code that proves this point.

    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }

        private void addChildToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var form = new ChildForm();
            IntPtr handle = form.Handle; // This will force the handle
            form.MdiParent = this;
            form.Show();
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void addDifferentChildToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var form = new ChildForm2 {MdiParent = this};
            form.Show();
        }
    }

Open in new window


I have not given the code for the forms since it is trivial - just the default code from creating a new form.

Using the above code, if you create two copies of ChildForm2, they switch Z order absolutely correctly.  If you then create one copy of ChildForm ALL THREE forms now exhibit the same inappropriate behaviour.

Closing the ChildForm does not bring back the correct behaviour, nor does opening another ChildForm2 - the new form remains broken...

This suggests that I will have to go through the entire application searching out any instance where the tag is set in the initialiser and move it outside. I should be able to prove this with a limited experiment by adjusting the automatically displayed forms first and opening only forms that have the moved tag setting and see if the correct behaviour is displayed.  Again, I will report back for the benefit of others.
0
 
LVL 3

Author Comment

by:chrisbray
ID: 39734928
Still struggling on with this issue... Simply changing the location of the code that sets the tag in the first couple of forms we open is insufficient in the main application.  Yet, doing so in the MDI Test application does resolve the issue. This suggests that something else is setting a value somewhere other than in the places we expect and that therefore there will be a large amount of going through code looking for missed calls to handles possibly in different circumstances to those we have detected so far.

It may take quite some time to work through all that code, so the next report may be a while but I will report back for the benefit of others that experience the same problem.
0
 
LVL 3

Accepted Solution

by:
chrisbray earned 0 total points
ID: 40038586
Hi All,

After a number of months of hard work, I have finally been able to identify and resolve all the issues that caused this problem.  There were two main parts to the problem:

1.

Using the Handle to uniquely identify individual forms

2.

Cursor control code that was itself calling the Handle property on the form
The solution to the first issue was to create and reference a Guid for each form, which ensured that there were no cases where the use of the identifier caused the handle to be accessed.

The solution to the second issue was more complex, and involved working through and removing all calls to the cursor control code that in turned used the handle in an API call to set or reset the form cursor.

With this two approaches implemented throughout the application the problem has now been resolved as far as we can tell.  Because there is so much code there is a small possibility that we may have missed one somewhere, but if it surfaces we will recognise the symptoms and be ready with the solution.
0
 
LVL 3

Author Closing Comment

by:chrisbray
ID: 40046610
I found the cause of the problem for myself, and worked through to the solution for myself.  I have added the solution information for the benefit of others that may be experiencing the same or similar issues.
0

Featured Post

Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
Problem to go to Web page 2 52
Spacing between controls 4 16
fomat Json objects 6 18
Saveas need to save a copy 16 11
We all know that functional code is the leg that any good program stands on when it comes right down to it, however, if your program lacks a good user interface your product may not have the appeal needed to keep your customers happy. This issue can…
More often than not, we developers are confronted with a need: a need to make some kind of magic happen via code. Whether it is for a client, for the boss, or for our own personal projects, the need must be satisfied. Most of the time, the Framework…
Internet Business Fax to Email Made Easy - With eFax Corporate (http://www.enterprise.efax.com), you'll receive a dedicated online fax number, which is used the same way as a typical analog fax number. You'll receive secure faxes in your email, fr…
Polish reports in Access so they look terrific. Take yourself to another level. Equations, Back Color, Alternate Back Color. Write easy VBA Code. Tighten space to use less pages. Launch report from a menu, considering criteria only when it is filled…

758 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

22 Experts available now in Live!

Get 1:1 Help Now