7

私はこの例を見ました-Binding.UpdateSourceTriggerプロパティ

この例では、UpdateSourceTriggerがExplicitに設定されており、ビューコードでTextBox名のUpdateSourceを呼び出しています。

しかし、MVVM dpを使用する場合、コントロールに名前を付けたくなく、ソースプロパティはVMにあり、ビューにはありません。コントロールをVMプロパティにバインドし、UpdateSourceTriggerを明示的に設定する正しい方法は何ですか?

私の場合はShowDialogウィンドウであり、ユーザーが[OK]をクリックした場合にのみソースが更新されるようにしたいので、これを実行したいと思います。

前もって感謝します!

4

2 に答える 2

14

MVVMを実際に使用している場合は、[OK]ボタンのクリックを誰かが処理する必要がありますCommand。このコマンドは、から来ている必要がありますViewModel。バインドされたExpliticlyプロパティは、再びあなたから来ている必要がありますViewModel。だから何があなたを止めていますか。

  1. バインディングを使用せず、Explicitバインディングを使用しますOneWay
  2. ボタンで、コマンドをバインドし、コマンドパラメーターをOneWayバインドされたDependencyプロパティにバインドします。
  3. コマンドの実行ハンドラー(ViewModelのメソッドである必要があります)で、パラメーターが来るようにViewModelのプロパティを変更します。
  4. NotifyPropertyChangedあなたのからそのプロパティのを上げますViewModel

例えば

[OK]ボタンをクリックすると、TextBoxのテキストをモデルに更新する必要があると仮定します。

そのために、プロパティEmployeeViewModelを持つクラスがありEmployeeNameます。プロパティには、ゲッターとセッターがあります。セッターはプロパティ変更通知を発行します。ビューモデルには、実行するコマンドを返す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がモデルのプロパティに更新されます。

編集

以下のコメントを見ると、UIに検証を実装しようとしていることがわかります。今、これは物事を少し変えます。

IDataErrorInfoおよび関連する検証は、入力コントロール(TextBoxなど)がTwoWayにバインドされている場合にのみ機能します。はい、それが意図された方法です。では、「これは、IDataErrorInfoを使用する場合、モデルへの無効なデータの受け渡しを許可しないという概念全体がMVVMでは無駄であることを意味しますか?」

実際はそうではなくて!

MVVMは、有効なデータのみが返される必要があるというルールを適用しないを参照してください。それは無効なデータを受け入れ、それがどのようにIDataErrorInfo機能し、エラー通知を発生させるかです。重要なのは、ViewModelはビューの単なるソフトコピーであるため、汚れている可能性があるということです。確認する必要があるのは、この汚れがサービスやデータベースなどの外部インターフェイスにコミットされていないことです。

ViewModelこのような無効なデータフローは、無効なデータをテストすることによって制限する必要があります。TwoWayバインディングを有効にすると、そのデータが表示されます。したがって、実装していることを考えると、MVVMで完全に許可されIDataErrorInfoているバインディングが必要です。TwoWay

アプローチ1:

ボタンクリック時に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;
    }

これにより、[OK]をクリックした場合にのみ検証がトリガーされます。EmployeeName検証が機能するには、無効なデータが含まれている必要があることに注意してください。

アプローチ2:

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値をリストに変換するだけのaです。

       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を使用することをお勧めします。

于 2011-10-05T07:45:41.567 に答える
1

これは古い質問ですが、この質問に遭遇した他のユーザーに別のアプローチを提供したいと思います...私のビューモデルでは、get /setPropertyメソッドでモデルプロパティを直接公開しません。すべてのプロパティに内部変数を使用します。次に、すべてのプロパティを双方向でバインドします。したがって、内部変数のみが変更されるため、すべての検証を「通常」として実行できます。ビューモデルコンストラクターでは、パラメーターとしてモデルオブジェクトがあり、内部変数をモデルの値に設定します。ここで、[保存]ボタンをクリックして(->ビューモデルで[保存]コマンドが起動します)、エラーが発生しない場合、モデルのすべてのプロパティを対応する内部変数の値に設定します。「Canel/Undo」-ボタン(->キャンセル-ビューモデルのコマンドが起動します)をクリックすると、

さらに別のアプローチは、モデルにMemento-Supportを実装することです。したがって、編集を開始する前に、モデル内の関数を呼び出して現在の値を保存し、編集をキャンセルする場合は、関数を呼び出してそれらの値を復元します...そのように1つのビューモデルだけでなく、どこでも取り消し/キャンセルのサポートがあります...私は両方のメソッドを異なるプロジェクトに実装しましたが、どちらも正常に機能します。プロジェクトの要件によって異なります...

于 2015-03-05T10:00:20.160 に答える