14

I would like to invoke a command using EventTrigger when a particular key is touched (for example, the spacebar key)

Currently I have:

  <i:Interaction.Triggers>
       <i:EventTrigger EventName="KeyDown">
            <i:InvokeCommandAction Command="{Binding DoCommand}" CommandParameter="{BindingText}"/>
       </i:EventTrigger>
  </i:Interaction.Triggers>

Now how can I specify that this should occur only when the KeyDown occurs with the spacebar?

lost_bits1110
  • 2,380
  • 6
  • 33
  • 44

3 Answers3

15

Another approach would be to use KeyBindings and bind them to your Window, UserControl, FrameworkElement, etc. That will not Trigger a button, but say you have a command "MyCommand" that is called from the button, you could invoke the command from InputBindings.

<UserControl.InputBindings>
   <KeyBinding Command="{Binding Path=ApplyCommand}" Key="Enter"/>
   <KeyBinding Command="{Binding Path=NextPage}" Modifiers="Ctrl" Key="Left"/>
</UserControl.InputBindings>

<StackPanel> 
    <Button IsDefault="True" Content="Apply">
        <i:Interaction.Triggers>
           <i:EventTrigger EventName="Click">
               <i:InvokeCommandAction Command="{Binding Path=ApplyCommand}" />                            
           </i:EventTrigger>
        </i:Interaction.Triggers>
    </Button>
 </StackPanel>

You could also bind these KeyBindings to a TextBox.

kalisohn
  • 291
  • 3
  • 6
  • 1
    I prefer this solution as it allows me to keep almost all of my code in the xaml for handling the key interactions. I'm currently working on a TextBlock/TextBox editiable label and doing it this way allowed me to create a new editable style I can apply to any label going forward. Simple and elegant. Thanks Kalisohn!! – James McDuffie Jul 05 '17 at 17:20
  • This worked for me. I was creating triggers for a '+' and '-' symbols. Take my updoot! – tCoe Feb 14 '20 at 20:02
15

You would have to build a custom Trigger to handle that:

public class SpaceKeyDownEventTrigger : EventTrigger {

    public SpaceKeyDownEventTrigger() : base("KeyDown") {
    }

    protected override void OnEvent(EventArgs eventArgs) {
        var e = eventArgs as KeyEventArgs;
        if (e != null && e.Key == Key.Space)
            this.InvokeActions(eventArgs);
    }
}
CodeNaked
  • 40,753
  • 6
  • 122
  • 148
  • thanks for your reply, though I still cannot get it to work. I changed my XAML to use `SpaceKeyDownEventTrigger` (instead of just` EventTrigger`) but `OnEvent` is not entered. Is there anything else I should change in my XAML? Thx again – lost_bits1110 Oct 17 '11 at 20:08
  • @lost_bits1110 - Can you please update your question to include a full example? Are you sure the KeyDown event is firing for the associated control? – CodeNaked Oct 17 '11 at 20:11
  • yep in my original post the event is being fired for the associated control, but was happening for every key down event. I would just like for it to happen on a specified key. I added a custom EventTrigger class as you've posted, and simply changed my XAML to replace EventTrigger with SpakeKeyDownEventTrigger, but it looks like I need something more – lost_bits1110 Oct 17 '11 at 20:23
  • okay it works now - my mistake was I forgot to put the 'override' keyword. Thanks! – lost_bits1110 Oct 17 '11 at 20:48
  • @lost_bits1110 - Ah, yeah I had that in there originally also, but corrected my mistake earlier. – CodeNaked Oct 17 '11 at 20:50
  • Brilliant. I am using this since the conventional means of using InputBindings does not seem to work for the DatePicker control, whenever I want to deal with an "Enter Pressed" event. Using this enables me to work around this apparent limitation. – AndyUK Apr 13 '17 at 11:09
1

I like the idea with a custom trigger but I didn't managed to make it work (some methods were changed or deprecated therefore the showed above definition of the SpaceKeyDownEventTrigger is not compiled now). So, I put here the working version with custom RoutedEvent instead. The SpaceKeyDownEvent is defined in MyControl custom control and is raised from the OnKeyDown method when an unhandled KeyDown attached event reaches MyControl and the key pressed is the spacebar.

public class MyControl : ContentControl
{
    // This constructor is provided automatically if you
    // add a Custom Control (WPF) to your project
    static MyControl()
    {
        DefaultStyleKeyProperty.OverrideMetadata(
            typeof(MyControl),
            new FrameworkPropertyMetadata(typeof(MyControl)));
    }

    // Create a custom routed event by first registering a RoutedEventID
    // This event uses the bubbling routing strategy
    public static readonly RoutedEvent SpaceKeyDownEvent = EventManager.RegisterRoutedEvent(
        "SpaceKeyDown",
        RoutingStrategy.Bubble,
        typeof(RoutedEventHandler),
        typeof(MyControl));

    // Provide CLR accessors for the event
    public event RoutedEventHandler SpaceKeyDown
    {
        add { AddHandler(SpaceKeyDownEvent, value); }
        remove { RemoveHandler(SpaceKeyDownEvent, value); }
    }

    // This method raises the SpaceKeyDown event
    protected virtual void RaiseSpaceKeyDownEvent()
    {
        RoutedEventArgs args = new RoutedEventArgs(SpaceKeyDownEvent);
        RaiseEvent(args);
    }

    // Here KeyDown attached event is customized for the desired key
    protected override void OnKeyDown(KeyEventArgs e)
    {
        base.OnKeyDown(e);

        if (e.Key == Key.Space)
            RaiseSpaceKeyDownEvent();
    }
}

The MyControl could be added to the template of another control, allowing the latter to use EventTrigger with the SpaceKeyDown routed event:

<Style TargetType="{x:Type local:MyControl}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:MyControl}">
                    <Grid>
                        <ContentPresenter/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <!-- Adding MyControl to the TextBox template -->
    <Style TargetType="{x:Type TextBox}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type TextBox}">
                    <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
                        <local:MyControl>
                            <ScrollViewer x:Name="PART_ContentHost" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
                        </local:MyControl>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <EventTrigger RoutedEvent="local:MyControl.SpaceKeyDown">
                <BeginStoryboard>
                    <Storyboard>
                        <ColorAnimation Storyboard.TargetProperty="Foreground.Color"
                                                    From="White" To="Transparent" Duration="0:0:0.066" AutoReverse="True" RepeatBehavior="3x"/>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger>
        </Style.Triggers>
    </Style>
Zubetto
  • 41
  • 5