我并不精通基于事件的编程。基本上,我仍然在磕磕碰碰。我正在尝试设置一些东西,但即使有教程,我也无法理解。我想做的(用文字)如下:
-
我有一个属性更改的数据对象。我在属性的setter中注意到了这一点,并希望引发属性已更改的事件。
-
在其他地方(在一个完全不同的类中),我想知道这个对象的属性已经更改,并采取一些操作。
现在我确信这是一个足够常见的场景,但我的谷歌功能让我失望了。我只是不理解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在哪里分配
- 作业是什么样子的
您必须触发事件。在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)
其中,第一个参数必须是对象类型,并表示引发事件的对象,第二个参数包含此事件的参数。在这种情况下,您自己的类会触发事件,从而将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页面链接,并附上示例)。
您查找的链接是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));
}
为什么这不只是调用PropertyChanged(this,newPropertyCHangedEventArgs(名称))
因为如果没有人将处理程序附加到事件,那么PropertyChanged
对象将返回null
。所以在调用它之前,你必须确保它不是空的
PropertyChanged在哪里分配?
在"listener"类中。
例如,您可以在其他类中编写:
ChattyClass tmp = new ChattyClass();
tmp.PropertyChanged += (sender, e) =>
{
Console.WriteLine(string.Format("Property {0} has been updated", e.PropertyName));
};
作业是什么样子的?
在C#中,我们对事件使用赋值运算符+=
和-=
。我建议阅读下面的文章,了解如何使用匿名方法表单(上面的示例)和"旧"表单编写事件处理程序。
从原始代码,并结合@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);
}
}