3

Basically what i want to do is to change a custom window's state from Maximized to Normal state and adjust the positions of the window, when user clicks and moves the mouse over TitleBar which is a simple border in my case.

The obvious thing to do is to attach an event-handler to MouseMove and check if the mouse left button is pressed:

    private void OnMouseMove(object sender, MouseEventArgs e)
    {
        if (e.LeftButton == MouseButtonState.Pressed)
        {
            Console.WriteLine("Mouse moved" + Mouse.Captured);
        }
    }

The problem is that the MouseMove occurs when the mouse capture changes. So in my application the window will snap to a Normal state after you open a popup and click the border, the snapping should happen after you start dragging.

Here is an XAML for the above code to prove the problem:

 <Grid MouseMove="OnMouseMove" Background="AliceBlue">
    <ComboBox Height="23">
        <ComboBoxItem>Test1</ComboBoxItem>
        <ComboBoxItem>Test1</ComboBoxItem>
        <ComboBoxItem>Test1</ComboBoxItem>
        <ComboBoxItem>Test1</ComboBoxItem>
        <ComboBoxItem>Test1</ComboBoxItem>
        <ComboBoxItem>Test1</ComboBoxItem>
    </ComboBox>
</Grid>

Issue Steps, run the above code in debug:

  1. Click the ComboBox to open its popup.
  2. Click on blue Grid (this will close the popup).

Notice: The Output window shows the Message "Mouse moved"

Expected behavior: Since only a click was made (there was no mouse move) i don't want the MouseMove event.

I understand that the MouseMove is fired when the Mouse.Captured element changes and this is normal, but i need a way to distinguish between a normal MouseMove and a MouseMove caused by a Mouse Capture.

EDIT:

A more complex example of this issue can be found in MahApps.Metro demo. The steps are the same as described above - open a popup and click on title bar when the window is in Maximized state. You will notice the window snapped from Maximized state to normal state. This shouldn't have happened, since you did not double-click, or drag the title bar.

My solution so far:

Since i know this is caused by mouse captured element changes, i handled the LostMouseCapture event and saved the mouse position:

    private Point mousePositionAfterCapture;

    private void OnLostMouseCapture(object sender, MouseEventArgs e)
    {
        mousePositionAfterCapture = e.GetPosition(this);
    }   

And in MouseMove handler i check if the position has changed after last lost mouse capture:

private void OnMouseMove(object sender, MouseEventArgs e)
{
    if (e.LeftButton == MouseButtonState.Pressed)
    {
        Point currentPossition = e.GetPosition(this);

        if (currentPossition == mousePositionAfterCapture)
            //means that the MouseMove event occurred as a result of 
            //Mouse Captured element change, we wait until an actual
            //mouse move will occure
            return;         

        Console.WriteLine("Mouse moved" + Mouse.Captured);
    }
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
Novitchi S
  • 3,711
  • 1
  • 31
  • 44
  • The above XAML and the EventHandler should be enough to reproduce the issue. The problem is that when you close the `ComboBox`'s popup by clicking somewhere inside the blue grid you will get a MouseMove event which i don't want. – Novitchi S Feb 04 '14 at 11:35
  • Hmmmm, change the ComboBox ClickMode to Press and let us know if that solves your issue... – dev hedgehog Feb 04 '14 at 11:46
  • 1
    The `ComboBox` does not have `ClickMode` property. – Novitchi S Feb 04 '14 at 12:41
  • I am sorry, that was ment to be posted on a ToggleButton question. Buttons have click mode. Have you tried using IsMouseDirectlyOver property?There is also IsMouseDirectlyOver event. – dev hedgehog Feb 04 '14 at 13:04
  • I've just found the solution to your last question about Focus, and you have already been deleted :(. Found a solution ?:) – Anatoliy Nikolaev Mar 10 '14 at 17:00
  • I have undeleted the question, please post a solution, i lost hope :) – Novitchi S Mar 10 '14 at 17:03

