1

この質問にはできるだけ多くの背景を説明したいと思いますが、要約すると、基本的に次の 2 つの質問をしています。

  • セッターが例外をスローしない場合、バインドされたプロパティを設定した後、WPF は常にゲッターを呼び出しますか?
  • ViewModel の実装時に setter でエラーが発生した後に、バインドされたプロパティの getter が呼び出されないようにすることはできますIDataErrorInfoか?

現在、プロパティ セッターから例外をスローして検証を実装するモデル クラスがあります。さらに、プロパティの多くは結合されているため、そのうちの 1 つの値を変更すると、他のいくつかの値が再計算される場合があります。INotifyPropertyChanged再計算が発生するたびに、外部のリスナーに警告するために実装されます。次のようになります。

public class Model : INotifyPropertyChanged
{
    private double property1;
    public double Property1
    {
        get { return property1; }
        set
        {
            if (value < 0.0)
                throw new Exception("Property1 cannot be less than 0.0.");

            property1 = value;
            OnPropertyChanged(new PropertyChangedEventArgs("Property1"));
        }
    }

    // ...Everything needed to support INotifyPropertyChanged...
}

最初に、このクラスの ViewModel を実装して、モデル プロパティのシン ラッパーとして機能し、エラーが発生するたびに追加のビュー固有の動作を提供します (無効なデータにフラグを立てる、ボタンを無効にするなど)。

public class ViewModel : INotifyPropertyChanged
{
    private readonly Model model;

    public ViewModel()
    {
        model = new Model();
        model.PropertyChanged += (sender, args) => OnPropertyChanged(args);
    }

    public string Property1
    {
        get { return model.Property1.ToString(); }
        set
        {
            try
            {
                model.Property1 = Double.Parse(value);
            }
            catch (Exception)
            {
                // Perform any view-specific actions
                throw;
            }
        }
    }

    // ...Everything needed to support INotifyPropertyChanged
}

特に、ViewModel には追加のバッキング フィールドがありません。そのすべてのプロパティはモデル内の対応するプロパティに直接リンクされ、モデルPropertyChangedからの通知は ViewModel によって渡されます。

しかし、検証に例外を使用すると制限が生じる可能性があるとよく耳にします。特に、このアプリケーションでは相互に結合された検証ルールの必要性が高まっているため、同じことを認識し始めています。

モデルはすでに他のいくつかの場所で使用されているため、モデルの動作を変更したくありませんでしたが、ViewModel を変更して実装しましたIDataErrorInfo

public class ViewModel : INotifyPropertyChanged, IDataErrorInfo
{
    private readonly Model model;

    public ViewModel()
    {
        model = new Model();
        model.PropertyChanged += (sender, args) => 
        {
            errorList.Remove(args.PropertyName);
            OnPropertyChanged(args);
        };
    }

    public string Property1
    {
        get { return model.Property1.ToString(); }
        set
        {
            try
            {
                model.Property1 = Double.Parse(value);
            }
            catch(Exception ex)
            {
                // Perform any view-specific actions                    
                errorList["Property1"] = ex.Message;
            }
        }
    }

    private readonly Dictionary<string, string> errorList = new Dictionary<string, string>();

    public string this[string propertyName]
    {
        get
        {
            string errorMessage;
            if (errorList.TryGetValue(propertyName, out errorMessage))
                return errorMessage;
            return String.Empty;
        }
    }

    public string Error { get { return String.Empty; } }

    // ...Everything needed to support INotifyPropertyChanged
}

ただし、これにより、動作に望ましくない劇的な変化が生じました。例外を使用する場合、ユーザーが無効な値を入力するValidation.ErrorTemplateと、コントロールの が表示され、無効な値がコントロールに残るため、ユーザーは間違いを修正することができます。を使用するIDataErrorInfoと、WPF はセッターが完了した後にプロパティのゲッターを呼び出すようです。エラーが発生するたびにモデルが変更されていないため、無効な値は以前の値に置き換えられます。これValidation.ErrorTemplateで、VALID 値が表示されたコントロールが表示されました。

PropertyChanged(ウィンドウが初期化された後) 通知を受信せずに WPF がプロパティ ゲッターを自動的に呼び出すのは、私にはおかしいと思います。また、例外がスローされた後にゲッターを呼び出そうとしないのに、なぜIDataErrorInfo使用時にそれを行うのでしょうか?

この動作を引き起こしている何か間違ったことをしていますか? ビューモデルの実装時にセッターでエラーが発生した後、WPF がビューモデルでプロパティ ゲッターを呼び出さないようにする方法はありますIDataErrorInfoか?

無効な値を格納するために ViewModel にバッキング フィールドを追加しようとしましたが、Model プロパティは ViewModel の外部で変更できるため (これがそもそも実装する理由ですINotifyPropertyChanged)、ソリューションは非常に複雑になり、さまざまなスキルレベルのプログラマーがいる環境では、基本的に持続不可能です。

4

2 に答える 2

0

MVVMでのフォーム検証に必要なアプローチは次のとおりです

あなたのモデル

public class Product:IDataErrorInfo
{
    public string ProductName {get;set;}

    public string this[string propertyName]
    {
       get 
       {
           string validationResult = null;
           switch (propertyName)
           {
               case "ProductName":
               validationResult = ValidateName();
           }
       }
     }
}

次に、ViewModel で

public string ProductName
{
  get { return currentProduct.ProductName; }
  set 
  {
    if (currentProduct.ProductName != value)
    {
      currentProduct.ProductName = value;
      base.OnPropertyChanged("ProductName");
    }
  }  
}

もう1つの考慮事項として、数値を検証する場合(二重検証など)、モデルを文字列ではなく二重にします

public double? Property1 {get;set;}

その後、これを行うことができます

<Textbox Name="myDoubleTextbox" >
    <Binding ValidatesOnDataErrors="true" Path="Property1" TargetValueNull="" />
/>

そのため、二重ボックスに間違ったものを入力すると、モデルに null が送信され、それをチェックできます。

于 2013-07-16T14:09:00.070 に答える