C# 变量同步(binding)——利用属性和事件
目录1.关于INotifyPropertyChanged2.自定义同步事件3.集合的同步ObservableCollection1.关于INotifyPropertyChanged在WPF中,常常需要让UI空间绑定变量,当我们在UI中修改值时,这个修改能够同步到对象中去,要实现这个功能,往往需要让这个对象派生自INotifyPropertyChanged 接口,下面给一个例子:public cla
目录
1.关于INotifyPropertyChanged
在WPF中,常常需要让UI空间绑定变量,当我们在UI中修改值时,这个修改能够同步到对象中去,要实现这个功能,往往需要让这个对象派生自INotifyPropertyChanged 接口,下面给一个例子:
public class ExecutionModule: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public int Id { get; set; }
public string Name { get; set; }
private StatusType status;
public StatusType Status
{
get
{
return status;
}
set
{
status = value;
if(PropertyChanged!=null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Status"));
}
}
}
private bool select;
public bool Select
{
get { return select; }
set
{
select = value;
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Name"));
}
}
}
public DateTime UpdateTime { get; set; }
public override string ToString()
{
string ans = Id.ToString() + " " + Name + " " + Status.ToString() + " " + select.ToString();
return ans;
}
}
我定义了一个叫ExecutionModule的类,并让其派生自INotifyPropertyChanged,这个接口位于:System.ComponentModel;
然后再这个类里面定义了一个事件:
public event PropertyChangedEventHandler PropertyChanged;
这个事件的委托类型是:PropertyChangedEventHandler,这个委托的模型是:
public delegate void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e);
它有两个参数,一个是Obejct,一个是PropertyChangedEventArgs ,第二个参数我们简单的看一看:
//
// 摘要:
// Provides data for the System.ComponentModel.INotifyPropertyChanged.PropertyChanged
// event.
public class PropertyChangedEventArgs : EventArgs
{
//
// 摘要:
// Initializes a new instance of the System.ComponentModel.PropertyChangedEventArgs
// class.
//
// 参数:
// propertyName:
// The name of the property that changed.
public PropertyChangedEventArgs(string propertyName);
//
// 摘要:
// Gets the name of the property that changed.
//
// 返回结果:
// The name of the property that changed.
public virtual string PropertyName { get; }
}
在构造函数中的注释里面说到:传递的参数是要改变的属性名字。所以这告诉我们两点:
- 要构造这样一个参数对象,只需要传入一个属性的名字
- 对象绑定的成员需要是属性,而不能是变量
然后再看Status 变量里面的set访问器,
set
{
status = value;
if(PropertyChanged!=null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Status"));
}
}
访问器里面先对status进行赋值,然后再判断委托对象PropertyChanged 是否为空,如果不为空,则执行该委托订阅的方法
这里可以看到直接将绑定的属性名“Status”作为参数传给委托函数。所以每当我们的绑定的对象发生变化时,就会触发set访问器,从而执行委托订阅的与绑定控件有关的方法,这些方法里面一般都有刷新UI的操作,从而可以实时的在界面上显示出变化。
有同学可能要问,委托订阅事件一般都需要“+=”操作,可是在整个开发过程,都没有看到这个符号啊,PropertyChanged里面不应该是空的吗?这个问题曾经也困扰着我!
事实上,当你写下这个绑定时,Framework自动帮你订阅了相关事件。
<DataGridComboBoxColumn Header="当前状态" Width="*" SelectedItemBinding="{Binding Status, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding Source={StaticResource statusEnum}}"></DataGridComboBoxColumn>
简而言之,Framework会检测控件所绑定的对象是否实现了INotifyPropertyChangedzhe'ge这个接口,如果实现了,它将在后台帮你订阅PropertyChanged这个事件,所以我们一般没有必要去看后台是怎么订阅的。
在WPF中为了防止这种订阅方式导致内存泄露,还专门开发了Weak Event Patterns技术,这个技术可以防止事件监听者(EventListener 事件侦听器代表由当前应用程序域中的事件源(EventSource对象)实现生成的所有事件的目标。 创建新的事件侦听器后,它将在逻辑上附加到该应用程序域中的所有事件源。)导致的内存泄露。主要实现了对事件对象和事件订阅的方法更强的管理,防止在事件没有订阅方法时,仍没有销毁(这句话不一定对)。有兴趣的同学可以去查看 官方文档
2.自定义同步事件
如果我们不在WPF中同步,就想在控制台程序实现它,那就需要自己定义事件来实现:
class Program
{
public event EventHandler XChanged;
public int x;
public int X
{
get { return x; }
set
{
x = value;
OnChanged();
}
}
public int Y { get; set; }
public void Subscribe1(object source,EventArgs e)
{
Y = X * X;
}
protected virtual void OnChanged()
{
XChanged?.Invoke(this, EventArgs.Empty);
}
public Program()
{
XChanged += Subscribe1;
}
static void Main(string[] args)
{
Program p = new Program
{
X = 3
};
Console.WriteLine(p.Y);
p.X = 10;
Console.WriteLine(p.Y);
}
}
运行程序,你会发现X变化,Y也自动变了。这里我用的是C#标准事件,当然你也可以自定义事件,核心思想是:在set访问器触发你订阅的事件。下面是一个自定义事件的例子,保证A永远是B+1:
public class Test
{
public delegate void change();
public event change BChange;
public int A { get; set; }
public int b;
public int B { get { return b; } set { b = value; BChange?.Invoke(); } }
public Test()
{
BChange += Add_One;
}
public void Add_One()
{
A = B + 1;
}
}
3.集合的同步 ObservableCollection<T>
在WPF中有时候,控件绑定的不是单个对象,而是一个集合,比如ListView,那么就需要在集合中任一个元素发生变化后,更新这个集合,那么微软给我们提供了一个很方便的泛型集合: ObservableCollection<T>。所以将该集合对象绑定到UI上,就可以实现前面介绍的功能。这里直接看一个例子:
List<Test> list = new List<Test>
{
new Test{ B=1},
new Test{B=3}
};
ObservableCollection<Test> tests = new ObservableCollection<Test>(list);
tests[1].B = 9;
Console.WriteLine(list[1].A); //输出10
list[0].B = 99;
Console.WriteLine(tests[0].A); /输出100
打印的结果是 10,也就是tests集合更新后,list中的数据也更新了,说明list和tests绑定在一起了。而我们常用的list类型和test类型又可以相互转换,所以我们在内部处理逻辑时可以直接用List自带的扩展方法以及linq语句,然后更新会同步到绑定的O贝尔色vableCollection对象。
//List 转 ObeservableCollection<T>
List<T> list=new List<T> {T1,T2};
ObeservableCollection<T> observeble=new ObeservableCollection<T>(list)
// ObservableCollection<T> 转 List
List<T> list=new List<T>(observable.ToList()); //ToList是System.Linq库中的扩展方法
更多关于绑定的文档请参考:WPF中的数据绑定
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)