2

GOAL:

I have an WPF/MVVM (mvvm-light to be exact) application where I want to recognise/capture the lostfocus event of the datagid as a whole and bind it to an ICommand in the view model.

The problem is that the lost focus event is fired every time a control within the datagrid loses focus as well as the datagrid itself losing focus. In my applications I throw a warning (a MVVM type message box) on the datagrid lostfocus event/command if a user tries to "navigate" away from the current view if the viewModle "HasErrors" property is true. The result is that even if the user moves between controls in the datagrid, the user gets this error/warning. I only want it when the datagrid as a whole loses focus.

What makes this hard: Simply put, what makes this hard is using the MVVM. Usually you could just check the FocusManager in the code behind lostfocus event to get the currently focussed element and see if its in the datagrid (As outlined here).

Question:

Is there a MVVM standard solution to this problem? I am not so blindly die hard MVVM to never have code behind, I guess I am just wondering if this is one of those times or is there some strategy.option I didn't think about, which is likely.

What I tried:

FIRST- I tried to have different command parameters for the different commands. ie:

<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>

and for the controls in the 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>

notice the difference in the command parameters of DataGridLostFocus and ControlLostFocus. But what happens is that these commands just happen twice, one for each command parameter with the control lostfocus happening first followed by the datadrid lostfocus.

SECOND- You will notice the different command properties/names. Even binding the commands to different command objects did not solve this problem. Both commands will be called, in the same order described above.

THIRD The datagrid is inside a grid, which is inside an expander, which is inside a usercontrol. I tried moving the ICommand triggerbinding up the visual tree to these three elements. The lost focus event gets fired the same way even when placed on any of these three "parent" objects.


I am starting to think I eighter need to find another event that would work or totally rethink how I handle this error trigger for the viewmodels's HasErrors property.

I would appreciate any help in isolating the lost focus event of the datagrid as whole that still follows MVVM standards.

Thanks

4

1 回答 1

8

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

这导致了不必要的过度复杂化,因为我们倾向于完全避免代码隐藏,并没有真正理解如果我们不将任何代码/逻辑放在 View 层中,将没有绑定,没有转换器,没有RelativeSource,没有 XAML全部。

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

这实际上意味着您可以并且应该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 中的某些方法,不是吗?

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

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

于 2013-06-17T04:20:31.393 回答