控件如何处理该控件之外的鼠标单击?

Posted

技术标签:

【中文标题】控件如何处理该控件之外的鼠标单击?【英文标题】:How can a control handle a Mouse click outside of that control? 【发布时间】:2011-10-09 08:38:36 【问题描述】:

我正在编写一个自定义控件,我希望当用户单击该控件时该控件从编辑状态切换到正常状态。我正在处理 LostFocus 事件,当用户离开或单击另一个可聚焦的控件时,这会有所帮助。但如果他们不点击可聚焦的东西,它就不会退出它的编辑状态。所以我想到了两个解决方案:

当它进入编辑状态时,沿着树向上走到最顶层的元素,并为MouseDownEvent 添加一个处理程序(并处理“已处理”事件)。在处理程序中,我会将控件从其编辑状态中踢出,并从最顶部的元素中删除处理程序。这看起来有点像 hack,但它可能会很好用。

示例代码:

private void RegisterTopMostParentMouseClickEvent()

   _topMostParent = this.FindLastVisualAncestor<FrameworkElement>();
   if ( _topMostParent == null )
      return;
   _topMostParent.AddHandler( Mouse.MouseDownEvent, new MouseButtonEventHandler( CustomControlMouseDownEvent ), true );


private void UnRegisterTopMostParentMouseClickEvent()

   if ( _topMostParent == null )
      return;
   _topMostParent.RemoveHandler( Mouse.MouseDownEvent, new MouseButtonEventHandler( CustomControlMouseDownEvent ) );
   _topMostParent = null;

使用Mouse.PreviewMouseDownOutsideCapturedElement 并向我的控件添加一个处理程序。在处理程序中,我会将控件踢出它的编辑状态。但我似乎没有让事件发生。 Mouse.PreviewMouseDownOutsideCapturedElement 什么时候启动?

示例代码:

AddHandler( Mouse.PreviewMouseDownOutsideCapturedElementEvent, new MouseButtonEventHandler( EditableTextBlockPreviewMouseDownOutsideCapturedElementEvent ), true );

【问题讨论】:

【参考方案1】:

只是为了澄清提供的关于鼠标焦点的答案 - 它很有用,但我必须做一些进一步的挖掘 + 挖掘才能得到真正有效的东西:

我试图实现类似组合框的东西,并且需要类似的行为 - 在单击其他内容时让下拉菜单消失,而控件不知道其他内容是什么。

我有一个下拉按钮的以下事件:

    private void ClickButton(object sender, RoutedEventArgs routedEventArgs)
    
        //do stuff (eg activate drop down)
        Mouse.Capture(this, CaptureMode.SubTree);
        AddHandler();
    

CaptureMode.SubTree 意味着您只能获取控件之外的事件,并且控件中的任何鼠标活动都会正常传递给事物。您没有在 UIElement 的 CaptureMouse 中提供此 Enum 的选项,这意味着您将获得对 HandleClickOutsideOfControl 的调用,而不是对控件内的任何子控件或其他处理程序的调用。即使您不订阅他们正在使用的事件也是如此 - 完整的鼠标捕获有点太多了!

    private void AddHandler()
    
        AddHandler(Mouse.PreviewMouseDownOutsideCapturedElementEvent, new MouseButtonEventHandler(HandleClickOutsideOfControl), true);
    

您还需要在适当的位置继续 + 删除处理程序,但为了清楚/简洁起见,我将其留在这里。

最后在处理程序中你需要再次释放捕获。

    private void HandleClickOutsideOfControl(object sender, MouseButtonEventArgs e)
    
        //do stuff (eg close drop down)
        ReleaseMouseCapture();
    

【讨论】:

某些控件(如 TextBox)在单击时往往会释放 caputre,因此每次控件出现 LostMouseCapture 事件时,您都必须重新附加处理程序。只是我的观察。 @Dave_cz 不太正确。事件订阅保持活动状态,因此您需要重复 Mouse.Capture(...)。只是我的观察【参考方案2】:

捕获鼠标。 当一个对象捕获鼠标时,所有与鼠标相关的事件都被视为具有鼠标捕获的对象执行该事件,即使鼠标指针位于另一个对象上。

【讨论】:

所以这很完美。我所要做的就是在进入编辑模式时捕获鼠标,然后当您在元素外部单击时,WPF 会自动将焦点从元素上移开。在我失去焦点后,我只需要小心 ReleaseMouseCapture()。谢谢! 我唯一需要注意的另一件事是,当您按下 Windows 键或其他应用程序启动时,我会失去鼠标捕获,而不会阻止我的控件处于编辑状态。所以我不得不处理 IsMouseCapturedChanged 事件。例如,private void CustomControlIsMouseCapturedChanged( object sender, DependencyPropertyChangedEventArgs e ) if ( (bool)e.NewValue == false) IsEditing = false; 由于mousecaption,所有其他控件都不能点击或者点击不会发生在它们上面。将鼠标悬停在元素上时,甚至元素也不再突出显示。如何解决?这种行为是不吉利的。【参考方案3】:

我通常获取父窗口并添加一个预览处理程序,即使已经处理。有时当 MouseCapture 不够用时,这种技术就派上用场了:

Window.GetWindow(this).AddHandler
(
    UIElement.MouseDownEvent,
    (MouseButtonEventHandler)TextBox_PreviewMouseDown,
    true
);

【讨论】:

【参考方案4】:

我会以不同的方式处理此问题 - 当用户单击表单的另一部分时,让包含控件的表单从控件中移除焦点。

让控件实际上失去焦点比试图让控件“模拟”在某些情况下失去焦点要干净得多,而实际上它并没有。请记住,除非控件真的失去焦点,否则它仍然会接受键盘输入等内容。

【讨论】:

所以你是说我让表单具有焦点?因为除非将焦点转移到另一个元素,否则焦点不会自动消失,对吗?例如,这不会从 TextBox 获得焦点:&lt;Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="FocusTest.MainWindow" x:Name="Window" Width="640" Height="480"&gt; &lt;Grid&gt; &lt;TextBox HorizontalAlignment="Center" VerticalAlignment="Center" Width="75"/&gt; &lt;/Grid&gt; &lt;/Window&gt; @chrislarson 您可以将另一个控件(例如标签,可能没有文本)设置为焦点 - 请参阅Is there a way to cause an UI element to loose focus 这对我来说当然没问题,我仍然可以根据控件是否处于编辑状态来判断它是否具有焦点。但是我仍然需要一种方法来可靠地使控件在用户单击控件外部时失去焦点。 @chrislarson 处理表单上的MouseClick 事件 - 如果用户单击控件,则控件将首先接收事件,因此可以处理它,防止它被冒泡到表单处理程序.但是,此事件将处理表单上其他地方的点击,您可以在此处将焦点移动到虚拟焦点控件。

以上是关于控件如何处理该控件之外的鼠标单击?的主要内容,如果未能解决你的问题,请参考以下文章

如何为Textbox控件添加一个鼠标单击(click)事件

c# 为自定义控件添加鼠标双击事件

如何使用 WebBrowser 控件模拟鼠标单击

C#怎样判断鼠标移动到控件上了

Linux QT 中控件QTableView相应鼠标事件

如何在富编辑控件中实现对 URL 的鼠标单击