11

I have a little WPF Window with 2 TextBoxes Having Ordered TabIndex 0,1 and i want to move focus automatically from the first TextBox to the second when i press Enter Key. I Use MVVM Light.

Remark : This post is not duplicated. here I do not use Classic approach with event Handler but MVVM Pattern and as you know Code Behind is not allowed in view.

I Found a solution but I don't know if it respect MVVM Principle. Here the code :

The View

<Window x:Class="WpfMMVLight2.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:cmd ="http://www.galasoft.ch/mvvmlight"
    Title="MainWindow" Height="350" Width="525"
    DataContext="{Binding MainViewModel}">
    <Grid FocusManager.FocusedElement="{Binding ElementName=tb1}">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="70"/>
    </Grid.ColumnDefinitions>

    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>

    <TextBlock Grid.Column="0" Text="Text 1" VerticalAlignment="Center"/>
    <TextBox x:Name="tb1" Grid.Column="1" VerticalAlignment="Center" Margin="5">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="KeyDown">
                <cmd:EventToCommand PassEventArgsToCommand="True"
                Command ="{Binding KeyDownCommand, Mode=OneWay}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </TextBox>


     <TextBlock Grid.Column="0" Grid.Row="1" Text="Text 2" VerticalAlignment="Center"/>
     <TextBox x:Name="tb2" Grid.Column="1" Grid.Row="1" VerticalAlignment="Center" Margin="5">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="KeyDown">
                <cmd:EventToCommand PassEventArgsToCommand="True"
            Command ="{Binding KeyDownCommand, Mode=OneWay}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </TextBox>

</Grid>

The ViewModel :

 private ICommand _keydownCommand;
 public ICommand KeyDownCommand
    {
       get
        {
            if (_keydownCommand== null)
                _keydownCommand= new DelegateCommand<KeyEventArgs>(KeyDownCommandExecute);
            return _keydownCommand;
        }



    }


    private void KeyDownCommandExecute(KeyEventArgs e)
    {
        if (e != null && e.Key == Key.Enter)
        {
      TraversalRequest request = new TraversalRequest(FocusNavigationDirection.Next);
      request.Wrapped = true;
      ((Control)e.Source).MoveFocus(request);
     }
    }
}    

I don't know if use of "Control" Class in ViewModel Is allowed or not

Sentry
  • 4,102
  • 2
  • 30
  • 38
Habib Gherairi
  • 261
  • 1
  • 5
  • 16

2 Answers2

17

As you are using MVVM, you can use a Behavior for this:

public class TabOnEnterBehavior : Behavior<TextBox>
{

  protected override void OnAttached()
  {
    AssociatedObject.PreviewKeyDown += AssociatedObject_PreviewKeyDown;
  }

  private void AssociatedObject_PreviewKeyDown(object sender, KeyEventArgs e)
  {
    if (e.Key == Key.Enter)
    {
      var request = new TraversalRequest(FocusNavigationDirection.Next);
      request.Wrapped = true;
      AssociatedObject.MoveFocus(request);
    }
  }

  protected override void OnDetaching()
  {
    AssociatedObject.PreviewKeyDown -= AssociatedObject_PreviewKeyDown;
  }

}

In your xaml:

<TextBox>
  <i:Interaction.Behaviors>
    <wpfTest:TabOnEnterBehavior />
  </i:Interaction.Behaviors>
</TextBox>
Julien Poulin
  • 12,737
  • 10
  • 51
  • 76
  • Thanks for good solution. I have very textbox on project, can i set this for default TextBox and don't add behaviors for all textbox? – ar.gorgin Jul 06 '15 at 08:24
  • @ar.gorgin: take a look a this SO question: http://stackoverflow.com/questions/1647815/how-to-add-a-blend-behavior-in-a-style-setter – Julien Poulin Jul 06 '15 at 09:44
  • Very nice. Now I need a way to make right and left work but only when at the end or beginning of the text. – Sentry Nov 09 '16 at 19:22
  • I am using a keydown icommand to add a new item to the list. Once I do that I want the new item to be the active (selected) item. I think I can just pass the keyeventargs to my delegate command, but is there a way to first execute my icommand, and then have the behavior trigger if I used this approach? – Paul Gibson Aug 22 '18 at 04:48
3

Add a PreviewKeyDown event to your TextBox first:

<TextBox PreviewKeyDown="OnTextBoxKeyDown" />

Then create a TraversalRequest to move focus to the next item:

private void OnTextBoxKeyDown(object sender, KeyEventArgs e)
{
 if (e.Key == Key.Return)
 {
   TraversalRequest request = new TraversalRequest(FocusNavigationDirection.Next);
   MoveFocus(request);
 }
}

EDIT

Alternatively, if you prefer using a Command, you can just set a KeyBinding in your TextBox :

        <TextBox.InputBindings>
            <KeyBinding Key="Enter" Command="{Binding YourCommand}" />
        </TextBox.InputBindings>

And just put pretty much the same thing as above in your ICommandsExecute` logic.

Also, same question here: How do I simulate a Tab key press when Return is pressed in a WPF application?

Community
  • 1
  • 1
Damascus
  • 6,553
  • 5
  • 39
  • 53
  • 2
    I follow MVVM pattern. I Think no events allowed. – Habib Gherairi Apr 11 '14 at 09:50
  • Actually i use MVVM Light that Offer EventToCommand to convert Keydown Event to Command. But my problem is how to move Focus to Next TextBox without using EventHandler. – Habib Gherairi Apr 11 '14 at 09:51
  • 10
    Again, MVVM is about separating view from logic. It's not about extremist "no events allowed". You are trying here to move focus, which is a pure UI action, why would you put it anywhere but in the UI (may it be xaml or xaml.cs) ? Make sure to remember this first: MVVM is not about "no code-behind" or "no events", it's about "no business in UI logic". You are completely entitled to use events there :) – Damascus Apr 11 '14 at 11:54
  • 1
    Please take a look at the solution above from @Julien Poulin with Attached Behavior. It is clean and it respects MVVM principle. – Habib Gherairi Apr 11 '14 at 13:42