如果你真的在使用 MVVM,那么你的 OK 按钮点击必须由 Command 处理。此命令必须来自您的ViewModel。 Expliticly 绑定的属性必须再次来自您的 ViewModel。那么是什么阻止了你。
- 不要使用
Explicit绑定,而是使用OneWay绑定。
- 在你的按钮中,绑定一个命令并将一个命令参数绑定到
OneWay绑定的依赖属性。
- 在 Command 的 Execute 处理程序(必须是 ViewModel 中的某个方法)中,使用传入的参数更改 ViewModel 的属性。
- 从您的
ViewModel 提高该属性的NotifyPropertyChanged。
例如
假设我需要在单击确定按钮时将 TextBox 的文本更新回我的模型中。
因此,我有一个 EmployeeViewModel 类,其中包含 EmployeeName 属性。该属性有一个 getter 和一个 setter。设置器引发属性更改通知。视图模型还有另一个 ICommand 类型的属性,名为 SaveNameCommand,它返回一个命令供我执行。
EmployeeViewModel 是我的视图的数据上下文类型。 Myview 有一个TextBox(命名为 x:Name="EmployeeNameTxBx")OneWay 绑定到EmployeeName 和一个按钮为OK。我将Button.Command 属性绑定到EmployeeViewModel.SaveNameCommand 属性,Button.CommandParameter 绑定到EmployeeNameTxBx.Text 属性。
<StackPanel>
<TextBox x:Name="EmployeeNameTxBx"
Text="{Binding EmployeeName, Mode=OneWay}" />
<Button Content="OK"
Command="{Binding SaveNameCommand}"
CommandParameter="{Bidning Text, ElementName=EmployeeNameTxBx}" />
</StackPanel>
在我的EmployeeViewModel 中,我有OnSaveNameCommandExecute(object param) 方法来执行我的SaveNameCommand。
在此执行此代码...
var text = (string)param;
this.EmployeeName = text;
这种方式只有 OK 按钮单击,将 TextBox 的文本更新回模型的 EmployeeName 属性。
编辑
查看下面的 cmets,我发现您正在尝试在 UI 上实现验证。现在这改变了一些事情。
IDataErrorInfo 和相关验证仅在您的输入控件(例如 TextBoxes)被 TwoWay 绑定时才有效。是的,这就是它的意图。所以现在你可能会问“如果我们使用 IDataErrorInfo,这是否意味着不允许无效数据传递给模型的整个概念在 MVVM 中是徒劳的”?
实际上没有!
请参阅 MVVM 不强制执行仅应返回有效数据的规则。它接受无效数据,这就是IDataErrorInfo 的工作方式并引发错误通知。关键是 ViewModel 只是您的视图的 软拷贝,因此它可能是 脏。它应该确保这种肮脏没有承诺到您的外部接口,例如服务或数据库。
这种无效的数据流应该被ViewModel通过测试无效数据来限制。如果我们启用了TwoWay 绑定,这些数据就会出现。所以考虑到你正在实现IDataErrorInfo,那么你需要有TwoWay绑定,这在MVVM中是完全允许的。
方法一:
如果我想在按钮点击时明确验证 UI 上的某些项目怎么办?
为此使用延迟验证技巧。在您的 ViewModel 中有一个名为 isValidating 的标志。默认设置为false。
在您的 IDataErrorInfo.this 属性中,通过检查 isValidating 标志来跳过验证...
string IDataErrorInfo.this[string columnName]
{
get
{
if (!isValidating) return string.Empty;
string result = string.Empty;
bool value = false;
if (columnName == "EmployeeName")
{
if (string.IsNullOrEmpty(AccountType))
{
result = "EmployeeName cannot be empty!";
value = true;
}
}
return result;
}
}
然后在您的 OK 命令执行的处理程序中,检查员工姓名,然后针对同一属性引发属性更改通知事件...
private void OnSaveNameCommandExecute(object param)
{
isValidating = true;
this.NotifyPropertyChanged("EmployeeName");
isValidating = false;
}
仅当您单击“确定”时才会触发验证。请记住,EmployeeName 必须包含无效数据才能进行验证。
方法2:
如果我想在 MVVM 中不使用 TwoWay 模式显式更新绑定怎么办?
那么你将不得不使用Attached Behavior。该行为将附加到“确定”按钮,并接受需要刷新其绑定的所有项目的列表。
<Button Content="OK">
<local:SpecialBindingBehavior.DependentControls>
<MultiBinding Converter="{StaticResource ListMaker}">
<Binding ElementName="EmployeeNameTxBx" />
<Binding ElementName="EmployeeSalaryTxBx" />
....
<MultiBinding>
</local:SpecialBindingBehavior.DependentControls>
</Button>
ListMaker 是一个 IMultiValueConverter,它只是将值转换为列表...
Convert(object[] values, ...)
{
return values.ToList();
}
在您的SpecialBindingBehavior 中有一个DependentControls 属性更改处理程序...
private static void OnDependentControlsChanged(
DependencyObject depObj,
DependencyPropertyChangedEventArgs e)
{
var button = sender as Button;
if (button != null && e.NewValue is IList)
{
button.Click
+= new RoutedEventHandler(
(object s, RoutedEventArgs args) =>
{
foreach(var element in (IList)e.NewValue)
{
var bndExp
= ((TextBox)element).GetBindingExpression(
((TextBox)element).Textproperty);
bndExp.UpdateSource();
}
});
}
}
但我仍然建议您使用我之前的基于纯 MVVM 的**方法 1。