我如何正确地将 IsPressed 属性 绑定到我的命令参数?
How do i properly bind the IsPressed property to my command parameter?
我制作了一个自定义按钮,将命令绑定到(自定义,路由)IsPressedChanged
事件,以便在按下按钮和释放按钮时执行命令:
<local:CustomButton xmlns:i="http://schemas.microsoft.com/xaml/behaviors" x:Name="MyButton">
<i:Interaction.Triggers>
<i:EventTrigger EventName="CustomIsPressedChanged">
<i:InvokeCommandAction Command="{Binding Path=SomeCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</local:CustomButton>
使用自定义按钮实现:
public partial class CustomButton : Button
{
/* Register a custom routed event using the bubble routing strategy. */
public static readonly RoutedEvent CustomIsPressedChangedEvent = EventManager.RegisterRoutedEvent(
name: "CustomIsPressedChanged",
routingStrategy: RoutingStrategy.Bubble,
handlerType: typeof(RoutedEventHandler),
ownerType: typeof(CustomButton));
/* Provide CLR accessors for assigning an event handler. */
public event RoutedEventHandler CustomIsPressedChanged
{
add { AddHandler(CustomIsPressedChangedEvent, value); }
remove { RemoveHandler(CustomIsPressedChangedEvent, value); }
}
public CustomButton() { InitializeComponent(); }
/* Custom Event handling of the IsPressedChanged event */
protected override void OnIsPressedChanged(System.Windows.DependencyPropertyChangedEventArgs e)
{
/* Call the base class OnIsPressedChanged() method so IsPressedChanged event subscribers are notified. */
base.OnIsPressedChanged(e);
/* Raise custom event */
RaiseEvent(new RoutedEventArgs(routedEvent: CustomIsPressedChangedEvent));
}
}
这完全可以正常工作。
问题来了:
当我尝试将 IsPressed
属性 的值传播到命令时,如下所示:
<i:InvokeCommandAction Command="{Binding Path=SomeCommand}"
CommandParameter="{Binding ElementName=MyButton, Path=IsPressed}"/>
传播的值(看起来)总是 IsPressed
的旧值。当我按下按钮时,使用参数 beeing false 调用的命令,当我释放按钮时参数为真。 但是 当我在事件处理程序 CustomButton.OnIsPressedChanged()
中检查 IsPressed
的值时,它代表了预期的新值。
我的问题是: 我应该如何传播 IsPressed
的值以获得正确的值?是否保证总是使用旧值调用该命令?在那种情况下,我可以简单地反转值,但这对我来说似乎有点阴暗,我真的不想这样做,除非我知道它总是会产生正确的结果。
您可以将 DependencyPropertyChangedEventArgs
作为引发的 RoutedEventArgs
的参数传递:
protected override void OnIsPressedChanged(DependencyPropertyChangedEventArgs e)
{
base.OnIsPressedChanged(e);
// you may want to pass e.NewValue here for simplicity.
RaiseEvent(new RoutedEventArgs(CustomIsPressedChangedEvent, e));
}
然后让InvokeCommandAction
传给命令:
<i:InvokeCommandAction Command="{Binding Path=SomeCommand}"
PassEventArgsToCommand="True" />
然后,在命令中你只是需要转换传递的对象来检索IsPressed
的新值:
SomeCommand = new ActionCommand(SomeCommandAction);
//...
private void SomeCommandAction(object o)
{
if (o is not RoutedEventArgs routedEventArgs)
return;
if (routedEventArgs.OriginalSource is not DependencyPropertyChangedEventArgs eventArgs)
return;
if (eventArgs.NewValue is true)
Count++;
if (eventArgs.NewValue is false)
Count--;
}
工作演示 here。
为了方便起见(为了您的控件的使用),您不应执行并行命令。而是修改现有行为。
按钮有一个 Button.ClickMode
属性。按钮对此 属性 的内部过滤使得 Button
仅在 ClickMode.Press
、ClickMode.Release
或 ClickMode.Hover
.
上执行一次
我们需要绕过此过滤以在 MouseLeftButtonDown
和 MouseLeftButtonUp
上执行 Button.Command
和 Button.Click
事件(以实现 ClickMode.Press
和 ClickMode.Release
)以及 MouseEnter
和 MouseLeave
(支持 ClickMode.Hover
):
DoubleTriggerButton.cs
public class DoubleTriggerButton : Button
{
public bool IsDoubleTriggerEnabled
{
get => (bool)GetValue(IsDoubleTriggerEnabledProperty);
set => SetValue(IsDoubleTriggerEnabledProperty, value);
}
public static readonly DependencyProperty IsDoubleTriggerEnabledProperty = DependencyProperty.Register(
"IsDoubleTriggerEnabled",
typeof(bool),
typeof(DoubleTriggerButton),
new PropertyMetadata(true));
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
if (this.IsDoubleTriggerEnabled
&& this.ClickMode != ClickMode.Hover)
{
base.OnClick();
}
else
{
base.OnMouseLeftButtonDown(e);
}
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
if (this.IsDoubleTriggerEnabled
&& this.ClickMode != ClickMode.Hover)
{
base.OnClick();
}
else
{
base.OnMouseLeftButtonUp(e);
}
}
protected override void OnMouseEnter(MouseEventArgs e)
{
if (this.IsDoubleTriggerEnabled
&& this.ClickMode == ClickMode.Hover)
{
base.OnClick();
}
else
{
base.OnMouseEnter(e);
}
}
protected override void OnMouseLeave(MouseEventArgs e)
{
if (this.IsDoubleTriggerEnabled
&& this.ClickMode == ClickMode.Hover)
{
base.OnClick();
}
else
{
base.OnMouseLeave(e);
}
}
}
我找到了另一个解决方案,与我的原始代码相比几乎不需要更改:
编辑:正如 BionicCode 在评论中指出的那样,由于多种原因,这不是一个好的设计。
通过向替换 IsPressed
属性 的 CustmButton
添加依赖项 属性,可以在 OnIsPressedChanged
事件处理程序中分配正确的值.绑定到新的 IsPressed
属性 然后像我预期的那样工作原来的 属性 工作:
public new static readonly DependencyProperty IsPressedProperty =
DependencyProperty.Register("IsPressed", typeof(bool), typeof(CustomButton),
new PropertyMetadata(false));
public new bool IsPressed
{
get { return (bool)GetValue(IsPressedProperty); }
set { SetValue(IsPressedProperty, value); }
}
protected override void OnIsPressedChanged(System.Windows.DependencyPropertyChangedEventArgs e)
{
/* Call the base class OnIsPressedChanged() method so IsPressedChanged event subscribers are notified. */
base.OnIsPressedChanged(e);
/* Forward the value of the base.IsPressed property to the custom IsPressed property */
IsPressed = (bool)e.NewValue;
/* Raise event */
RaiseCustomRoutedEvent(new RoutedEventArgs(routedEvent: CustomIsPressedChangedEvent));
}
现在可以将命令参数与正在转发的新值绑定:
<local:CustomButton xmlns:i="http://schemas.microsoft.com/xaml/behaviors" x:Name="MyButton">
<i:Interaction.Triggers>
<i:EventTrigger EventName="CustomIsPressedChanged">
<i:InvokeCommandAction Command="{Binding Path=SomeCommand}"
CommandParameter="{Binding ElementName=MyButton, Path=IsPressed}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</local:CustomButton>
我制作了一个自定义按钮,将命令绑定到(自定义,路由)IsPressedChanged
事件,以便在按下按钮和释放按钮时执行命令:
<local:CustomButton xmlns:i="http://schemas.microsoft.com/xaml/behaviors" x:Name="MyButton">
<i:Interaction.Triggers>
<i:EventTrigger EventName="CustomIsPressedChanged">
<i:InvokeCommandAction Command="{Binding Path=SomeCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</local:CustomButton>
使用自定义按钮实现:
public partial class CustomButton : Button
{
/* Register a custom routed event using the bubble routing strategy. */
public static readonly RoutedEvent CustomIsPressedChangedEvent = EventManager.RegisterRoutedEvent(
name: "CustomIsPressedChanged",
routingStrategy: RoutingStrategy.Bubble,
handlerType: typeof(RoutedEventHandler),
ownerType: typeof(CustomButton));
/* Provide CLR accessors for assigning an event handler. */
public event RoutedEventHandler CustomIsPressedChanged
{
add { AddHandler(CustomIsPressedChangedEvent, value); }
remove { RemoveHandler(CustomIsPressedChangedEvent, value); }
}
public CustomButton() { InitializeComponent(); }
/* Custom Event handling of the IsPressedChanged event */
protected override void OnIsPressedChanged(System.Windows.DependencyPropertyChangedEventArgs e)
{
/* Call the base class OnIsPressedChanged() method so IsPressedChanged event subscribers are notified. */
base.OnIsPressedChanged(e);
/* Raise custom event */
RaiseEvent(new RoutedEventArgs(routedEvent: CustomIsPressedChangedEvent));
}
}
这完全可以正常工作。
问题来了:
当我尝试将 IsPressed
属性 的值传播到命令时,如下所示:
<i:InvokeCommandAction Command="{Binding Path=SomeCommand}"
CommandParameter="{Binding ElementName=MyButton, Path=IsPressed}"/>
传播的值(看起来)总是 IsPressed
的旧值。当我按下按钮时,使用参数 beeing false 调用的命令,当我释放按钮时参数为真。 但是 当我在事件处理程序 CustomButton.OnIsPressedChanged()
中检查 IsPressed
的值时,它代表了预期的新值。
我的问题是: 我应该如何传播 IsPressed
的值以获得正确的值?是否保证总是使用旧值调用该命令?在那种情况下,我可以简单地反转值,但这对我来说似乎有点阴暗,我真的不想这样做,除非我知道它总是会产生正确的结果。
您可以将 DependencyPropertyChangedEventArgs
作为引发的 RoutedEventArgs
的参数传递:
protected override void OnIsPressedChanged(DependencyPropertyChangedEventArgs e)
{
base.OnIsPressedChanged(e);
// you may want to pass e.NewValue here for simplicity.
RaiseEvent(new RoutedEventArgs(CustomIsPressedChangedEvent, e));
}
然后让InvokeCommandAction
传给命令:
<i:InvokeCommandAction Command="{Binding Path=SomeCommand}"
PassEventArgsToCommand="True" />
然后,在命令中你只是需要转换传递的对象来检索IsPressed
的新值:
SomeCommand = new ActionCommand(SomeCommandAction);
//...
private void SomeCommandAction(object o)
{
if (o is not RoutedEventArgs routedEventArgs)
return;
if (routedEventArgs.OriginalSource is not DependencyPropertyChangedEventArgs eventArgs)
return;
if (eventArgs.NewValue is true)
Count++;
if (eventArgs.NewValue is false)
Count--;
}
工作演示 here。
为了方便起见(为了您的控件的使用),您不应执行并行命令。而是修改现有行为。
按钮有一个 Button.ClickMode
属性。按钮对此 属性 的内部过滤使得 Button
仅在 ClickMode.Press
、ClickMode.Release
或 ClickMode.Hover
.
上执行一次
我们需要绕过此过滤以在 MouseLeftButtonDown
和 MouseLeftButtonUp
上执行 Button.Command
和 Button.Click
事件(以实现 ClickMode.Press
和 ClickMode.Release
)以及 MouseEnter
和 MouseLeave
(支持 ClickMode.Hover
):
DoubleTriggerButton.cs
public class DoubleTriggerButton : Button
{
public bool IsDoubleTriggerEnabled
{
get => (bool)GetValue(IsDoubleTriggerEnabledProperty);
set => SetValue(IsDoubleTriggerEnabledProperty, value);
}
public static readonly DependencyProperty IsDoubleTriggerEnabledProperty = DependencyProperty.Register(
"IsDoubleTriggerEnabled",
typeof(bool),
typeof(DoubleTriggerButton),
new PropertyMetadata(true));
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
if (this.IsDoubleTriggerEnabled
&& this.ClickMode != ClickMode.Hover)
{
base.OnClick();
}
else
{
base.OnMouseLeftButtonDown(e);
}
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
if (this.IsDoubleTriggerEnabled
&& this.ClickMode != ClickMode.Hover)
{
base.OnClick();
}
else
{
base.OnMouseLeftButtonUp(e);
}
}
protected override void OnMouseEnter(MouseEventArgs e)
{
if (this.IsDoubleTriggerEnabled
&& this.ClickMode == ClickMode.Hover)
{
base.OnClick();
}
else
{
base.OnMouseEnter(e);
}
}
protected override void OnMouseLeave(MouseEventArgs e)
{
if (this.IsDoubleTriggerEnabled
&& this.ClickMode == ClickMode.Hover)
{
base.OnClick();
}
else
{
base.OnMouseLeave(e);
}
}
}
我找到了另一个解决方案,与我的原始代码相比几乎不需要更改:
编辑:正如 BionicCode 在评论中指出的那样,由于多种原因,这不是一个好的设计。
通过向替换 IsPressed
属性 的 CustmButton
添加依赖项 属性,可以在 OnIsPressedChanged
事件处理程序中分配正确的值.绑定到新的 IsPressed
属性 然后像我预期的那样工作原来的 属性 工作:
public new static readonly DependencyProperty IsPressedProperty =
DependencyProperty.Register("IsPressed", typeof(bool), typeof(CustomButton),
new PropertyMetadata(false));
public new bool IsPressed
{
get { return (bool)GetValue(IsPressedProperty); }
set { SetValue(IsPressedProperty, value); }
}
protected override void OnIsPressedChanged(System.Windows.DependencyPropertyChangedEventArgs e)
{
/* Call the base class OnIsPressedChanged() method so IsPressedChanged event subscribers are notified. */
base.OnIsPressedChanged(e);
/* Forward the value of the base.IsPressed property to the custom IsPressed property */
IsPressed = (bool)e.NewValue;
/* Raise event */
RaiseCustomRoutedEvent(new RoutedEventArgs(routedEvent: CustomIsPressedChangedEvent));
}
现在可以将命令参数与正在转发的新值绑定:
<local:CustomButton xmlns:i="http://schemas.microsoft.com/xaml/behaviors" x:Name="MyButton">
<i:Interaction.Triggers>
<i:EventTrigger EventName="CustomIsPressedChanged">
<i:InvokeCommandAction Command="{Binding Path=SomeCommand}"
CommandParameter="{Binding ElementName=MyButton, Path=IsPressed}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</local:CustomButton>