【问题标题】:Recognise the lost focus event of a DataGrid as a whole, not its controls lost focus识别整个 DataGrid 的失去焦点事件,而不是其控件失去焦点
【发布时间】:2013-06-12 23:18:27
【问题描述】:

目标:

我有一个 WPF/MVVM(确切地说是 mvvm-light)应用程序,我想在其中识别/捕获整个 datagid 的 lostfocus 事件并将其绑定到视图模型中的 ICommand。

问题是每次数据网格中的控件失去焦点以及数据网格本身失去焦点时都会触发丢失焦点事件。在我的应用程序中,如果 viewModle“HasErrors”属性为真,如果用户尝试“导航”离开当前视图,我会在 datagrid lostfocus 事件/命令上抛出警告(MVVM 类型消息框)。结果是即使用户在数据网格中的控件之间移动,用户也会收到此错误/警告。我只在整个数据网格失去焦点时才想要它。

是什么让这变得困难: 简而言之,使这变得困难的是使用 MVVM。通常你可以在 lostfocus 事件后面的代码中检查 FocusManager 来获取当前聚焦的元素并查看它是否在数据网格中 (As outlined here)。

问题:

这个问题有 MVVM 标准解决方案吗?我不是那么盲目地死去 MVVM 永远不会有代码,我想我只是想知道这是不是其中一个时代,或者是否有一些我没有考虑过的策略选项,这很可能。

我尝试了什么:

FIRST- 我尝试为不同的命令设置不同的命令参数。即:

<DataGrid>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="LostFocus">
            <cmd:EventToCommand Command="{Binding DataContext.PreNavigateValidateCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
            CommandParameter="DataGridLostFocus"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</DataGrid>

对于数据网格中的控件

<DataGrid>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="LostFocus">
            <cmd:EventToCommand Command="{Binding DataContext.LostFocusValidateCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
             CommandParameter="ControlostFocus"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</DataGrid>

注意DataGridLostFocus 和ControlLostFocus 命令参数的不同。但是发生的情况是这些命令只发生了两次,每个命令参数一个,首先发生控制 lostfocus,然后是 datadrid lostfocus。

SECOND- 您会注意到不同的命令属性/名称。即使将命令绑定到不同的命令对象也不能解决这个问题。这两个命令都将按照上述相同的顺序被调用。

THIRD 数据网格在网格内,在扩展器内,在用户控件内。我尝试将 ICommand triggerbinding 向上移动到视觉树到这三个元素。即使放置在这三个“父”对象中的任何一个上,失去焦点事件也会以相同的方式触发。


我开始认为我需要找到另一个可行的事件,或者完全重新考虑如何处理视图模型的 HasErrors 属性的这个错误触发器。

如果能帮助我将整个数据网格的失去焦点事件隔离开来,仍然遵循 MVVM 标准,我将不胜感激。

谢谢

【问题讨论】:

    标签: .net wpf mvvm datagrid mvvm-light


    【解决方案1】:

    我认为我们大多数开发人员(包括我自己)长期以来都广泛地误解了 MVVM。

    这会导致不必要的过度复杂化,因为我们倾向于完全避免代码隐藏,并没有真正理解如果我们不在视图层中放置任何代码/逻辑,就没有绑定、没有转换器、没有 @ 987654321@,根本没有 XAML。

    MVVM 的真正精神是来自 UI 的separate 逻辑,而不是避免视图中有任何代码。

    这确实意味着您可以并且应该通过代码隐藏解决Focus 问题(顺便说一句,这纯粹是视图问题)。 但是,这并不意味着您要将任何应用程序/业务逻辑放在代码隐藏中。

    简单地说,只需在代码中处理任何 UI 事件,然后将逻辑委托给 ViewModel:

    private void DataGrid_LostFocus(object sender, RoutedEventArgs e)
    {
        if (DataGrid.IsKeyboardFocusWithin) //or whatever UI condition
        {
            //Resolve the ViewModel via DI, constructor injection or whatever. Then:
            ViewModel.DoMyBusinessLogic();
        }
    }
    

    明白我的意思了吗?您没有将业务逻辑放在这里。业务逻辑仍在 ViewModel/Model 中,而 View 相关代码 (Focus) 则放在 Code Behind 中。

    另外,这正是Commands 所做的。它们对 View 中的某些事件做出反应,然后调用 ViewModel 中的某些方法,不是吗?

    我认为这将真正缓解我们习惯的严格的无代码隐藏政策所带来的痛苦。

    我也想听听其他人对此的看法

    【讨论】:

    • 谢谢。一个很好的清晰解释,显示了如何“处理后面代码中的任何 UI 事件,然后将逻辑委托给 ViewModel”我倾向于这种方式,但我的解决方案并不那么干净,我正在寻找一些社区反馈。我知道这是一个常见的话题/误解,我同意我们可以把事情复杂化。我自己把注意力集中在通过严格绑定到 ViewModel 属性来完成所有事情上。话虽如此,我仍然觉得总体而言 MVVM 方法促使我编写更好、更易于维护和可测试的代码。
    • 我认为有趣的是,我的经验是 MVVM 没有普遍接受的“最佳实践”。甚至 MVVM 的框架都是新的,并且得到了较小社区的支持(除了 prism,这是另一个陡峭的学习曲线)我对 MVVM 的学习曲线非常陡峭,因为我发现了很多关于如何应用 MVVM 的不同文档,其中大部分是几年老的。即使是声称使用 MVVM 编写了大部分 Visual Studio 2012 的 Microsoft 也没有清楚地记录其最佳实践。至少我能找到什么。我希望随着 MVVM 的成熟,这种情况会有所改善。
    • @HighCode 简直是绝妙的词。每种方法或最佳实践都应合理采用,否则会沦为 Cargo Cult
    猜你喜欢
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-03
    相关资源
    最近更新 更多