39

我不精通基于事件的编程。基本上,我仍然在纠结。我正在尝试设置一些东西,但即使有教程,我也无法理解它。我想做的(用文字)如下:

  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 在哪里分配?
  • 任务是什么样的?
4

4 回答 4

43

您必须触发该事件。在 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)

其中第一个参数必须是 object 类型并表示触发事件的对象,第二个参数包含此事件的参数。在这种情况下,您自己的类会触发事件并因此this作为参数给出sender。第二个参数包含已更改的属性的名称。

现在为了能够对事件的触发做出反应,您必须为该类分配一个事件处理程序。在这种情况下,您必须在您的addChatter方法中分配它。除此之外,您必须首先定义您的处理程序。在你的NosyClass你必须添加一个方法来做到这一点,例如:

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

如您所见,此方法对应于我之前解释的签名。在第二个参数中,您将能够找到更改了哪个参数的信息。最后要做的是添加事件处理程序。现在在你的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 页面的链接,其中包含示例)。

于 2012-08-20T09:23:20.123 回答
14

您查看的链接适用于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));
   }
于 2012-08-20T09:11:46.080 回答
6

为什么这不只是调用 PropertyChanged(this, new PropertyCHangedEventArgs(name))

因为如果没有人将处理程序附加到事件,则PropertyChanged对象返回null. 因此,在调用它之前,您必须确保它不为空。

PropertyChanged 在哪里分配?

在“听众”课程中。

例如,您可以在其他类中编写:

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

任务是什么样的?

在 C# 中,我们使用赋值运算符+=-=事件。我建议阅读以下文章以了解如何使用匿名方法形式(上面的示例)和“旧”形式编写事件处理程序。

于 2012-08-20T09:23:09.707 回答
3

从获取原始代码并结合@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);
    }
}
于 2015-08-18T13:36:49.107 回答