识别整个DataGrid丢失的焦点事件,而不是其控制失去焦点

时间:2013-06-17 03:44:55

标签: .net wpf mvvm datagrid mvvm-light

目标:

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

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

是什么让这很难: 简单地说,使用MVVM的原因很简单。通常你可以在lostfocus事件后面的代码中检查FocusManager来获取当前聚焦的元素,看看它是否在datagrid(As outlined here)中。

问题:

这个问题是否有MVVM标准解决方案?我不是那么盲目地死硬MVVM从来没有代码落后,我想我只是想知道这是否是那个时代之一或者是否有一些策略。我没有考虑过,这很可能。

我尝试了什么:

第一次 - 我试图为不同的命令提供不同的命令参数。即:

<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中的控件

<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触发器将视觉树绑定到这三个元素。即使放在这三个“父”对象中的任何一个上,丢失的焦点事件也会以相同的方式被触发。


我开始认为我需要找到另一个可以工作的事件,或者完全重新思考我如何为viewmodels的HasErrors属性处理这个错误触发器。

我非常感谢帮助隔离数据网格的丢失焦点事件,这仍然遵循MVVM标准。

由于

1 个答案:

答案 0 :(得分:5)

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

这会导致无处不在的过度复杂化,因为我们倾向于避免在ALL处出现代码隐藏,而不是真正理解如果我们不在View层中放置任何代码/逻辑,那么就没有Bindings,没有转换器,没有{ {1}},根本没有XAML。

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

这实际上意味着您可以并且应该通过Code-Behind解决separate问题(纯粹是View关注点,btw)。 但是,这并不意味着您将在Code-Behind中放置任何应用程序/业务逻辑。

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

Focus

明白我的意思?你不是在这里放置业务逻辑。业务逻辑仍在ViewModel / Model中,而View相关代码(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(); } } )则放在Code Behind中。

此外,这正是Focus所做的。他们对View中的某个事件做出反应,然后在ViewModel中调用一些方法,不是吗?

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

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