ObservableObject
これはObservableObject
、オブジェクトとINotifyPropertyChanging
インターフェイスを実装することによって観察できるオブジェクトのINotifyPropertyChanged
基本クラスです。 これは、プロパティ変更通知をサポートする必要があるあらゆる種類のオブジェクトの開始点として使用できます。
プラットフォーム API:
ObservableObject
,TaskNotifier
,TaskNotifier<T>
しくみ
ObservableObject
には、次の主な機能があります。
- これは、イベントと
INotifyPropertyChanging
イベントを公開するためのINotifyPropertyChanged
基本実装をPropertyChanged
PropertyChanging
提供します。 - 継承する型からプロパティ値を簡単に設定し、適切なイベントを
ObservableObject
自動的に発生させるために使用できる一連SetProperty
のメソッドが用意されています。 - このメソッドは、プロパティを
SetPropertyAndNotifyOnCompletion
SetProperty
設定Task
し、割り当てられたタスクが完了したときに自動的に通知イベントを発生させる機能に似ています。 - 通知イベントの発生方法を
OnPropertyChanged
カスタマイズするために、派生型でオーバーライドできるメソッドとOnPropertyChanging
メソッドが公開されます。
Simple プロパティ
カスタム プロパティに通知サポートを実装する方法の例を次に示します。
public class User : ObservableObject
{
private string name;
public string Name
{
get => name;
set => SetProperty(ref name, value);
}
}
指定された SetProperty<T>(ref T, T, string)
メソッドは、プロパティの現在の値をチェックし、異なる場合は更新し、関連するイベントも自動的に発生させます。 プロパティ名は属性を [CallerMemberName]
使用して自動的にキャプチャされるため、更新するプロパティを手動で指定する必要はありません。
観測不可能なモデルをラップする
たとえば、データベース項目を操作する場合の一般的なシナリオは、データベース モデルのプロパティを中継し、必要に応じてプロパティ変更通知を発生させるラップ "バインド可能" モデルを作成することです。 これは、インターフェイスを実装 INotifyPropertyChanged
していないモデルに通知サポートを挿入する場合にも必要です。 ObservableObject
は、このプロセスをより簡単にする専用のメソッドを提供します。 次の例では、 User
継承せずにデータベース テーブルを直接マッピングする ObservableObject
モデルです。
public class ObservableUser : ObservableObject
{
private readonly User user;
public ObservableUser(User user) => this.user = user;
public string Name
{
get => user.Name;
set => SetProperty(user.Name, value, user, (u, n) => u.Name = n);
}
}
この場合、オーバーロードを SetProperty<TModel, T>(T, T, TModel, Action<TModel, T>, string)
使用しています。 シグネチャは前の署名よりも少し複雑です。これは、前のシナリオのようなバッキング フィールドにアクセスできない場合でも、コードを非常に効率的にするために必要です。 このメソッドシグネチャの各部分を詳しく見て、さまざまなコンポーネントの役割を理解することができます。
TModel
は、ラップしているモデルの型を示す型引数です。 この場合は、クラスUser
になります。 これを明示的に指定する必要はありません。C# コンパイラでは、メソッドの呼び出しSetProperty
方法によって自動的に推論されます。T
は、設定するプロパティの型です。 同様にTModel
、これは自動的に推論されます。T oldValue
は最初のパラメーターであり、この場合はラップしているそのプロパティの現在の値を渡すために使用user.Name
しています。T newValue
はプロパティに設定する新しい値であり、ここではプロパティセッター内の入力値である渡value
しています。TModel model
はラップしているターゲット モデルです。この場合、フィールドに格納されているインスタンスをuser
渡します。Action<TModel, T> callback
は、プロパティの新しい値が現在の値と異なり、プロパティを設定する必要がある場合に呼び出される関数です。 これは、ターゲット モデルと設定する新しいプロパティ値を入力として受け取るこのコールバック関数によって行われます。 この場合は、入力値(呼び出n
した値)をプロパティにName
割り当てます(そうu.Name = n
することで)。 ここでは、現在のスコープから値をキャプチャしないようにし、コールバックへの入力として指定されたものとのみ対話することが重要です。これにより、C# コンパイラはコールバック関数をキャッシュし、いくつかのパフォーマンス向上を実行できます。 このため、ここでフィールドまたはvalue
セッター内のパラメーターに直接アクセスuser
するだけでなく、ラムダ式の入力パラメーターのみを使用しています。
このメソッドは SetProperty<TModel, T>(T, T, TModel, Action<TModel, T>, string)
、非常にコンパクトな API を提供しながら、ターゲット プロパティの取得と設定の両方を処理するため、これらのラップ プロパティを非常に簡単に作成します。
注意
LINQ 式を使用したこのメソッドの実装と比較して、特に状態パラメーターとコールバック パラメーターではなく型 Expression<Func<T>>
のパラメーターを使用することで、この方法で実現できるパフォーマンスの向上は非常に重要です。 特に、このバージョンは LINQ 式を使用するバージョンよりも約 200 倍高速であり、メモリ割り当てをまったく行いません。
プロパティの処理Task<T>
プロパティがある場合は Task
、タスクが完了したら通知イベントも発生させ、バインディングが適切なタイミングで更新されるようにする必要があります。たとえば、タスクによって表される操作に読み込みインジケーターやその他の状態情報を表示します。 ObservableObject
には、このシナリオの API があります。
public class MyModel : ObservableObject
{
private TaskNotifier<int>? requestTask;
public Task<int>? RequestTask
{
get => requestTask;
set => SetPropertyAndNotifyOnCompletion(ref requestTask, value);
}
public void RequestValue()
{
RequestTask = WebService.LoadMyValueAsync();
}
}
ここでは、メソッドは SetPropertyAndNotifyOnCompletion<T>(ref TaskNotifier<T>, Task<T>, string)
ターゲット フィールドの更新、新しいタスク (存在する場合) の監視、そのタスクの完了時に通知イベントの発生を処理します。 これにより、タスク プロパティにバインドするだけで、状態が変更されたときに通知を受け取ることができます。 これはTaskNotifier<T>
、ターゲット Task<T>
インスタンスをラップし、このメソッドに必要な通知ロジックを有効にすることによってObservableObject
公開される特殊な型です。 この TaskNotifier
型は、一般 Task
のみがある場合にのみ直接使用することもできます。
注意
このSetPropertyAndNotifyOnCompletion
メソッドは、パッケージからのMicrosoft.Toolkit
型の使用法をNotifyTaskCompletion<T>
置き換えることを目的とします。 この型が使用されていた場合は、内部 Task
(または Task<TResult>
) プロパティのみに置き換えることができます。その後、メソッドを SetPropertyAndNotifyOnCompletion
使用して値を設定し、通知の変更を発生させることができます。 型によって NotifyTaskCompletion<T>
公開されるすべてのプロパティは、インスタンスで Task
直接使用できます。