2

s を使用して一部のコードをより適切な MVVM 実装に変換する作業を行っておりDataTemplate、特定の種類の UI 検証で問題が発生しています。

ビュー モデルでの検証に問題はありませんIDataErrorInfo。実装されており、すべて問題ありません。私が問題を抱えているのは、文字をTextBoxint にバインドする可能性がある UI バインディング エラーです。

以前は、次を使用していました:

System.Windows.Controls.Validation.AddErrorHandler(userControl, handler) 

...そして、フォームのすべてのデータが問題ないかどうかを知るために、追加および削除されたエラーの数を保持しました。

しかし、MVVM を実行しているので、このハンドラーを設定するための userControl にアクセスできません。だから私はこれを始めるためのフックを本当に持っていません。

DataTemplateApplied次のようなことができるグローバル イベント ハンドラはありますか。

void OnDataTemplateApplied(object data, Control template)
{
  if (data is MyViewModelBase)
  {
    Validation.AddErrorHandler(template, handler);
  }
}

または、外側のシェル ウィンドウのブートストラッパーで 1 回呼び出しAddErrorHandlerて、イベントが発生するたびに、どの ViewModel がその特定のコントロールに電力を供給しているかを何らかの形で把握することもできますか?

すべての VM フィールドを文字列にして、VM で多くの型変換を行うのが好きな人がいることは知っています。これは、さまざまな理由から、私たちのシステムでは現実的ではありません。

4

1 に答える 1

4

この回答に興味があるかもしれません: https://stackoverflow.com/a/13335971/1094526 主なアイデアはまさにあなたが言ったことです (エラーハンドラーにサブスクライブします)。私が理解しているように、問題はViewModelからコントロールにアクセスできないことですが、解決するのは難しくありません

私が取り組んでいるプロジェクトでは、ViewModel から AddUIError と RemoveUIError の 2 つのメソッドを公開しました。ビューでイベント ハンドラーを作成し、そこで DataContext をビューモデルの型にキャストし、何が起こったかに応じて AddUIError または RemoveUIError を呼び出します。View を ViewModel に関連付けるために DataTemplates を使用しているため、テンプレートが適用されると、DataContext が自動的に ViewModel に設定されます。必要に応じて、ViewModel をプライベート フィールド (ビュー内) に格納し、DataContext が変更されるたびに参照を更新できます (DataContextChanged イベントがあります)。

これを複数の ViewModel で行う場合は、両方のメソッド (AddUIError と RemoveUIError) を ViewModelBase などのクラスに配置し、ValidationError イベント処理を Behavior に移動して、各ビューで使用できます。


動作部分に関する詳細: Behavior クラスは Expression Blend SDK の一部であるため、この方法に従う場合はこれが必要になります。

動作は、たとえば、派生クラスを作成せずに、いくつかの共通機能を多くのコンポーネントにアタッチするのに役立ちます。

まず、ViewModelBase という名前のクラスで AddUIError と RemoveUIError を定義する必要があります (もちろん、これは他のすべての ViewModel の基本クラスです)。

class ViewModelBase {
   public void AddUIError(...) {/* Details ommitted */ }
   public void RemoveUIError(...) {/* Details ommitted */ }
}

次に、Behavior をサブクラス化して Behavior を作成します。FrameworkElement をテンプレート引数として使用するため、この動作を任意の FrameworkElement (または派生クラス) インスタンスにアタッチできます。

class NotifyDataErrorsBehavior : Behavior<FrameworkElement>
{
    // Called when the the Behavior is attached
    protected override void OnAttached()
    {
        base.OnAttached();
        // Initialize the handler for the Validation Error Event
        _handler = new RoutedEventHandler(OnValidationRaised);
        // Add the handler to the event from the element which is attaching this behavior
        AssociatedObject.AddHandler(System.Windows.Controls.Validation.ErrorEvent, _handler);
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        // Remove the event handler from the associated object
        AssociatedObject.RemoveHandler(System.Windows.Controls.Validation.ErrorEvent, _handler);
    }

    private RoutedEventHandler _handler = null;


    private void OnValidationRaised(object sender, RoutedEventArgs e)
    {
        var args = (System.Windows.Controls.ValidationErrorEventArgs)e;

        ViewModelBase viewModel = AssociatedObject.DataContext as ViewModelBase;
        if (viewModel != null)
        {
            // You can add only Exception validation errors if you want..

            if (args.Action == ValidationErrorEventAction.Added)
                viewModel.AddUIValidationError(...);
            else if (args.Action == ValidationErrorEventAction.Removed)
                viewModel.RemoveUIValidationError(...);
            else
                throw new NotSupportedException("ValidationErrorEventAction has changed");
        }
    }
}

最後に XAML で使用します。 1. NotifyDataErrorsBehavior が配置されている名前空間への参照と、System.Windows.Interactivity 名前空間への参照 (Expression Blend SDK から) を追加します。

<UserControl 
        ...
        xmlns:behavior="clr-namespace:MyApp.Behaviors"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        ...
>

2.動作を追加します(UserControlのコンテンツと同じレベルで:

<i:Interaction.Behaviors>
    <behavior:NotifyDataErrorsBehavior/>
</i:Interaction.Behaviors>

元:

<UserControl 
        ...
        xmlns:behavior="clr-namespace:MyApp.Behaviors"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        ...
>
    <i:Interaction.Behaviors>
       <behavior:NotifyDataErrorsBehavior/>
    </i:Interaction.Behaviors>
    <Grid>
       ...
    </Grid>
</UserControl>
于 2012-11-28T02:55:20.193 に答える