使用MVVM在WPF中设置焦点

6

我有一个包含多个文本框的网格(Grid)。根据用户可能采取的操作,焦点应更改为其中一个文本框。我的当前解决方案使用ViewModel中的字符串属性和xaml中的数据触发器来更改焦点。它很好用,但似乎是一种比较绕弯子的方式来实现这一点,所以我想知道是否有更简洁的方法?

    <Grid.Style>
        <Style TargetType="Grid">
            <Style.Triggers>
                <DataTrigger Binding="{Binding FocusedItem}" Value="number">
                    <Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=number}"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding FocusedItem}" Value="name">
                    <Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=name}"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding FocusedItem}" Value="id">
                    <Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=id}"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Grid.Style>

您可以看到,属性的值和元素的名称相同,因此我希望在单个触发器中完成此操作,而不是针对每个元素设置一个触发器。

也许有人能想出更清晰的方法?

提前感谢您。


我可以问一下你为什么这样设置焦点吗?因为用户也可以通过 TabIndex 进行 Tab 键切换,所以你只需要设置一次焦点。 - WiiMaxx
1
只要这个解决方案对你有效,你就应该采纳它。 - blindmeis
2
个人认为,焦点是一个特定于UI的概念,因此我会将所有的焦点处理放在视图的代码后台,而不是我的ViewModel中(除非焦点在业务逻辑中具有某些特定的含义)。 - Rachel
1
@blindmeis 如果我从不尝试改进,我就不会学到任何东西,因此永远无法变得更好。 - Farawin
作为一个例子,如果我想将焦点设置在验证失败的控件上,我可能会将单击事件附加到按钮,找到第一个具有验证错误的控件并将焦点设置在它上面。尽管理想情况下,如果存在验证错误,我甚至不会启用保存按钮 :) - Rachel
显示剩余5条评论
1个回答

4

在我的一个项目中,我处理焦点设置的方式是使用一个焦点扩展(很抱歉我不记得原始帖子来自哪里)。

    public static class FocusExtension
    {
        public static bool GetIsFocused(DependencyObject obj)
        {
           return (bool)obj.GetValue(IsFocusedProperty);
        }


        public static void SetIsFocused(DependencyObject obj, bool value)
        {
            obj.SetValue(IsFocusedProperty, value);
        }


        public static readonly DependencyProperty IsFocusedProperty =
                DependencyProperty.RegisterAttached(
                 "IsFocused", typeof(bool), typeof(FocusExtension),
                 new UIPropertyMetadata(false, OnIsFocusedPropertyChanged));


        private static void OnIsFocusedPropertyChanged(DependencyObject d,
                DependencyPropertyChangedEventArgs e)
        {
            var uie = (UIElement)d;
            if ((bool)e.NewValue)
            {
                uie.Focus();
            }
        }
    }

然后在 XAML 文件中,我将它作为一个依赖属性使用:

<TextBox Uid="TB1" FontSize="13" localExtensions:FocusExtension.IsFocused="{Binding Path=TB1Focus}" Height="24" HorizontalAlignment="Left" Margin="113,56,0,0" Name="TB_UserName" VerticalAlignment="Top" Width="165" Text="{Binding Path=TB1Value, UpdateSourceTrigger=PropertyChanged}" />

然后您可以使用绑定来设置焦点。


1
谢谢。我曾考虑过使用这样的解决方案,但最终决定不采用,因为它会使我的虚拟机与实际的用户界面耦合得太紧密了。我开始认为,代码后台可能是更好的选择。 - Farawin
你的文本框也需要设置Focusable="True",否则这个功能将无法使用。 - krilovich
1
我相信原始帖子是https://dev59.com/m3M_5IYBdhLWcg3wfTO7 - Richard Mitchell

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