【问题标题】:WPF MVVM Focus Field on LoadWPF MVVM 负载上的焦点字段
【发布时间】:2010-11-13 18:54:58
【问题描述】:

我有一个视图,它下面有一个 TextBox 和一对 Buttons。当窗口加载时,我希望 TextBox 获得焦点。

如果我不使用 MVVM,我只会在 Loaded 事件中调用 TextBox.Focus()。但是,我的 ViewModel 不知道我的视图,那么如果不将代码放入我的视图的代码隐藏中,我该如何实现呢?

编辑: 阅读答案后,我决定将此代码放在视图 xaml 中

<DockPanel FocusManager.FocusedElement="{Binding ElementName=MessageTextBox}">    
    <TextBox Name="MessageTextBox" Text="{Binding Message}"/>
</DockPanel>

如果这不是初始页面焦点,我可能会推荐 Jon Galloway 的答案,因为它可以从 ViewModel 控制。

【问题讨论】:

    标签: c# wpf mvvm textbox focus


    【解决方案1】:

    在这种情况下,我认为将代码放入视图中很好。将焦点设置到控件会影响用户界面的行为,而不是应用程序的逻辑,因此是视图的责任。

    【讨论】:

    • 我就是这么想的。每次想到向视图添加代码时,我都觉得有点脏。不过在这种情况下,这似乎是有道理的。
    • 我认为一旦我将我的 MVVM 理念从“没有代码背后”更改为“最少​​且相关的代码背后”,我的生活变得轻松多了。我没有听说过 Anderson Imes 提到的附加属性,这听起来像是一个很好的解决方案,可以解决背后的代码给你带来的不良品味,但不要害怕把 合法,非- UI 中的逻辑代码。
    • 我非常同意你的观点。我认为从视图中删除代码是没有意义的。重要的是视图中的代码应该与 UI 非常相关。 VM 中的代码包含与模型有关的状态和其他逻辑。试金石是代码是否可以/应该被测试。视图中的代码无法测试。
    【解决方案2】:

    我认为带有焦点的控件非常“仅视觉”,因此在代码后面不会有任何问题。

    VM 的想法是将逻辑从视图中移出,并提供模型的数据绑定友好版本以供视图绑定。这并不一定意味着所有代码都应该存在于 VM 中,只有逻辑代码和任何与 UI 没有直接关联的代码。

    【讨论】:

      【解决方案3】:

      实际上,焦点不是 UI 问题吗? MVVM 是关于分离关注点 - 属于模型的在模型中,属于视图的在视图中,以及在 ViewModel 中将模型和视图绑定在一起(这当然是过于简单的描述)。

      这意味着 UI 逻辑仍保留在 View 中 - TextBox.Focus() 在我看来是实现这一点的适当方式。

      【讨论】:

        【解决方案4】:
        1. 在您的 ViewModel 中有一个属性,该属性指示当前焦点所在的元素。
        2. 使用 FocusManager 绑定到该属性。

          <Window FocusManager.FocusedElement="{Binding ElementName=ViewModel.FocusedItem}"/>
          

        您的 ViewModel 是一个翻译器,它的存在只是为了向 View 提供信息,因此您可以将 View 需要运行的任何信息添加到 VM。

        【讨论】:

        • 但是您必须命名所有元素并在 VM 中使用该名称。如果您在视图绑定的 VM 中有一组项目,这将不起作用。
        • 恕我直言,VM 在实际视图实现中不应该有关于控件名称或类型的信息。相反,我会让视图监听来自 VM 的属性更改通知,然后让它处理它。例如ActivateNameField 变为 true.. View 找到相关控件并将焦点设置为它。
        • 我同意VM在涉及业务逻辑时必须在流程中发挥作用。一旦设计要求“当他们选择此复选框并且他们是管理员时,然后将焦点设置到 OverrideReason 文本框”,那么逻辑就属于 VM。诀窍是让视图尊重属性更改。
        • 这是如何工作的?如果您设置 ElementName=ViewModel.FocusedItem,它会寻找一个字面上命名为“ViewModel.FocusedItem”的元素,不是吗?我正在尝试使用我在代码隐藏中设置的 ViewModel 类型变量(并分配给我页面的 DataContext),但 ElementName 属性看不到它。
        【解决方案5】:

        如果它让您感觉更好(它让我感觉更好),您可以在 Xaml 中使用附加属性执行此操作:

        http://msdn.microsoft.com/en-us/library/system.windows.input.focusmanager.focusedelement.aspx

        如果您知道技巧,那么您可以在代码隐藏中执行的任何操作都可以在 Xaml 中执行。幸运的是,您不必实施此技巧 - MS 为您完成了。

        【讨论】:

        • 这正是我想要的。我认为这段代码确实属于视图,出于某种原因,我更喜欢 xaml 中的代码而不是后面的代码。
        • Galloway 的建议允许您从 ViewModel 控制焦点...您也可以仔细看看。
        • 在阅读了其他一些答案后,我不确定代码是否属于 ViewModel。它似乎与视图直接相关。如果我有一个更复杂的页面,它实际上是基于其他可能有意义的事件来移动焦点。也就是说,它仍然是一个不错的技巧。
        【解决方案6】:

        在经历了“WPF Initial Focus Nightmare”并基于堆栈上的一些答案之后,以下证明对我来说是最佳解决方案。

        首先,将您的 App.xaml OnStartup() 添加以下内容:

        EventManager.RegisterClassHandler(typeof(Window), Window.LoadedEvent,
                  new RoutedEventHandler(WindowLoaded));
        

        然后在 App.xaml 中添加“WindowLoaded”事件:

        void WindowLoaded(object sender, RoutedEventArgs e)
            {
                var window = e.Source as Window;
                System.Threading.Thread.Sleep(100);
                window.Dispatcher.Invoke(
                new Action(() =>
                {
                    window.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
        
                }));
            }
        

        必须使用线程问题,因为 WPF 初始焦点主要由于某些框架竞争条件而失败。

        我发现以下解决方案最好,因为它在全球范围内用于整个应用程序。

        希望对你有帮助...

        奥兰

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-10-14
          • 2013-02-28
          • 2011-07-17
          • 1970-01-01
          • 2011-06-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多