WPF和MVVM:如何自动将焦点移动到下一个控件

12

我有一个带有两个文本框的小WPF窗口,TabIndex为0和1,并且当我按Enter键时,我希望自动从第一个文本框移动焦点到第二个。

注意:这篇文章不是重复的。在这里,我不使用事件处理程序的传统方法,而是使用MVVM模式,正如您所知,代码后台不允许在视图中使用。

我找到了一个解决方案,但我不知道它是否符合MVVM原则。以下是代码:

视图

<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>

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);
     }
    }
}    

我不知道在ViewModel中是否允许使用"Control"类。

2个回答

18

由于您正在使用MVVM,因此可以使用Behavior来实现此功能:

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;
  }

}
在您的 XAML 中:
<TextBox>
  <i:Interaction.Behaviors>
    <wpfTest:TabOnEnterBehavior />
  </i:Interaction.Behaviors>
</TextBox>

感谢您提供的好解决方案。我在项目中有很多文本框,我能否将其设置为默认文本框,而不必为所有文本框添加行为? - ar.gorgin
@ar.gorgin:看一下这个SO问题:https://dev59.com/IHI-5IYBdhLWcg3w1sXi - Julien Poulin
非常好。现在我需要一种方法使右和左起作用,但仅当位于文本的结尾或开头时。 - Sentry
我正在使用一个keydown icommand将一个新项目添加到列表中。一旦我这样做,我希望新项目成为活动(选定)项目。我认为我可以将keyeventargs传递给我的委托命令,但是如果我使用这种方法是否有一种先执行我的icommand然后再触发行为的方法呢? - Paul Gibson

3
首先,在你的TextBox中添加一个PreviewKeyDown事件:
<TextBox PreviewKeyDown="OnTextBoxKeyDown" />

然后创建一个TraversalRequest来将焦点移动到下一个项目:
private void OnTextBoxKeyDown(object sender, KeyEventArgs e)
{
 if (e.Key == Key.Return)
 {
   TraversalRequest request = new TraversalRequest(FocusNavigationDirection.Next);
   MoveFocus(request);
 }
}

编辑

或者,如果您喜欢使用命令,则可以在您的TextBox中设置一个KeyBinding

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

在您的ICommandsExecute逻辑中,基本上与上述内容大致相同。

另外,这里有一个相同的问题:如何在WPF应用程序中模拟按Tab键时按下Return键?


2
我遵循MVVM模式。我认为不允许使用事件。 - Habib Gherairi
实际上,我使用MVVM Light提供的EventToCommand将KeyDown事件转换为命令。但我的问题是如何在不使用EventHandler的情况下将焦点移动到下一个文本框。 - Habib Gherairi
10
MVVM的主要目的是分离视图和逻辑,而不是极端地禁止事件。在这里,你试图移动焦点,这是一个纯粹的用户界面操作,为什么要将其放在除UI(无论是xaml还是xaml.cs)之外的任何地方?请记住:MVVM并不意味着“没有代码后台”或“没有事件”,而是指“没有业务逻辑在UI层”。在UI层完全可以使用事件 :) - Damascus
1
请查看 @Julien Poulin 提供的附加行为解决方案。它简洁明了并且遵循MVVM原则。 - Habib Gherairi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接