web-dev-qa-db-ja.com

OnPropertyChangedの処理

私はイベントベースのプログラミングに精通していません。基本的に、私はまだそれでつまずいています。何かを設定しようとしていますが、チュートリアルを使用しても頭を包み込むことはできません。私がやりたいことは(言葉で)以下です:

  1. プロパティが変更されるデータオブジェクトがあります。プロパティのセッターでこれに気付き、プロパティが変更されたイベントを発生させたいと思います。

  2. 他の場所(完全に異なるクラス)で、このオブジェクトのプロパティが変更されたことを知り、何らかのアクションを実行したいと思います。

これは十分に一般的なシナリオであると確信していますが、私のgoogle-fuは私を失望させます。 http://msdn.Microsoft.com/en-us/library/ms743695.aspx を理解していないだけです。

私はこれを持っています:

_public class ChattyClass {
  private int someMember;

  public event PropertyChangedEventHandler PropertyChanged;

  public int SomeMember {
    get {
      return this.someMember;
    }
    set {
      if (this.someMember != value){
        someMember = value;
        // Raise event/fire handlers. But how?
      }
   }
}

public class NosyClass{
  private List<ChattyClass> myChatters;

  public void addChatter(ChattyClass chatter){
    myChatters.add(chatter);
    // Start listening to property changed events
  }

  private void listner(){
    // I want this to be called when the PropertyChangedEvent is called
    Console.WriteLine("Hey! Hey! Listen! A property of a chatter in my list has changed!");
  }
}
_

これを接続するにはどうすればよいですか?

リンクに戻って私を指すコメントについて:

例では、私は見ます:

_protected void OnPropertyChanged(string name)
{
    PropertyChangedEventHandler handler = PropertyChanged;
    if (handler != null)
    {
        handler(this, new PropertyChangedEventArgs(name));
    }
}
_

私が理解していないこと:

  • なぜPropertyChanged(this, new PropertyCHangedEventArgs(name))を呼び出すだけではないのですか
  • PropertyChangedはどこに割り当てられますか?
  • 割り当てはどのように見えますか?
33
Martijn

イベントを発生させる必要があります。 MSDNの例では、保護されたメソッドOnPropertyChangedを作成して、これを簡単に処理します(コードの重複を防ぎます)。

// Create the OnPropertyChanged method to raise the event 
protected void OnPropertyChanged(string name)
{
    PropertyChangedEventHandler handler = PropertyChanged;
    if (handler != null)
    {
        handler(this, new PropertyChangedEventArgs(name));
    }
}

このメソッドは、イベントハンドラが割り当てられているかどうかを確認します(割り当てられていない場合に呼び出すだけで、NullReferenceExceptionが取得されます)。割り当てられている場合は、このイベントハンドラーを呼び出します。提供されるイベントハンドラには、PropertyChangedEventHandlerデリゲートの署名が必要です。この署名は次のとおりです。

void MyMethod(object sender, PropertyChangedEventArgs e)

最初のパラメーターはオブジェクト型でなければならず、イベントを発生させるオブジェクトを表し、2番目のパラメーターにはこのイベントの引数が含まれます。この場合、独自のクラスがイベントを発生させるため、thisをパラメーターsenderとして指定します。 2番目のパラメーターには、変更されたプロパティの名前が含まれます。

ここで、イベントの発生時に反応できるようにするには、イベントハンドラーをクラスに割り当てる必要があります。この場合、addChatterメソッドでこれを割り当てる必要があります。それとは別に、最初にハンドラーを定義する必要があります。 NosyClassに、これを行うためのメソッドを追加する必要があります。例えば:

private void chatter_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    Console.WriteLine("A property has changed: " + e.PropertyName);
}

ご覧のとおり、このメソッドは前に説明した署名に対応しています。 2番目のパラメーターでは、どのパラメーターが変更されたかに関する情報を見つけることができます。最後に、イベントハンドラーを追加します。 addChatterメソッドで、これを割り当てる必要があります。

public void AddChatter(ChattyClass chatter)
{
    myChatters.Add(chatter);
    // Assign the event handler
    chatter.PropertyChanged += new PropertyChangedEventHandler(chatter_PropertyChanged);
}

.NET/C#のイベントに関する何かを読むことをお勧めします: http://msdn.Microsoft.com/en-us/library/awbftdfh これを読んだり学んだりすることで、物事がより明確になると思います。

