(再次叹一下中国的网络环境,搜出来的网页一大堆,可有用的没几个,基本是大家相互转,真正有了问题楞是找不到能解决的)
首先如果是基本类型的变量,或者是自定义的类,直接绑定到控件之后,控件只能显示其初始值,值的改变并不能更新UI,只有以下两种情况的绑定:
①绑定到某个控件的依赖属性DependencyProperty
②绑定到定义了INotifyPropertyChanged的类(ObservableCollection就是实现了该接口)
先看DependencyProperty。
可以把任何一个CLR对象绑定为DependencyProperty。在VS2010下输入propdp,按Tab,会生成一个模板
public int MyProperty { get { return (int)GetValue(MyPropertyProperty); } set { SetValue(MyPropertyProperty, value); } } // Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc... public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register("MyProperty", typeof(int), typeof(ownerclass), new UIPropertyMetadata(0));
MyProperty就是你要绑定的成员,修改它之后按Tab,后面的自动都会改变,如:
public string Test { get { return (string)GetValue(TestProperty); } set { SetValue(TestProperty, value); } } // Using a DependencyProperty as the backing store for Test. This enables animation, styling, binding, etc... public static readonly DependencyProperty TestProperty = DependencyProperty.Register("Test", typeof(string), typeof(ownerclass), new UIPropertyMetadata(0));
这里的ownerclass是拥有此成员的类名(如:class ownerclass {//...})。最后一个参数VS自动生成的有问题,因为它放入了一个参数0。实际上应该是UIPropertyMetadata(Object, PropertyChangedCallback, CoerceValueCallback, Boolean),用于设置UI中的数据更改后回调的函数。一般删除之或者用默认构造函数就行。如果需要它,一个简单的定义如下:
public static readonly DependencyProperty IsNetworkChangedProperty = DependencyProperty.Register("IsNetworkChanged", typeof(bool), typeof(MainWindow), new UIPropertyMetadata(false, new PropertyChangedCallback(MainWindow.OnIsNetworkChanged))); private static void OnIsNetworkChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { //MessageBox.Show(d.GetType().ToString()); }
注意这里是静态函数,由DependencyObject d来得到具体的通知对象。
然后在控件中进行绑定,如
注意:①使用DependencyProperty比INotifyPropertyChanged的方法性能要高。因为它用Hash实现,不需要反射,而且是WPF系统中相当底层的一个基类
②DependencyObjects are not marked as serializable
③The DependencyObject class overrides and seals the Equals() and GetHashCode() methods
④A DependencyObject has thread affinity – it can only be accessed on the thread on which it was created。(这一点很重要,特别是当程序中用到注册事件时,因为这些往往要开新线程。比如我的这个软件中,将bool型IsNetworkAvailabe用DependencyProperty绑定到控件,然后注册事件NetworkAvailabilityChanged,在NetworkAvailabilityChangedEventHandler中更新IsNetworkAvailabe值,这样就会抛出异常)
在需要多线程 中操作的绑定变量,则需要用INotifyPropertyChanged
再看如何用INotifyPropertyChanged:
这里要将待绑定的变量包装在一个类中:
class MyTest : INotifyPropertyChanged { private string test; public string Test { get { return this.test; } set { if (this.test != value) { this.test = value; this.NotifyPropertyChanged("Test"); } } } public event PropertyChangedEventHandler PropertyChanged; private void NotifyPropertyChanged(String info) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(info)); } } }
其中test就是我们要绑定的变量。(绑定一个变量写这么多一堆,有点不值,我另开一篇,做一个snippet模板,用来快速生成)。
绑定时不知为何上面的绑定方法不灵了,于是换成代码绑定吧:
MyTest Test; public MainWindow() { InitializeComponent(); this.Test = new MyTest(); Test.test = "thy"; TestBox.DataContext = this.Test; }
最终,在我的应用场景中,这两者都没能有用,我注册了事件NetworkAvailabilityChanged,在其中去修改一个绑定到
ListBox的ObservableCollection,让其实现UI自动更新。但是总是会引起异常,我想可能是跨线程的原因吧。于是我试
着用DependencyProperty绑定一个变量,在NetworkAvailabilityChanged事件中修改,还是不行,
INotifyPropertyChanged也一样,BackgroundWorker也不行。最终没辙,在MainWindow中开了一个DispatcherTimer来轮
询一个普通的bool变量:
private DispatcherTimer _timer = new DispatcherTimer(DispatcherPriority.ContextIdle); _timer.Interval = TimeSpan.FromMilliseconds(100); _timer.Tick += new EventHandler(_timer_Tick); _timer.Start();
虽说比较丑,但没法子啊,没这么多精力再去整了,先让它工作起来吧。