我看到了这个例子 - Binding.UpdateSourceTrigger Property
在示例中,UpdateSourceTrigger设置为Explicit,然后在视图代码中调用TextBox名称的UpdateSource。
但是,如果我使用MVVM dp,我不想让我的控件名称和源属性在VM中而不在视图中,那么将控件绑定到VM属性并将UpdateSourceTrigger设置为显式的正确方法是什么?
我想这样做是因为在我的情况下它的ShowDialog窗口我希望只有当用户点击“确定”时才会更新源
提前致谢!
答案 0 :(得分:11)
如果您真正使用MVVM,则必须由某些Command
处理您的“确定”按钮。此命令必须来自您的ViewModel
。 Expliticly
绑定属性必须再次来自ViewModel
。所以什么阻止了你。
Explicit
绑定,而是使用OneWay
绑定。OneWay
绑定的依赖项属性。NotifyPropertyChanged
ViewModel
醇>
E.g。
假设我需要在单击确定按钮上将TextBox的文本更新回我的模型。
因此,我有一个EmployeeViewModel
类,其中包含EmployeeName
属性。该物业有一个吸气剂和一个二传手。 setter引发属性更改通知。视图模型还有另一个名为ICommand
的{{1}}类型的属性,它返回一个命令供我执行。
SaveNameCommand
是我视图的数据上下文类型。 Myview将EmployeeViewModel
(名称为x:Name =“EmployeeNameTxBx”)TextBox
绑定到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
这样只需单击OK按钮,将TextBox的文本更新回模型的 var text = (string)param;
this.EmployeeName = text;
属性。
修改强>
查看下面的评论,我发现您正在尝试在UI上实施验证。现在这会改变一些事情。
仅当您的输入控件(例如TextBoxes) TwoWay 绑定时, EmployeeName
和相关验证才有效。是的,这就是它的意图。所以现在你可能会问:“这是否意味着如果我们使用IDataErrorInfo,那么不允许将无效数据传递给模型的整个概念在MVVM中是徒劳的”?
实际上不是!
请参阅MVVM不会强制执行仅返回有效数据的规则。它接受无效数据,这就是IDataErrorInfo
的工作方式,并引发错误通知。关键是ViewModel只是View的软拷贝,因此它可以脏。它应该确保这种肮脏不会提交到您的外部接口,例如服务或数据库。
通过测试无效数据,IDataErrorInfo
应限制此类无效数据流。如果我们启用了ViewModel
绑定,那么这些数据就会出现。因此,考虑到您正在实现TwoWay
,那么您需要拥有IDataErrorInfo
绑定,这在MVVM中是完全允许的。
方法1:
如果我想在按钮点击时明确验证用户界面上的某些项目该怎么办?
对于此用途,延迟验证技巧。在您的ViewModel中有一个名为isValidating的标志。默认设置为false。
在TwoWay
属性中,通过检查isValidating标志...
IDataErrorInfo.this
然后在您的OK命令执行处理程序中,检查员工姓名,然后为同一属性提出属性更改通知事件...
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;
}
}
仅在单击“确定”时触发验证。请记住, private void OnSaveNameCommandExecute(object param)
{
isValidating = true;
this.NotifyPropertyChanged("EmployeeName");
isValidating = false;
}
必须包含无效数据才能使验证生效。
方法2:
如果我想在MVVM中显式更新没有TwoWay模式的绑定怎么办?
然后你必须使用EmployeeName
。该行为将附加到“确定”按钮,并将接受需要刷新其绑定的所有项目的列表。
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
但我仍然建议你使用我以前的纯MVVM **方法1 。
答案 1 :(得分:1)
这是一个古老的问题,但我仍然想为偶然发现这个问题的其他用户提供另一种方法...... 在我的viewmodels中,我没有直接在get / set Property方法中公开模型属性。我为所有属性使用内部变量。然后我双向绑定所有属性。所以我可以做所有的验证,因为&#34;通常&#34;因为只有内部变量被改变了。在视图模型构造函数中,我将模型对象作为参数,并将内部变量设置为我的模型的值。现在当我点击&#34; Save&#34;按钮( - &gt;保存命令在我的视图模型中触发)并且没有错误,我将模型的所有属性设置为相应内部变量的值。如果我点击&#34; Canel / Undo&#34; -Button(在我的视图模型中触发&gt;取消 - 命令),我将内部变量设置为我未触摸模型的值(使用视图的设置者)模型属性,以便调用NotifyPropertyChanged并且视图显示更改=旧值。)
另一种方法是在模型中实现Memento-Support,因此在开始编辑之前,调用模型中的函数来保存当前值,如果取消编辑,则调用函数来恢复这些值。那么你不仅可以在一个视图模型中获得撤消/取消支持... 我在不同的项目中实现了这两种方法,并且都运行良好,这取决于项目的要求......