2011-07-20 21 views
20

我正在写一个自定义控件,并且希望控件在用户单击控件时从编辑状态切换到正常状态。我正在处理LostFocus事件,这有助于当用户退出或者他们点击另一个Focusable控件时。但是如果他们没有点击Focusable,它将不会退出编辑状态。所以我心里有两个解决方案:控件如何处理鼠标在该控件之外的点击?

  • 走到树最顶端的元素,当它进入到编辑状态,并添加一个处理MouseDownEvent(和处理“处理”事件)。在处理程序中,我会将控制权踢出编辑状态,并从最上面的元素中移除处理程序。这似乎有点破解,但它可能会运行良好。

示例代码:

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); 

回答

12

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

+2

所以这个工作很完美。我所要做的只是在进入编辑模式时捕捉鼠标,然后当点击它之外时,WPF会自动将焦点从元素上移开。在失去焦点后,我只需要小心ReleaseMouseCapture()。谢谢! – chrislarson

+3

我唯一需要注意的其他事情是,当您按下Windows键或其他应用程序启动时,我将失去鼠标捕捉而不停止我的控件进入编辑状态。所以我不得不处理IsMouseCapturedChanged事件。例如,private void CustomControlIsMouseCapturedChanged(object sender,DependencyPropertyChangedEventArgs e) if((bool)e.NewValue == false) IsEditing = false; } }' – chrislarson

0

我会采取这种不同的方式 - 当用户单击窗体的另一部分时,将包含控件的窗体从控件中移除焦点。

拥有控制实际上松散的焦点远比在某些情况下试图让控制“模拟”焦点丢失时更清洁,而事实上它没有。请记住,除非控件真的失去了焦点,否则它仍然会接受键盘输入之类的东西。

+0

那么你是说我让表单可以聚焦?因为焦点不会被自动带走,除非它被转移到另一个元素,对吗?例如,这不会从TextBox获得焦点:' \t \t \t \t ' – chrislarson

+0

@chrislarson你可以设置另一个控件(例如一个标签,可能是一个没有文本的标签)来关注 - 请参阅[是否有一种方法来导致UI元素失去焦点](http://stackoverflow.com/questions/) 2664460/wpf-is-there-a-way-to-cause-an-ui-element-to-loose-focus-without-changing-the-fo) – Justin

+0

这对我来说确实很好,我仍然可以基于不是控件处于编辑状态,而是由它是否具有焦点。但是我仍然需要一种方式来可靠地让用户在控制之外点击时失去焦点。 – chrislarson

30

只是为了澄清提供了关于鼠标焦点的答案 - 这是有用的,但我不得不做一些进一步的挖掘+瞎得到的东西,实际工作:

我想实现像一个组合框,需要类似的行为 - 当点击其他某些东西时,让这个下拉消失,而没有控制知道其他东西是什么。

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

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

的CaptureMode.SubTree意味着你只能得到是控制和控制任何鼠标活动以外发生的事件是通过传递到事情正常。你没有在UIElement的CaptureMouse中提供这个枚举的选项,这意味着你将会调用HandleClickOutsideOfControl INSTEAD来调用控件中的任何子控件或其他处理程序。即使您不订阅他们正在使用的事件,情况也是如此 - 完全鼠标捕捉有点太过分了!

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

您还需要挂到+删除的处理程序在适当的点,但我已经离开了这一点这里清晰/简洁起见。

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

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

像TextBox这样的一些控件倾向于在点击时释放caputre,所以每次控制上升'LostMouseCapture'事件时你都必须重新附加处理程序。只是我的观察。 –

0

我通常会得到父窗口,并添加一个预览处理程序,即使已经处理。有时,当鼠标捕捉不够时,这种技术很方便:

Window.GetWindow(this).AddHandler 
(
    UIElement.MouseDownEvent, 
    (MouseButtonEventHandler)TextBox_PreviewMouseDown, 
    true 
); 
相关问题