7

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

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

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

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

提前致谢!

4

2 回答 2

14

如果您真正使用 MVVM,那么您的 OK 按钮单击必须由某些Command. 此命令必须来自您的ViewModel. 绑定的Expliticly属性必须来自您的ViewModel再次。那是什么阻止了你。

  1. 不要使用Explicit绑定,而是使用OneWay绑定。
  2. 在您的按钮中,绑定命令并将命令参数绑定到OneWay绑定的 Dependency 属性。
  3. 在 Command 的 Execute 处理程序(必须是 ViewModel 中的某个方法)中,使用传入的参数更改 ViewModel 的属性。
  4. NotifyPropertyChanged您的ViewModel.

例如

假设我需要在单击 OK 按钮时将 TextBox 的 Text 更新回我的模型中。

因此,为此我有一个EmployeeViewModel具有EmployeeName属性的类。该属性有一个 getter 和一个 setter。设置器引发属性更改通知。视图模型还有另一个ICommand名为的类型的属性,SaveNameCommand它返回一个命令供我执行。

EmployeeViewModel是我的视图的数据上下文类型。Myview 有一个TextBox(命名为 x:Name="EmployeeNameTxBx")OneWay绑定到EmployeeName和一个 Button 作为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>

在我里面我EmployeeViewModelOnSaveNameCommandExecute(object param)方法来执行我的SaveNameCommand.

在此执行此代码...

     var text = (string)param;
     this.EmployeeName = text;

这种方式只有 OK 按钮单击,将 TextBox 的文本更新回EmployeeName模型的属性。

编辑

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

IDataErrorInfo只有当您的输入控件(例如 TextBoxes)是双向绑定时,相关验证才有效。是的,这就是它的意图。所以现在你可能会问“如果我们使用 IDataErrorInfo,这是否意味着不允许无效数据传递给模型的整个概念在 MVVM 中是徒劳的”?

实际上不是!

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

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

方法一:

如果我想在按钮单击时明确验证 UI 上的某些项目怎么办?

为此,请使用延迟验证技巧。在您的 ViewModel 中有一个名为 isValidating 的标志。默认设置为假。

在您的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必须包含无效数据才能进行验证。

方法二:

如果我想在 MVVM 中显式更新没有 TwoWay 模式的绑定怎么办?

然后你将不得不使用Attached Behavior. 该行为将附加到 OK 按钮,并将接受需要刷新其绑定的所有项目的列表。

       <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();
       }

在您SpecialBindingBehaviorDependentControls属性更改处理程序中...

      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 的 **Approach 1

于 2011-10-05T07:45:41.567 回答
1

这是一个老问题,但我仍然想为偶然发现这个问题的其他用户提供一种替代方法......在我的视图模型中,我不会直接在 get/set Property 方法中公开模型属性。我为所有属性使用内部变量。然后我双向绑定所有属性。所以我可以像“通常”那样做所有的验证,因为只有内部变量被改变了。在视图模型构造函数中,我将模型对象作为参数,并将内部变量设置为模型的值。现在,当我单击“保存”按钮(-> 在我的视图模型触发中触发保存命令)并且没有错误时,我将模型的所有属性设置为对应的内部变量的值。如果我点击“Canel/Undo”按钮(我的视图模型中的-> Cancel-Command 触发),

另一种方法是在模型中实现 Memento-Support,因此在开始编辑之前,您可以在模型中调用一个函数来保存当前值,如果取消编辑,则调用一个函数来恢复这些值……这样您将在任何地方都拥有撤消/取消支持,而不仅仅是在一个视图模型中......我已经在不同的项目中实现了这两种方法并且都可以正常工作,这取决于项目的要求......

于 2015-03-05T10:00:20.160 回答