【问题标题】:Set UpdateSourceTrigger to Explicit in ShowDialog (WPF MVVM)在 ShowDialog (WPF MVVM) 中将 UpdateSourceTrigger 设置为显式
【发布时间】:2011-12-01 06:16:27
【问题描述】:

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

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

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

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

提前致谢!

【问题讨论】:

    标签: wpf data-binding mvvm showdialog updatesourcetrigger


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

      如果你真的在使用 MVVM,那么你的 OK 按钮点击必须由 Command 处理。此命令必须来自您的ViewModelExpliticly 绑定的属性必须再次来自您的 ViewModel。那么是什么阻止了你。

      1. 不要使用Explicit绑定,而是使用OneWay绑定。
      2. 在你的按钮中,绑定一个命令并将一个命令参数绑定到OneWay绑定的依赖属性。
      3. 在 Command 的 Execute 处理程序(必须是 ViewModel 中的某个方法)中,使用传入的参数更改 ViewModel 的属性。
      4. 从您的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

      【讨论】:

      • 首先感谢您的详细解答!回到我的问题,我使用的是纯 mvvm,我的 vm 中有命令,我使用的是 NotifyPropertyChanged。但是如果我使用 OneWay 绑定并且我在窗口中有 10 个文本框,这意味着我需要在命令参数中发送 10 个元素名称?然后手动更新它们?有没有办法使用ToWay绑定?
      • 我现在也在使用 IDataError 来验证文本框中的文本,如果我使用 OneWay 绑定,我还能验证文本吗?
      • 如果我理解你的答案,我需要做 ToWay 绑定,但我会回到我的上一个。我不想立即更新源的问题,但只要用户单击“确定”,我就需要将 UpdateSourceTrigger 设置为 Explicit,然后呢?我无法在 TextBox 实例上使用 UpdateSource,因为我在 VM 中而不是在视图中...
      • 我错过了什么吗?让我告诉你我的理解,如果我是对的,请告诉我。如果我实现 IDataErrorInfo,我需要进行 ToWay 绑定,并且我可以避免错误的数据来实际更新源,直到这里一切正常。现在即使没有错误输入,如果用户不单击“确定”按钮,我也不想更新源。例如用户单击“取消”或按 Esc。因此,如果我使用 ToWay 并且 IDataErrorInfo 不会引发任何错误,即使用户单击“取消”,源也会立即更新。
      猜你喜欢
      • 2011-03-05
      • 2016-07-28
      • 2015-01-06
      • 2010-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多