3 Answers3

2

As i learned more about Dragging i will answer this question myself. The Drag is an operation that should start when the mouse is moved and the left mouse button is down, but it should not start right away, to prevent accidentally started Drags the SystemParameters.MinimumHorizontalDragDistance and SystemParameters.MinimumVerticalDragDistance static members should be used.

private Point startPoint;

private void OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    startPoint = e.GetPosition(null);
}  

private void OnPreviewMouseMove(object sender, MouseEventArgs e)
{
    if (e.LeftButton == MouseButtonState.Pressed)
    {
        Point position = e.GetPosition(this);

        if (Math.Abs(position.X - startPoint.X) > SystemParameters.MinimumHorizontalDragDistance ||
            Math.Abs(position.Y - startPoint.Y) > SystemParameters.MinimumVerticalDragDistance)
           {
               StartDrag(e);
           }
    }
}

This solved the issue asked in this question since the drag will never start if the mouse did not changed its position.

Novitchi S
  • 3,711
  • 1
  • 31
  • 44
1

Thing that you want to achieve is related to event routing. So to get your code working as expected you'll have to modify it just a bit:

<ComboBox Height="23" MouseMove="HandleMouseMove">
...

and

private void HandleMouseMove(object sender, MouseEventArgs e)
{
     e.Handled = true;
}

Why this will work?
By adding MouseMove event we're creating routed event which will be responsible for handling all mouse move events bubbled to current level not yet handled by anyone else. Other words any event raised on ComboBox will bubble up the element tree until reaches appropriate handler which will set Handled = true or reaches top of the tree.

Anatolii Gabuza
  • 6,184
  • 2
  • 36
  • 54
0

You should set for several controls one event handler OnMouseMove, as this bubble event arrives from the control and he goes up to the root element. In our case, MouseMove event triggered on ComboBox and comes to Grid. Further, in event handler to check the type of the desired element and make the necessary action.

XAML

<Grid MouseMove="OnMouseMove" Background="AliceBlue">
    <ComboBox Height="30" MouseMove="OnMouseMove">
        <ComboBoxItem>1</ComboBoxItem>            
        <ComboBoxItem>2</ComboBoxItem>
        <ComboBoxItem>3</ComboBoxItem>
        <ComboBoxItem>4</ComboBoxItem>
    </ComboBox>
</Grid>

Code behind

private void OnMouseMove(object sender, MouseEventArgs e)
{
    var target = sender;

    if (target is Grid)
    {
        if (e.LeftButton == MouseButtonState.Pressed)
        {
            MessageBox.Show("Mouse moved in " + target.GetType());
        }
    }

    e.Handled = true;
}
Anatoliy Nikolaev
  • 22,370
  • 15
  • 69
  • 68
  • Nothing is changed if i use your example. The `MouseMove` event still occurs with target == Grid when the popup is closed by clicking on the grid. – Novitchi S Feb 04 '14 at 12:47
  • @Novitchi S: Maybe I do not fully understand the question, is necessary to consider in the Grid mouse events, or not? – Anatoliy Nikolaev Feb 04 '14 at 12:49
  • Please take a look at the edited question, hope its more clear now. – Novitchi S Feb 04 '14 at 13:22
  • @Novitchi S: Okay, I think I understand. You do not want that when you click on the Grid triggered MouseMove event, and it only triggered with `(e.LeftButton == MouseButtonState.Pressed)`.Right? Unfortunately, I can not really see how it works in the `MahApps` demo. – Anatoliy Nikolaev Feb 04 '14 at 16:15
  • You have understood correctly the problem. I found an ugly workaround that is not worth mentioning here. Still hoping for a better solution. – Novitchi S Feb 06 '14 at 09:18
  • 1
    @Novitchi S: Can you provide `ugly` solution in the edit of question, that I did not do too :). – Anatoliy Nikolaev Feb 06 '14 at 09:23