目标:
我有一个 WPF/MVVM(确切地说是 mvvm-light)应用程序,我想在其中识别/捕获整个 datagid 的 lostfocus 事件并将其绑定(bind)到 View 模型中的 ICommand。
问题是每次数据网格中的控件失去焦点以及数据网格本身失去焦点时都会触发丢失焦点事件。在我的应用程序中,如果 viewModle“HasErrors”属性为真,如果用户尝试“导航”离开当前 View ,我会在 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- 您会注意到不同的命令属性/名称。即使将命令绑定(bind)到不同的命令对象也不能解决这个问题。这两个命令都将按照上述相同的顺序被调用。
THIRD 数据网格在网格内,在扩展器内,在用户控件内。我尝试将 ICommand triggerbinding 向上移动到视觉树到这三个元素。即使放置在这三个“父”对象中的任何一个上,失去焦点事件也会以相同的方式触发。
我开始认为我需要找到另一个可行的事件,或者完全重新考虑如何处理 View 模型的 HasErrors 属性的这个错误触发器。
如果能帮助我将整个数据网格的丢失焦点事件隔离开来,仍然遵循 MVVM 标准,我将不胜感激。
谢谢
最佳答案
我认为我们大多数开发人员(包括我自己)长期以来都广泛地误解了 MVVM。
这会导致不必要的过度复杂化,因为我们倾向于完全避免代码隐藏,并没有真正理解如果我们不在 View 层中放置任何代码/逻辑,就没有绑定(bind)、没有转换器、没有 RelativeSource
,根本没有 XAML。
MVVM 的真正精神是将逻辑与 UI 分离
,而不是避免 View 中有任何代码。
这实际上意味着您可以并且应该通过 Code-Behind 解决 Focus
问题(顺便说一句,这纯粹是 View 问题)。
但是,这并不意味着您要将任何应用程序/业务逻辑放在代码隐藏中。
简单地说,只需在代码中处理任何 UI 事件,然后将逻辑委托(delegate)给 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 中的某些事件使用react,然后调用 ViewModel 中的某些方法,不是吗?
我认为这将真正缓解我们习惯的严格的无代码隐藏政策所带来的痛苦。
我也想听听其他人对此的看法
关于.net - 识别整个 DataGrid 的失去焦点事件,而不是其控件失去焦点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17140137/