在ShowDialog中将UpdateSourceTrigger设置为Explicit(WPF MVVM)

May*_*aya 6 data-binding wpf mvvm updatesourcetrigger showdialog

我看到了这个例子--Binding.UpdateSourceTrigger属性

在示例中,UpdateSourceTrigger设置为Explicit,然后在视图代码中调用TextBox名称的UpdateSource.

但是,如果我使用MVVM dp,我不想让我的控件和源属性在VM中,而不是在视图中,那么将控件绑定到VM属性并将UpdateSourceTrigger设置为显式的正确方法是什么?

我想这样做,因为在我的情况下它的ShowDialog窗口,我希望只有当用户点击"确定"时源才会更新

提前致谢!

WPF*_*-it 12

如果你真的使用MVVM,那么你的OK按钮必须由一些人来处理Command.此命令必须来自您的ViewModel.该Expliticly绑定属性必须从现身ViewModel一次.所以什么阻止你.

  1. 不要使用Explicit绑定但使用OneWay绑定.
  2. 在按钮中,绑定命令并将命令参数绑定到OneWay绑定的Dependency属性.
  3. 在Command的Execute处理程序(必须是ViewModel中的某个方法)中,使用参数来更改ViewModel的属性.
  4. NotifyPropertyChanged从你的房子那里筹集房产ViewModel.

例如

假设我需要在单击确定按钮时将TextBox的文本更新回我的模型.

所以为此,我有一个EmployeeViewModel具有EmployeeName属性的类.该物业有一个吸气剂和一个二传手.setter引发属性更改通知.视图模型也有类型的另一个属性ICommand名为SaveNameCommand返回给我执行命令.

EmployeeViewModel是我的视图的数据上下文类型.MyView的有TextBox(命名为x:名称="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>
Run Code Online (Sandbox Code Playgroud)

在我的内部我EmployeeViewModelOnSaveNameCommandExecute(object param)执行我的方法SaveNameCommand.

在此执行此代码...

     var text = (string)param;
     this.EmployeeName = text;
Run Code Online (Sandbox Code Playgroud)

这样只需单击OK按钮,将TextBox的文本更新回EmployeeName模型的属性.

编辑

查看下面的评论,我发现您正在尝试在UI上实现验证.现在这会改变一些事情.

IDataErrorInfo和相关验证仅在输入控件(如TextBoxes)是TwoWay绑定时才起作用.是的,这就是它的意图.所以现在你可能会问:"这是否意味着如果我们使用IDataErrorInfo,那么不允许将无效数据传递给模型的整个概念在MVVM中是徒劳的"?

并不是!

请参阅MVVM不强制执行仅返回有效数据的规则.它接受无效数据,这是如何IDataErrorInfo工作和引起错误通知.关键是ViewModel仅仅是对View的软拷贝,因此它可能很脏.应该确保的是,这种肮脏不会提交给您的外部接口,例如服务或数据库.

这种无效数据流应该ViewModel通过测试无效数据来限制.如果我们TwoWay启用了绑定,那么数据将会出现.因此,考虑到您正在实现,IDataErrorInfo那么您需要具有TwoWayMVVM中完全允许的绑定.

方法1:

如果我想在按钮点击时明确验证用户界面上的某些项目怎么办?

为此使用延迟验证技巧.在您的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;
      }
    }
Run Code Online (Sandbox Code Playgroud)

然后在您的OK命令执行处理程序中,检查员工姓名,然后为同一属性提出属性更改通知事件...

    private void OnSaveNameCommandExecute(object param)
    {
         isValidating = true;
         this.NotifyPropertyChanged("EmployeeName");
         isValidating = false;
    }
Run Code Online (Sandbox Code Playgroud)

仅在单击"确定"时才会触发验证.请记住,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>
Run Code Online (Sandbox Code Playgroud)

ListMaker是一个IMultiValueConverter简单的将值转换为列表...

       Convert(object[] values, ...)
       {
            return values.ToList();
       }
Run Code Online (Sandbox Code Playgroud)

在你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();
                              }
                         });
           }
      }
Run Code Online (Sandbox Code Playgroud)

但我仍然建议你使用我之前的纯MVVM**方法1.