WPF鼠标移出控件范围,导致鼠标事件无法触发的解决方法
1 <Window x:Class="WpfApp6.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:WpfApp6" 7 mc:Ignorable="d" 8 Title="MainWindow" Height="100" Width="200"> 9 <Grid> 10 <TextBlock x:Name="lblTest" Text="Test" HorizontalAlignment="Center" VerticalAlignment="Center" MouseDown="lblTest_MouseDown" MouseUp="lblTest_MouseUp"/> 11 </Grid> 12 </Window>
1 using System.Windows; 2 using System.Windows.Input; 3 using System.Windows.Media; 4 5 namespace WpfApp6 6 { 7 /// <summary> 8 /// MainWindow.xaml 的交互逻辑 9 /// </summary> 10 public partial class MainWindow : Window 11 { 12 public MainWindow() 13 { 14 InitializeComponent(); 15 } 16 17 private void lblTest_MouseDown(object sender, MouseButtonEventArgs e) 18 { 19 lblTest.Background = Brushes.Green; 20 } 21 22 private void lblTest_MouseUp(object sender, MouseButtonEventArgs e) 23 { 24 lblTest.Background = Brushes.Red; 25 } 26 } 27 }
需求场景:如上述代码,窗口一个TextBlock,鼠标在该控件上按下时,背景色置为Green,鼠标释放时,背景色置为Red。
问题场景:当鼠标在TextBlock上按下时,背景色按照预想中的置为了Green,但当鼠标移动到TextBlock外的范围在释放鼠标时,MouseUp事件没有被触发。
问题原因:当鼠标在TextBlock上时,此时鼠标被TextBlock控件捕获,所以释放鼠标时可以按照预想将背景色置为Red的;但当鼠标没有在TextBlock上而是在其他地方即Window上时,TextBlock控件丢失了鼠标捕获,而是被Window捕获,所以鼠标释放时,也就不会触发TextBlock的MouseUp事件,应该是会触发Window的MouseUp事件;
解决办法:
第一步:使用Mouse.Capture()设置鼠标捕获,当一个鼠标捕获被设置到一个元素时,鼠标就只能与该元素进行交互,无法与其他元素进行交互,将代码修改为如下所示:
1 using System.Windows; 2 using System.Windows.Input; 3 using System.Windows.Media; 4 5 namespace WpfApp6 6 { 7 /// <summary> 8 /// MainWindow.xaml 的交互逻辑 9 /// </summary> 10 public partial class MainWindow : Window 11 { 12 public MainWindow() 13 { 14 InitializeComponent(); 15 } 16 17 private void lblTest_MouseDown(object sender, MouseButtonEventArgs e) 18 { 19 lblTest.Background = Brushes.Green; 20 if (sender is IInputElement ele) 21 { 22 // 将鼠标捕获到触发该事件的元素 23 Mouse.Capture(ele); 24 } 25 } 26 27 private void lblTest_MouseUp(object sender, MouseButtonEventArgs e) 28 { 29 lblTest.Background = Brushes.Red; 30 Mouse.Capture(null);// 重置鼠标捕获 31 } 32 } 33 }
在MouseDown事件中,通过Mouse.Capture()将鼠标捕获到了TextBlock控件上,当我们鼠标在任意位置释放鼠标,即使没有鼠标没有在TextBlock上,也会触发TextBlock的MouseUp事件,因为我们设置了TextBlock鼠标捕获。
在MouseUp事件中我们需要通过传入null参数,重置鼠标捕获,否则会导致鼠标的所有交互都指向TextBlock,而无法与其他控件进行交互。
到这里貌似我们的需求可以得到满足了,但是,还有一些情况下会导致系统丢失鼠标捕获,即使使用了Mouse.Capture(),任然无法正常进入MouseUp事件。
代码修改如下:
1 using System.Windows; 2 using System.Windows.Input; 3 using System.Windows.Media; 4 5 namespace WpfApp6 6 { 7 /// <summary> 8 /// MainWindow.xaml 的交互逻辑 9 /// </summary> 10 public partial class MainWindow : Window 11 { 12 public MainWindow() 13 { 14 InitializeComponent(); 15 } 16 17 private void lblTest_MouseDown(object sender, MouseButtonEventArgs e) 18 { 19 lblTest.Background = Brushes.Green; 20 if (sender is IInputElement ele) 21 { 22 // 将鼠标捕获到触发该事件的元素 23 Mouse.Capture(ele); 24 } 25 MessageBox.Show("MouseDown"); 26 } 27 28 private void lblTest_MouseUp(object sender, MouseButtonEventArgs e) 29 { 30 lblTest.Background = Brushes.Red; 31 Mouse.Capture(null);// 重置鼠标捕获 32 } 33 } 34 }
如上代码,虽然在MouseDown事件中通过Mouse.Capture()设置了鼠标捕获,但当MouseDown事件中弹出系统对话框时,此时会导致鼠标捕获丢失,从而导致MouseUp事件无法触发。
第二步:使用UIElement类中提供的GotMouseCapture和LostMouseCapture事件,监听元素获取/释放鼠标捕获。
代码修改如下:
1 <Window x:Class="WpfApp6.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:WpfApp6" 7 mc:Ignorable="d" 8 Title="MainWindow" Height="100" Width="200"> 9 <Grid> 10 <TextBlock x:Name="lblTest" Text="Test" HorizontalAlignment="Center" VerticalAlignment="Center" MouseDown="lblTest_MouseDown" MouseUp="lblTest_MouseUp" UIElement.GotMouseCapture="lblTest_GotMouseCapture" UIElement.LostMouseCapture="lblTest_LostMouseCapture"/> 11 </Grid> 12 </Window>
1 using System.Windows; 2 using System.Windows.Input; 3 using System.Windows.Media; 4 5 namespace WpfApp6 6 { 7 /// <summary> 8 /// MainWindow.xaml 的交互逻辑 9 /// </summary> 10 public partial class MainWindow : Window 11 { 12 public MainWindow() 13 { 14 InitializeComponent(); 15 } 16 17 private void lblTest_MouseDown(object sender, MouseButtonEventArgs e) 18 { 19 if (sender is IInputElement ele) 20 { 21 // 将鼠标捕获到触发该事件的元素 22 Mouse.Capture(ele); 23 } 24 MessageBox.Show("MouseDown"); 25 } 26 27 private void lblTest_MouseUp(object sender, MouseButtonEventArgs e) 28 { 29 Mouse.Capture(null);// 重置鼠标捕获 30 } 31 32 private void lblTest_GotMouseCapture(object sender, MouseEventArgs e) 33 { 34 lblTest.Background = Brushes.Green; 35 } 36 37 private void lblTest_LostMouseCapture(object sender, MouseEventArgs e) 38 { 39 lblTest.Background = Brushes.Red; 40 } 41 } 42 }
至此,问题解决!