コンソールアプリケーションを見つけることができます ここではPastebin すぐにテストしたい場合(新しいコンソールアプリケーションにコピー/貼り付けするだけです)。

C#の新しいバージョンでは、イベントハンドラーの呼び出しをインライン化できます。

// inside your setter
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MyProperty)));

Fody PropertyChanged のようなものを使用して、必要なコードを自動的に生成することもできます(サンプル付きのGitHubページへのリンクにアクセスしてください)。

36
Styxxy

ご覧になったリンクは、 [〜#〜] mvvm [〜#〜] パターンおよび [〜#〜] wpf [〜#〜] です。一般的なC#の実装ではありません。次のようなものが必要です。

public event EventHandler PropertyChanged;

    public int SomeMember {
        get {
            return this.someMember;
        }
        set {
            if (this.someMember != value) {
                someMember = value;
                if (PropertyChanged != null) { // If someone subscribed to the event
                    PropertyChanged(this, EventArgs.Empty); // Raise the event
                }
            }
        }

...

public void addChatter(ChattyClass chatter) {
    myChatters.add(chatter);
    chatter.PropertyChanged += listner; // Subscribe to the event
}
// This will be called on property changed
private void listner(object sender, EventArgs e){
    Console.WriteLine("Hey! Hey! Listen! A property of a chatter in my list has changed!");
}

どのプロパティが変更されたかを知りたい場合は、イベント定義を次のように変更する必要があります。

public event PropertyChangedEventHandler PropertyChanged;

呼び出しを次のように変更します。

public int SomeMember {
    get {
        return this.someMember;
    }
    set {
        if (this.someMember != value){
            someMember = value;
            if (PropertyChanged != null) { // If someone subscribed to the event
                PropertyChanged(this, new PropertyChangedEventArgs("SomeMember")); // Raise the event
            }
        }
   }

   private void listner(object sender, PropertyChangedEventArgs e) {
       string propertyName = e.PropertyName;
       Console.WriteLine(String.Format("Hey! Hey! Listen! a {0} of a chatter in my list has changed!", propertyName));
   }
11
Vale

なぜPropertyChanged(this、new PropertyCHangedEventArgs(name))を呼び出すだけではないのですか

誰もハンドラーをイベントにアタッチしなかった場合、PropertyChangedオブジェクトはnullを返すためです。したがって、呼び出す前にnullでないことを確認する必要があります。

propertyChangedはどこに割り当てられますか?

「リスナー」クラス内。

たとえば、他のクラスで書くことができます:

ChattyClass tmp = new ChattyClass();
tmp.PropertyChanged += (sender, e) =>
    {
        Console.WriteLine(string.Format("Property {0} has been updated", e.PropertyName));
    };

割り当てはどのように見えますか?

C#では、イベントに割り当て演算子+=および-=を使用します。 次の記事 を読んで、匿名メソッドフォーム(上記の例)と「古い」フォームを使用してイベントハンドラを記述する方法を理解することをお勧めします。

6
ken2k

元のコードを取得し、@ Styxxyの答えを組み込むことで、次のようになります。

public class ChattyClass  : INotifyPropertyChanged 
{
  private int someMember, otherMember;

  public int SomeMember
  {
      get
      {
          return this.someMember;
      }
      set
      {
          if (this.someMember != value)
          {
              someMember = value;
              OnPropertyChanged("Some Member");
          }
      }
  }

  public int OtherMember
  {
      get
      {
          return this.otherMember;
      }
      set
      {
          if (this.otherMember != value)
          {
              otherMember = value;
              OnPropertyChanged("Other Member");
          }
      }
  }

  protected virtual void OnPropertyChanged(string propertyName)
  {
      PropertyChangedEventHandler handler = PropertyChanged;
      if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
  }

  public event PropertyChangedEventHandler PropertyChanged;

}

public class NosyClass
{
    private List<ChattyClass> myChatters = new List<ChattyClass>();

    public void AddChatter(ChattyClass chatter)
    {
        myChatters.Add(chatter);
        chatter.PropertyChanged+=chatter_PropertyChanged;
    }

    private void chatter_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        Console.WriteLine("A property has changed: " + e.PropertyName);
    }
}
3
Paul Richards