#692 – Common Controls Typically Do Not Capture the Mouse

It’s a convention within Windows that controls typically do not capture the mouse after they intercept a mouse down event.  For example, you click on a Button by pressing and releasing the left mouse button.  But if you press the left mouse button, and then move the mouse pointer off the button before releasing, the mouse will not get clicked.

Avoiding a mouse capture in this case allows the user to avoid a click action after inadvertently pressing the left mouse button down over the wrong user interface element.

#691 – IsMouseCaptured Indicates Whether Mouse Is Currently Captured

You can use the CaptureMouse and ReleaseMouseCapture methods to capture/release the mouse pointer, so that a user interface element will get all future mouse events.

You can also check the IsMouseCaptured property of a UIElement object at any time, to see whether this element has currently captured the mouse.  In the example below, IsMouseCaptured is true while we’re moving the mouse pointer across the label only while we have a mouse button pressed.

        private void Feast_MouseDown(object sender, MouseButtonEventArgs e)
        {
            Console.WriteLine("Feast_MouseDown");
            Label l = e.Source as Label;
            l.CaptureMouse();
        }

        private void Feast_MouseUp(object sender, MouseButtonEventArgs e)
        {
            Console.WriteLine("Feast_MouseUp");
            Label l = e.Source as Label;
            l.ReleaseMouseCapture();
        }

        private void Feast_MouseMove(object sender, MouseEventArgs e)
        {
            Label l = e.Source as Label;
            Console.WriteLine(string.Format("IsMouseCaptured={0}", l.IsMouseCaptured));
        }

#690 – Being Notified When Mouse Capture Is Lost

Your application can lose its mouse capture due to some system event.  When this happens, you might want to know that the capture was lost so that you can restore some application state.

You can discover when you’ve lost the mouse capture due to some external event by handling the LostMouseCapture event.

In the example below, the code normally releases the mouse capture and reverts the label’s color on a MouseUp event.  It also reverts the label’s color when it discovers that it has lost the mouse capture.

        private Brush savedBrush;

        private void Feast_MouseDown(object sender, MouseButtonEventArgs e)
        {
            Console.WriteLine("Feast_MouseDown");
            Label l = e.Source as Label;
            l.CaptureMouse();
            savedBrush = l.Background;
            l.Background = Brushes.Cyan;
        }

        private void Feast_MouseUp(object sender, MouseButtonEventArgs e)
        {
            Console.WriteLine("Feast_MouseUp");
            Label l = e.Source as Label;
            l.ReleaseMouseCapture();
            l.Background = savedBrush;
        }

        private void Feast_LostMouseCapture(object sender, MouseEventArgs e)
        {
            Label l = e.Source as Label;
            l.Background = savedBrush;
        }

#689 – An Application Can Lose Its Mouse Capture

You can capture the mouse on one mouse button event and then release it on another, with your application in some state while the mouse is captured.

For example, changing the background color of a label while the left mouse button is down.

        private Brush savedBrush;

        private void Feast_MouseDown(object sender, MouseButtonEventArgs e)
        {
            Console.WriteLine("Feast_MouseDown");
            Label l = e.Source as Label;
            l.CaptureMouse();
            savedBrush = l.Background;
            l.Background = Brushes.Cyan;
        }

        private void Feast_MouseUp(object sender, MouseButtonEventArgs e)
        {
            Console.WriteLine("Feast_MouseUp");
            Label l = e.Source as Label;
            l.ReleaseMouseCapture();
            l.Background = savedBrush;
        }

Although your code normally releases the mouse capture, it’s also possible that some system event causes the mouse capture to be lost.  (E.g. A system dialog pops up).  In this case, you won’t get the expected event, so your event handler never gets a chance to release the mouse capture.  Your application then ends up in an unacceptable state.  (E.g. Label’s background color wrong).

#688 – Moving an Element with the Mouse

Here’s a short example that uses the MouseDown, MouseMove and MouseUp events to move a user interface element around on the screen.  We place a Label in a Canvas and then bind its position to a couple of properties that keep track of the desired X and Y position of the label.  The properties derive from a base position for the label, plus the current mouse position.

Here’s the XAML:

    <Canvas>
        <Label Content="Feast" Background="ForestGreen" Padding="12,7"
               Canvas.Left="{Binding XPosition}" Canvas.Top="{Binding YPosition}"
               MouseDown="Feast_MouseDown"  MouseUp="Feast_MouseUp" MouseMove="Feast_MouseMove"/>
    </Canvas>

And the code:

    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;
        }

        private Point BasePoint = new Point(0.0, 0.0);
        private double DeltaX = 0.0;
        private double DeltaY = 0.0;
        private bool moving = false;
        private Point PositionInLabel;

        public double XPosition
        {
            get { return BasePoint.X + DeltaX; }
        }

        public double YPosition
        {
            get { return BasePoint.Y + DeltaY; }
        }

        private void Feast_MouseDown(object sender, MouseButtonEventArgs e)
        {
            Label l = e.Source as Label;
            if (l != null)
            {
                l.CaptureMouse();
                moving = true;
                PositionInLabel = e.GetPosition(l);
            }
        }

        private void Feast_MouseMove(object sender, MouseEventArgs e)
        {
            if (moving)
            {
                Point p = e.GetPosition(null);
                DeltaX = p.X - BasePoint.X - PositionInLabel.X;
                DeltaY = p.Y - BasePoint.Y - PositionInLabel.Y;
                RaisePropertyChanged("XPosition");
                RaisePropertyChanged("YPosition");
            }
        }

        private void Feast_MouseUp(object sender, MouseButtonEventArgs e)
        {
            Label l = e.Source as Label;
            if (l != null)
            {
                l.ReleaseMouseCapture();
                BasePoint.X += DeltaX;
                BasePoint.Y += DeltaY;
                DeltaX = 0.0;
                DeltaY = 0.0;
                moving = false;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string prop)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }
    }

