Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can a mouse event sender be null? (Only on Windows 8.1+ TOUCH)

This only happens on a Windows 8.1+ touch device. I have some panels where the user can slide around with their finger. I have implemented PreFilterMessage so that I can catch the mouse move globally throughout the application without having to worry about child controls interfering.

I sometimes receive the generic error when clicking the form on a touch device:

IsCanceledMove() Object reference not set to an instance of an object.

I do my test in a blank form with no tool bar or toolstrips. There are only two panels. One has a large label, and the other has buttons and a textbox. Here is my mouse move filter where I am passing the sender that raised the event to the function that raises the exception.

private static void mouseFilter_MouseFilterMove(object sender, MouseFilterEventArgs e)
{
     IsCanceledMove(sender as Control);
}

public bool PreFilterMessage(ref Message m)
{
      Point mousePosition = Control.MousePosition;
      var args = new MouseFilterEventArgs(MouseButtons.Left, 0, mousePosition.X, mousePosition.Y, 0);

      switch (m.Msg)
      {
          case WM_MOUSEMOVE:
              if (MouseFilterMove != null)
                     MouseFilterMove(Control.FromHandle(m.HWnd), args);
              break;

            // more cases
      }

        // Always allow message to continue to the next filter control
        return args.Handled;
}

Next I am just doing a check to see if the control being moved over is a textbox, and if it's text is being highlighted. I also check a static boolean variable from another public class. If either of these are true, I set LastTouchedPanel to null. (Which is of type Panel)

// On Mouse move check if text is behing high lighted
private static void IsCanceledMove(Control c)
{
     try
     {
          // If highlighting text, stop moving
          if (c.GetType() == typeof(TextBox))
              if ((c as TextBox).SelectionLength > 0)
                  LastTouchedPanel = null;

          // Checks a static boolean variable from another control class.
          if (UKSlider.IsSliding)
               LastTouchedPanel = null;
     }
     catch (Exception ex)
     {
          MessageBox.Show("IsCanceledMove() " + ex.Message);
     }
}

How can an object send an event if it is null, and how do you handle this for ARM devices? This works fine on Vista to Windows 10, but not Windows 10 ARM.

Edit: I noticed Control.FromHandle(m.HWnd) sometimes returns Null in my PreMessageFilter on and ARM device. Putting a null check in obviously resolves the exception, but than some move events are missed.

like image 565
clamchoda Avatar asked Nov 08 '22 03:11

clamchoda


1 Answers

The null mouse event sender was directly related to using an IMessageFilter on a touch device. I have seen this happen on Windows 8.1 touch devices and Windows 10.

I started at the beginning of the event in my application, the PreFilterMessage. I wanted to make sure that there was actually a null sender created by Control.FromHandle. Sure enough, I could see that sometimes simply touching an open space on my form would return a null control. Presumably, a window handle that can't be cast to a .NET Control type.

The form is empty with a custom control. I am sure to only use controls that are safe with Control.FromHandle.

So this leads me to the big question - How can I be getting a null reference trying to convert these controls?

Since I know the problematic control can not be cast to a .NET control, I turn towards the window handle to find some information. I pay attention to the window handle raised on the null control.

When I click on an empty space in my form, I can see that I get the window handle for my form as expected. I noticed that when I get the null reference the handle doesn't come from any control in my application.

I think there was really only one answer, the gesture controller or something similar. This is when I start to try out some gestures. There it was, the first time in hours I was happy to see the null reference exception.

Turns out a move with one finger will get a mousemove in the PreMessageFilter from your form. A move with two fingers and you get the mousemove for your form AND as a mousemove message from the gesture controller. The gesture controller can not be cast to a control using Control.FromHandle.

What was happening here was I was getting lazy during my testing and resting my palm on the touch screen while clicking.

For my scenario I know I only want to handle the mousemove and don't care about the gesture. I can get away with ignoring the null handle. If you want to handle both, I think you should ignore it in your PreMessageFilter and properly handle the gesture with gesture messages.

like image 64
clamchoda Avatar answered Nov 14 '22 01:11

clamchoda