views:

31

answers:

2

Suppose I have usercontrol with textbox, combobox, button,... inside this control.

Button1 is bound to a ICommand in view model.

My request is: when user hit Enter key in any field, like any textbox, combobox, it will fire Button1 Click event, so that ICommand will be called.

How to implement this?

A: 

Add a dependency property to the UserControl:-

public ICommand EnterKeyCommand
{
    get { return GetValue(EnterKeyCommandProperty) as ICommand; }
    set { SetValue(EnterKeyCommandProperty, value); }
}

public static readonly DependencyProperty EnterKeyCommandProperty =
        DependencyProperty.Register(
                "EnterKeyCommand",
                typeof(ICommand),
                typeof(MyControl),
                null);

Attach a handler for the Keyup event on the UserControl using the AddHandler method:-

void MyControl()
{

    InitializeComponent();
    this.AddHandler(UIElement.KeyUpEvent, new KeyEventHandler(UserControl_KeyUp), true);  //Note that last parameter important
}

void UserControl_KeyUp(object sender, KeyEventArgs e)
{
   if (e.Key == Key.Enter && EnterKeyCommand != null && EnterKeyCommand.CanExecute(null))
   {
       EnterKeyCommand.Execute(null);
   }

}

Note the point here is that the use of AddHandler allows you to intercept an event that has already been handled.

Also note that this is simplified for clarity. In reality you would also want to implement another dependency property for the Command parameter and pass that to CanExecute and Execute instead of null. You would also need to detect whether the OriginalSource is a TextBox that has AcceptsReturn set to true.

AnthonyWJones
Thank you. Try your solution and got two errors:Error 2 Argument 2: cannot convert from 'method group' to 'System.Delegate'Error 1 The best overloaded method match for 'System.Windows.UIElement.AddHandler(System.Windows.RoutedEvent, System.Delegate, bool)' has some invalid arguments. Another question: where to fire up Button1 Click event in your solution?
KentZhou
@KentZhou: Oops the needed to explicitly wrap the `UserControl_KeyUp` in `KeyEventHandler`, the normal C# syntatic sugar doesn't work in this case since `AddHandler` only takes `delegate` for this parameter.
AnthonyWJones
@KentZhou: You don't "fire up Button1 Click". The whole point here is to wire both the Button1 Command property and this new EnterKeyCommand to the same ViewModel Command property that you want to invoke. That's one of the reasons that the commanding paradigm exists in first place. In fact one reason you might want to also use a Command parameter is that you can set a different parameter on the Usercontrol than you do on the button. That way you can inform the command on the view model the manner of invocation, by enter key or button click.
AnthonyWJones
A: 

I created simple behaviour for this kind of situation

<TextBox Grid.Row="2" x:Name="Tags">
    <i:Interaction.Behaviors>
       <this:KeyEnterCommand Command="{Binding AddTagsCommand}" CommandParameter="{Binding ElementName=Tags}" />
    </i:Interaction.Behaviors>
</TextBox>

behaviour code:

public class KeyEnterCommand : Behavior<Control>
{
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.KeyDown += KeyDown;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.KeyDown -= KeyDown;
    }

    void KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
    {
        if (e.Key == System.Windows.Input.Key.Enter && Command != null)
        {
            Command.Execute(CommandParameter);
        }
    }

    #region Command (DependencyProperty)

    /// <summary>
    /// Command
    /// </summary>
    public ICommand Command
    {
        get { return (ICommand)GetValue(CommandProperty); }
        set { SetValue(CommandProperty, value); }
    }
    public static readonly DependencyProperty CommandProperty =
        DependencyProperty.Register("Command", typeof(ICommand), typeof(KeyEnterCommand),
            new PropertyMetadata(null));

    #endregion

    #region CommandParameter (DependencyProperty)

    /// <summary>
    /// CommandParameter
    /// </summary>
    public object CommandParameter
    {
        get { return (object)GetValue(CommandParameterProperty); }
        set { SetValue(CommandParameterProperty, value); }
    }
    public static readonly DependencyProperty CommandParameterProperty =
        DependencyProperty.Register("CommandParameter", typeof(object), typeof(KeyEnterCommand),
            new PropertyMetadata(null));


    #endregion
}
Denis
Using a Behaviour nicely packages up the functionality and transports it to any control via Xaml. The downside is you need add the appropriate SDK dlls to the download (although IMO thats well worth it). Note that your code won't work where the enter key has be handled by a child control, using `AddHandler` can resolve that.
AnthonyWJones
Well, if enter key was handled then maybe it is the right thing to do, not to issue command. Or else you could run into some unexpected behaviours.
Denis