I've made a custom button to bind a command to a (custom, routed) IsPressedChanged
event so that the command is executed both when the button is pressed AND when it is released:
<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>
With the custom button implementation:
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));
}
}
This works perfectly as it should.
And now comes the Problem:
When I try to propagate the value of the IsPressed
property to the command like so:
<i:InvokeCommandAction Command="{Binding Path=SomeCommand}"
CommandParameter="{Binding ElementName=MyButton, Path=IsPressed}"/>
the propagated value will (seemingly) allways be the old value of IsPressed
. When I press the button, the command called with the parameter beeing false, when I release the button the parameter is true. But when I check the value of IsPressed
inside the event handler CustomButton.OnIsPressedChanged()
, it represents the new value as expected.
My Question is: How should I propagate the value of IsPressed
to get the correct value? Is it guaranteed that the command will always be called with the old value? In that case I could simply invert the value but that seems a bit shady to me and I really would not want to do this unless I know it will allways yield the correct result.
CodePudding user response:
You can pass the DependencyPropertyChangedEventArgs
as a parameter of the RoutedEventArgs
that is raised:
protected override void OnIsPressedChanged(DependencyPropertyChangedEventArgs e)
{
base.OnIsPressedChanged(e);
// you may want to pass e.NewValue here for simplicity.
RaiseEvent(new RoutedEventArgs(CustomIsPressedChangedEvent, e));
}
Then ask the InvokeCommandAction
to pass it to the command:
<i:InvokeCommandAction Command="{Binding Path=SomeCommand}"
PassEventArgsToCommand="True" />
And then, in the command you just need to cast the passed object to retrieve the new value of 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--;
}
Working demo here.
CodePudding user response:
For reasons of convenience (for the usage of your control), you should not implement a parallel command. Instead modify the existing behavior.
The button has a Button.ClickMode
property. The button's internal filtering of this property makes the Button
execute only once on either ClickMode.Press
, ClickMode.Release
or ClickMode.Hover
.
We need to bypass this filtering to execute the Button.Command
and the Button.Click
event on both MouseLeftButtonDown
and MouseLeftButtonUp
(to implement ClickMode.Press
and ClickMode.Release
) as well as MouseEnter
and MouseLeave
(to support 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 onm ouseLeftButtonDown(MouseButtonEventArgs e)
{
if (this.IsDoubleTriggerEnabled
&& this.ClickMode != ClickMode.Hover)
{
base.OnClick();
}
else
{
base.OnMouseLeftButtonDown(e);
}
}
protected override void onm ouseLeftButtonUp(MouseButtonEventArgs e)
{
if (this.IsDoubleTriggerEnabled
&& this.ClickMode != ClickMode.Hover)
{
base.OnClick();
}
else
{
base.OnMouseLeftButtonUp(e);
}
}
protected override void onm ouseEnter(MouseEventArgs e)
{
if (this.IsDoubleTriggerEnabled
&& this.ClickMode == ClickMode.Hover)
{
base.OnClick();
}
else
{
base.OnMouseEnter(e);
}
}
protected override void onm ouseLeave(MouseEventArgs e)
{
if (this.IsDoubleTriggerEnabled
&& this.ClickMode == ClickMode.Hover)
{
base.OnClick();
}
else
{
base.OnMouseLeave(e);
}
}
}