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

J K*_*ing 2 .net wpf datagrid mvvm mvvm-light

目标:

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

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

是什么让这很难: 简单地说,使用MVVM的原因很难.通常,您可以在lostfocus事件后面的代码中检查FocusManager,以获取当前聚焦的元素,并查看它是否在datagrid中(如此处所述).

题:

是否有针对此问题的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>
Run Code Online (Sandbox Code Playgroud)

以及数据网格中的控件

<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>
Run Code Online (Sandbox Code Playgroud)

注意DataGridLostFocus和ControlLostFocus的命令参数的差异.但是发生的事情是这些命令只发生两次,每个命令参数一次,控制lostfocus首先发生,然后是datadrid lostfocus.

SECOND - 你会注意到不同的命令属性/名称.即使将命令绑定到不同的命令对象也无法解决此问题.将按照与上述相同的顺序调用这两个命令.

第三个数据网格位于网格中,网格位于扩展器内,位于用户控件内.我尝试将ICommand触发器将视觉树绑定到这三个元素.即使放在这三个"父"对象中的任何一个上,丢失的焦点事件也会以相同的方式被触发.


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

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

谢谢

Fed*_*gui 6

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

这导致无处不在的过度复杂化,因为我们倾向于避免在ALL处进行代码隐藏,而不是真正理解如果我们不在View层中放置任何代码/逻辑,那么就没有绑定,没有转换器,没有RelativeSource,没有XAML在所有.

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

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

简单地说,只需处理代码中的任何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();
    }
}
Run Code Online (Sandbox Code Playgroud)

明白了吗?你不是在这里放置业务逻辑.业务逻辑仍在ViewModel/Model中,而View相关代码(Focus)则放在Code Behind中.

而且,这正是做Commands什么的.他们对View中的某些事件做出反应,然后在ViewModel中调用一些方法,不是吗?

我认为这将真正缓解我们习惯的严格的无代码制策略所带来的痛苦.

我也想听听别人对此的看法