I'm working on a line connector control which connects from one pin to another. The typical WPF solution is to use mouse capturing when the user starts to drag the connection line. Unfortunately I need a mouse over indicator if the user is over a valid pin. But the indicator is never shown because the target pin never gets the mouse events when I already captured the mouse before.
I wrote a lighweight sample to show my problem:
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
WindowState="Maximized"
Title="MainWindow" Height="350" Width="525">
<Canvas>
<CheckBox x:Name="EnableMouseCapture" IsChecked="True" Content="Enable Mouse Capture" />
<Rectangle x:Name="Test" Fill="Blue" Width="40" Height="40" Canvas.Left="200" Canvas.Top="200" />
<Line x:Name="Line" Stroke="Black" StrokeThickness="1" IsHitTestVisible="False" />
</Canvas>
</Window>
And the code behind file:
using System;
using System.Windows.Input;
using System.Windows.Media;
namespace WpfApp1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
Test.MouseEnter += TestOnMouseEnter;
Test.MouseLeave += TestOnMouseLeave;
MouseDown += OnMouseDown;
}
private void TestOnMouseEnter(object sender, MouseEventArgs mouseEventArgs)
{
Console.WriteLine("(Test) MouseEnter");
Test.Fill = Brushes.Coral;
}
private void TestOnMouseLeave(object sender, MouseEventArgs mouseEventArgs)
{
Console.WriteLine("(Test) MouseLeave");
Test.Fill = Brushes.Blue;
}
private void OnMouseMove(object sender, MouseEventArgs mouseEventArgs)
{
Console.WriteLine("(Window) MouseMove");
var pos = mouseEventArgs.GetPosition(this);
Line.X2 = pos.X;
Line.Y2 = pos.Y;
}
private void OnMouseDown(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
Console.WriteLine("(Window) MouseDown");
MouseUp += OnMouseUp;
MouseMove += OnMouseMove;
var pos = mouseButtonEventArgs.GetPosition(this);
Line.X1 = pos.X;
Line.Y1 = pos.Y;
if (EnableMouseCapture.IsChecked == true)
{
CaptureMouse();
}
}
private void OnMouseUp(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
Console.WriteLine("(Window) MouseUp");
ReleaseMouseCapture();
MouseUp -= OnMouseUp;
MouseMove -= OnMouseMove;
}
}
}
If mouse capturing on the Canvas is enabled, the function TestOnMouseEnter
and TestOnMouseLeave
are not called. If mouse capturing is disabled these two functions are getting called.
I know that this is a WPF typical behavior, but did anyone maybe know how I get informed even another control has the capture?
Well, the way I understand it is that MouseCapture is there to make the code neat, so why don't you just not use mouse capturing.
https://msdn.microsoft.com/en-us/library/ms771301.aspx
What does it mean to "Capture the mouse" in WPF?
If this is getting in the way of making your app behave the way you want then why not just avoid using it? Sounds like the purpose of mouse capturing defeats what you are trying to achieve.
I found a similar question to this too, if you wanna take a look:
How to fire MouseEnter for one object if another object has mousecapture?
Edit: I did have an example in here but it didn't work properly, basically you need to manually hit test and then trigger your mouse enter and mouse leave manually.