禁用WPF按钮,但仍然吞下点击事件
我在我的应用程序中绑定了一个命令按钮。此按钮位于另一个对鼠标点击作出反应的控件内。当按钮被启用时,我得到了我期望的行为 - 点击按钮并触发命令,点击按钮外部但在容器控件内部,而是触发。禁用WPF按钮,但仍然吞下点击事件
不幸的是,当按钮被禁用时(通过命令的CanExecute方法),按钮上的点击冒泡到容器控件。我不想要这个,我想要点击被吞噬 - 既不触发命令也不冒泡。
我试图创建一个新的类从按钮,但没有下列方法继承甚至似乎获得上被禁用按钮称为解决此问题:
- OnPreviewMouseDown
- OnPreviewMouseUp
- OnPreviewMouseLeftButtonDown
- OnPreviewMouseLeftButtonUp
- OnMouseDown
- OnMouseUp
- OnMouseLeftButtonDown在
- OnMouseLeftButtonUp
- 的OnClick
由WPF完全忽略禁用的控制路由事件系统?如果是这样的话,无论如何,我可以得到我期待的行为?
RCGoforth的回答让我90%的方式出现,但解决的办法不是把一个长方形背后按钮,因为冒泡事件发生在兄弟姐妹不在的地方。最后,我周围的按钮,一个ContentControl中,这吞噬的情况下,才能够进一步上升(因为矩形的不能生孩子):
<ContentControl MouseDown="ContentControl_MouseDown">
<Button Content="Click Test"
Padding="2"
Command="{Binding TestCommand}"/>
</ContentControl>
在后面的代码:
private void ContentControl_MouseDown(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
}
或者完全在XAML中完成此操作(并增加代码的黑客级别...)
<Button>
<Button.Template>
<ControlTemplate>
<Button Content="Click Test"
Command="{Binding TestCommand}"/>
</ControlTemplate>
</Button.Template>
</Button>
它不是很干净,但是你可以在你的按钮后面放一个透明的矩形来吞噬点击事件。
你想要做的是使用the overload that takes the handledEventsToo
parameter注册路由事件处理程序,并为该参数指定值为true。这样,无论按钮是否实际处理事件,外部处理程序都将接收事件。这将是这个样子:
this.AddHandler(Mouse.MouseUpEvent, this.MyMouseUpHandler, true);
然后在你的处理器,你可以随时检查被点击什么,它是否处理等通过MouseButtonEventArgs
你是右手。例如,要检查是否另一个控制实际处理该事件已经可以这样做:
if(!args.Handled)
{
// handle it here instead
}
更清洁,更可重用的解决方案将作为附加属性实现此功能。
使用服务/动作模式:
namespace Control.Services
{
public class UIElementService
{
public static readonly DependencyProperty HandleMouseEventsProperty = DependencyProperty.RegisterAttached("HandleMouseEvents",
typeof(bool), typeof(UIElementService), new FrameworkPropertyMetadata(false, UIElementService.HandleMouseEventsPropertyChanged));
static void HandleMouseEventsPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = sender as FrameworkElement;
if (element == null)
return;
new HandleMouseEventsAction(element);
}
public static bool GetHandleMouseEvents(FrameworkElement target)
{
return (bool)target.GetValue(HandleMouseEventsProperty);
}
public static void SetHandleMouseEvents(FrameworkElement target, bool value)
{
target.SetValue(HandleMouseEventsProperty, value);
}
class HandleMouseEventsAction
{
UIElement m_Target;
MouseButtonEventHandler m_Handler;
internal HandleMouseEventsAction(FrameworkElement source)
{
m_Source = source;
m_Handler = new MouseButtonEventHandler(PreviewMouseLeftButtonUp);
m_Source.Loaded += OnSource_Loaded;
m_Source.Unloaded += OnSource_Unloaded;
}
void OnSource_Loaded(object sender, RoutedEventArgs e)
{
m_Source.AddHandler(Mouse.PreviewMouseUpEvent, m_Handler, true);
}
void OnSource_Unloaded(object sender, RoutedEventArgs e)
{
m_Source.RemoveHandler(Mouse.PreviewMouseUpEvent, m_Handler);
}
void PreviewMouseLeftUIElementUp(object sender, MouseUIElementEventArgs e)
{
e.Handled = true;
}
}
}
}
即可使用,导入命名空间。
<Button sv:UIElementService.HandleMouseEvents="True" />
或
<ContentControl sv:UIElementService.HandleMouseEvents="True">
<Button Content="Click Test" Padding="2" Command="{Binding TestCommand}"/>
</ContentControl>
我没有测试过这一点(目前没时间)。我相信即使禁用了该动作仍然会获得鼠标事件。
HTH,
丹尼斯
这不会编译,如果你只是重构足够的编译它不起作用。 – 2017-01-26 18:07:25
@KellyElton,它在答案中说:“我没有测试过(现在没有时间)”。你得到了什么错误?也许我可以帮忙。 – Dennis 2017-01-30 00:10:33
是的,我知道你没有测试它。这些错误是编译时错误......一旦你修复了这些错误并启动并运行,更正后的代码并不能解决问题。 – 2017-01-30 21:18:37
我创建了一个行为(即实际工作),其插入Button
与其父之间的ContentPresenter
。当Button
被禁用时,ContentPresenter
吞下鼠标点击。该行为使用几种基于this answer的代码的扩展方法。使用它很简单:
<Button ...>
<i:Interaction.Behaviors>
<behaviors:SwallowMouseClicksWhenDisabled />
</i:Interaction.Behaviors>
</Button>
这里是源:
// the behavior (could also be an attached behavior)
public class SwallowMouseClicksWhenDisabled : Behavior<FrameworkElement>
{
protected override void OnAttached()
{
var oldParent = AssociatedObject.Parent;
oldParent.RemoveChild(AssociatedObject);
var newParent = new ContentPresenter { Content = AssociatedObject };
oldParent.AddChild(newParent);
newParent.PreviewMouseDown += OnPreviewMouseEvent;
newParent.PreviewMouseUp += OnPreviewMouseEvent;
}
private void OnPreviewMouseEvent(object sender, MouseButtonEventArgs e)
{
e.Handled = AssociatedObject.IsEnabled == false;
}
}
// the extension methods
public static class DependencyObjectExtensions
{
public static void AddChild(this DependencyObject parent, UIElement child)
{
var panel = parent as Panel;
if (panel != null)
{
panel.Children.Add(child);
return;
}
var decorator = parent as Decorator;
if (decorator != null)
{
decorator.Child = child;
return;
}
var contentPresenter = parent as ContentPresenter;
if (contentPresenter != null)
{
contentPresenter.Content = child;
return;
}
var contentControl = parent as ContentControl;
if (contentControl != null)
{
contentControl.Content = child;
return;
}
// maybe more
}
public static void RemoveChild(this DependencyObject parent, UIElement child)
{
var panel = parent as Panel;
if (panel != null)
{
panel.Children.Remove(child);
return;
}
var decorator = parent as Decorator;
if (decorator != null)
{
if (Equals(decorator.Child, child))
{
decorator.Child = null;
}
return;
}
var contentPresenter = parent as ContentPresenter;
if (contentPresenter != null)
{
if (Equals(contentPresenter.Content, child))
{
contentPresenter.Content = null;
}
return;
}
var contentControl = parent as ContentControl;
if (contentControl != null)
{
if (Equals(contentControl.Content, child))
{
contentControl.Content = null;
}
return;
}
// maybe more
}
}
谢谢,但我只是在寻找到这一点,我想你可能误解了我的问题。我不希望外部控件总是*接收事件,我希望它只在点击位于按钮外部时才接收事件,即使该按钮被禁用。 – 2009-11-06 16:01:59
你说得对,我误解了你的意图。我会修改。 – 2009-11-06 18:08:24