tags:

views:

464

answers:

5

I have a c# form (let's call it MainForm) with a number of custom controls on it. I'd like to have the MainForm.OnClick() method fire anytime someone clicks on the form regardless of whether the click happened on the form or if the click was on one of the custom controls. I'm looking for behavior similar to the KeyPreview feature of forms except for mouse clicks rather than key presses.

A: 

Catching a click on an open space on the form is easy, but to get a click that's actually on a control, you'll need the cooperation of that control to send it to the form.

One possibility is to place a transparent control over the entire form, and accept clicks onto that, deal with them, and then pass them onto the proper control underneath.

James Curran
I feel like there must be a more elegant solution than this.
Mykroft
A: 

This is a common pattern in development, its called the Observer pattern. There are lots of examples of Observer patterns and c# here is 1 example http://msdn.microsoft.com/en-us/library/ms954621.aspx but have a good google around.

Tim Jarvis
+1  A: 

The only way I've ever managed to do this is to handle the [c]Click[/c] event of every control. I don't believe the event is raised before the control processes it.

In WPF, there are "tunneling" preview events that provide this functionality, but that doesn't really help you in WinForms.

OwenP
Well it's too late to switch to WPF now but I'll have to seriously consider using it in the future I guess.
Mykroft
+2  A: 

In the form's ControlAdded event, add a MouseClick handler to the control, with the Address of the form's click event. I haven't tested this, but it might work.

Private Sub Example_ControlAdded(ByVal sender As Object, ByVal e As System.Windows.Forms.ControlEventArgs) Handles Me.ControlAdded

    AddHandler e.Control.MouseClick, AddressOf Example_MouseClick
End Sub

Private Sub Example_MouseClick(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseClick
    MessageBox.Show("Click")
End Sub
This still seems to be a little bit of a hack but this seems to be the best option I have.
Mykroft
+1  A: 

You can hook all the control's events, if you like, and then monitor that way. I assume there is some uber fancy Win32 api way to trap them all, but that is beyond me at the moment.

    public Form1()
    {
        InitializeComponent();
        HookEvents();
    }

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

    void Form1_MouseClick(object sender, MouseEventArgs e)
    {
        LogEvent(sender, "MouseClick");
    }

    // and then this just logs to a multiline textbox you have somwhere on the form
    private void LogEvent(object sender, string msg) {
        this.textBox1.Text = string.Format("{0} {1} ({2}) \n {3}",
            DateTime.Now.TimeOfDay.ToString(),
            msg,
            sender.GetType().Name,
            textBox1.Text
            );
    }

The output is something like this, showing all the events and who "sent" them up:

14:51:42.3381985 MouseClick (Form1) 
14:51:40.6194485 MouseClick (RichTextBox) 
14:51:40.0100735 MouseClick (TextBox) 
14:51:39.6194485 MouseClick (Form1) 
14:51:39.2131985 MouseClick (RichTextBox) 
14:51:38.8694485 MouseClick (Button)

HTH.

Andrew Backer