The end result looks something like this:

#687 – What Happens If You Forget to Release The Mouse

A user interface element can capture the mouse, asking to receive all future mouse events, no matter where the mouse cursor is located.  You’ll typically capture the mouse in a mouse down or related event and then release the mouse in the corresponding mouse up event so that things go back to normal.

But what happens if something goes wrong and you forget to release the mouse?  At that point, the element that captured the mouse will continue to receive mouse input.  This means that other elements in your application that use the mouse will no longer function.

In the example below, the Raphael label captures the mouse on mouse down, but does not release it on mouse up.  Then when I click on the Button, the Raphael label is still getting the mouse events.

#686 – Using Animation to Make an Image Pulse

You can animate the opacity of an Image element to make it appear to pulse.  Below is one example of how to do this, using a Storyboard.  We animate the image’s Opacity property, down to 10% opaque over half a second and then back again.

    <StackPanel Margin="15">
        <CheckBox Content="Turn On" Margin="10" IsChecked="True"/>
        <StackPanel Orientation="Horizontal">
            <CheckBox Content="Tune In" Margin="10" VerticalAlignment="Center"/>
            <Image Name="funkyArrow" Source="Arrow.png" Margin="15,0">
                <Image.Triggers>
                    <EventTrigger RoutedEvent="Image.Loaded">
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation Storyboard.TargetName="funkyArrow"
                                    Storyboard.TargetProperty="Opacity"
                                    From="1.0" To="0.1" Duration="0:0:0.5"
                                    AutoReverse="True" RepeatBehavior="Forever"/>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger>
                </Image.Triggers>
            </Image>
        </StackPanel>
        <CheckBox Content="Drop Out" Margin="10"/>
    </StackPanel>


#685 – Capturing the Mouse

When you are handling mouse events, the event is generally fired by the control that the mouse is over.  For example, if you hold down the mouse button over one control, move the mouse, and release over another control, the first control will see the MouseDown event and the second will see the MouseUp event.

A control can request that it receive all future mouse events by executing the CaptureMouse event.  When it sees the event it was waiting for, it can then call the ReleaseMouseCapture event to go back to normal behavior.

        private void MouseDown_Raph(object sender, MouseButtonEventArgs e)
        {
            Console.WriteLine("Raphael sees MouseDown");
            ((UIElement)e.Source).CaptureMouse();
        }

        private void MouseUp_Raph(object sender, MouseButtonEventArgs e)
        {
            Console.WriteLine("Raphael sees MouseUp");
            ((UIElement)e.Source).ReleaseMouseCapture();
        }

With the code above, the “Raphael” label captures the mouse on a MouseDown, allowing it to see the corresponding MouseUp event, no matter where the mouse pointer is moved.

#684 – Mouse Events Occur for the Element That the Mouse Is Over

Mouse events normally fire for the element that mouse is current positioned over.  In the example below, notice that the Source property for the event handler always refers to the Label that the mouse is currently positioned over.

    <StackPanel MouseDown="Label_MouseDown" MouseUp="Label_MouseUp" MouseMove="Label_MouseMove">
        <Label Content="Sistine" Background="ForestGreen" Padding="10,20"/>
        <Label Content="Buonarroti" Background="Peru" Padding="10,20"/>
    </StackPanel>

 

        private void Label_MouseDown(object sender, MouseButtonEventArgs e)
        {
            Label l = e.Source as Label;
            if (l != null)
                Console.WriteLine(string.Format("MouseDown on {0}", l.Content));
        }

        private void Label_MouseUp(object sender, MouseButtonEventArgs e)
        {
            Label l = e.Source as Label;
            if (l != null)
                Console.WriteLine(string.Format("MouseUp on {0}", l.Content));
        }

        private void Label_MouseMove(object sender, MouseEventArgs e)
        {
            Label l = e.Source as Label;
            if (l != null)
            {
                Point p = e.GetPosition(null);
                Console.WriteLine(string.Format("MouseMove on {0} at ({1},{2})", l.Content, p.X, p.Y));
            }
        }

If we press the mouse button while over one Label, move to the other Label and release the button, we get:

#683 – MouseUp Can Happen in Different Control from MouseDown

You can press a mouse button down while the mouse pointer is located in one control, move the mouse, and then release the mouse button with the mouse pointer located in a different control.  When you do this, the first control will receive the MouseDown event and the second control will receive the MouseUp event.

    <StackPanel>
        <Label Content="Sistine" Background="ForestGreen" Padding="10,20"
               MouseDown="Label_MouseDown" MouseUp="Label_MouseUp"/>
        <Label Content="Buonarroti" Background="Peru" Padding="10,20"
               MouseDown="Label_MouseDown" MouseUp="Label_MouseUp"/>
    </StackPanel>

 

        private void Label_MouseDown(object sender, MouseButtonEventArgs e)
        {
            Label l = sender as Label;
            Console.WriteLine(string.Format("MouseDown on {0}", l.Content));
        }

        private void Label_MouseUp(object sender, MouseButtonEventArgs e)
        {
            Label l = sender as Label;
            Console.WriteLine(string.Format("MouseUp on {0}", l.Content));
        }