0

In my very simple WPF app, I have a canvas with a bitmap image that I'm wanting to allow the user to "capture" a section of. In order to select this section, the user should click the left mouse button down (which selects the "top left") and then, keeping the button down, move to the "bottom right" and release the left button - this completes the selected area . In order to highlight what has been selected, I'm drawing a red rectangle - which moves as the mouse moves.

My initial idea was that once the button was clicked down, I could detect that by the OnMouseLeft down event. And I would detect the release by the the OnMouseLeftUp event. The first part seems to be working fine but the OnMouseLeftUp doesn't seem to be fired, at least not if a I "drag" the mouse while the left button is down, and then release it. (It does seem to fire if I click the left button quickly.)

I've got around this problem by checking in the OnMouseMove event whether the mouse left is still down. This works, so my code is achieving what I want it to do. However, I'm still curious as to why does the OnMouseLeftUp event not seem to fire? I still not sure what was wrong with my original approach. I've done some searching and have come across suggestions that maybe another event (perhaps the OnMouseMove?) is pre-empting that OnMouseLeftUp event and that I need to subscribe to the PreviewMouseLeftButtonUp event. I tried that without success either - but I definitely wasn't sure what i was doing wrong!

<Window x:Class = "WpfApp_Mouse_Test.MainWindow" 
    xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:d = "http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:local = "clr-namespace:WpfApp_Mouse_Test" 
    mc:Ignorable = "d" Title = "MainWindow" Height = "350" Width = "604">

    <StackPanel>
        <Canvas x:Name = "canvas" Background = "LightBlue" 
         MouseMove = "OnMouseMove" 
         MouseLeftButtonDown="OnMouseLeftDown" 
         MouseLeftButtonUp = "OnMouseLeftUp"
         Height = "150" Margin = "20">
        </Canvas>
        <TextBlock x:Name = "txt1" Height = "31" HorizontalAlignment = "Right" 
         Width = "250" Margin = "0,0,294,0" />
    </StackPanel>
</Window>

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;

namespace WpfApp_Mouse_Test
{
    public partial class MainWindow : Window
        {
        bool mouseLeftStillDown = false;
        Point startPos = new Point();
        Rectangle markerRect;
        
        public MainWindow()
        {
            InitializeComponent();
            //canvas.AddHandler(MouseLeftButtonUpEvent, new RoutedEventHandler(canvas_MouseUp), true);
        }

        private void OnMouseMove(object sender, MouseEventArgs e)
        {
              
            if(e.LeftButton == MouseButtonState.Released)
            {
                mouseLeftStillDown = false;  //I need this because the MouseLeftUp event doesn't seem to fire
            }

            //Draw Rectangle
            if (mouseLeftStillDown)  
            { 
                canvas.Children.Remove(markerRect);  //Remove old rectangle
            
                markerRect = new Rectangle()
                {
                    Width = Math.Abs(e.GetPosition(canvas).X - startPos.X),
                    Height = Math.Abs( e.GetPosition(canvas).Y - startPos.Y),
                    Fill = Brushes.Transparent, Stroke = Brushes.Red, StrokeThickness = 1
                };
                canvas.Children.Add(markerRect);
                Canvas.SetLeft(markerRect, Math.Min(startPos.X,e.GetPosition(canvas).X));
                Canvas.SetTop(markerRect, Math.Min(startPos.Y, e.GetPosition(canvas).Y));
            }

            txt1.Text = $"Mouse left button down: {mouseLeftStillDown.ToString()} ";
        }


        private void OnMouseLeftDown(object sender, MouseButtonEventArgs e)
        {
            mouseLeftStillDown = true;
            canvas.Children.Remove(markerRect);  //Remove old rectangle 
            startPos = e.GetPosition(canvas);
            txt1.Text = "(OnMouseLeftDown) left button down : " + mouseLeftStillDown.ToString();
        }
        
        //This doesn't seem to fire on release of the left mouse button - at least not regularly!
        private void OnMouseLeftUp(object sender, MouseButtonEventArgs e)
        {
            mouseLeftStillDown = false;
            txt1.Text = "(OnMouseLeftUp) left button down : " + mouseLeftStillDown.ToString();
        }
    }
}
HedgePig
  • 469
  • 3
  • 10
  • 2
    What if you call `canvas.CaptureMouse();` in OnMouseLeftDown (and of course `canvas.ReleaseMouseCapture();` in OnMouseLeftUp)? – Clemens Jul 09 '20 at 11:18
  • That works! I didn't know about that - thank you. – HedgePig Jul 09 '20 at 11:40
  • I didn't know about that - thank you. Actually now, I have to check that my highlighted rectangle is still within the border, which my dirty fix handled automatically. But CaptureMouse is definitely worth knowing about. (I still wonder what event was intercepting and preventing the OnMouseLeftUp from firing.) Once again thank you - very helpful information! – HedgePig Jul 09 '20 at 11:49

0 Answers0