xaml mvvm(2)之属性绑定
通过微软INotifyPropertyChanged接口,可以实现对UI实时更新,不管是数据源或者目标对象,可以实现相互通知。
下面我们根据INotifyPropertyChanged编写一个扩展类。该类是基于C#5.0特性,这里我们介绍一下System.Runtime.CompilerServices命名空间下的CallerMemberName特性,当RaisePropertyChanged的属性名称参数为空,而通过编译器可以智能加上,可以通过反编译工具知晓,这点改进这点很人性化。注:如果开发版本framework 4.0,则需要安装KB2468871补丁或者更新framework 4.0以上版本。
为这个扩展类添加命名空间如下。
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Diagnostics; 5 using System.Linq.Expressions; 6 using System.Reflection; 7 using System.Runtime.CompilerServices;
1 public class ObservableObject : INotifyPropertyChanged 2 { 3 public event PropertyChangedEventHandler PropertyChanged; 4 protected PropertyChangedEventHandler PropertyChangedHandler 5 { 6 get 7 { 8 return this.PropertyChanged; 9 } 10 } 11 [Conditional("DEBUG"), DebuggerStepThrough] 12 public void VerifyPropertyName(string propertyName) 13 { 14 Type type = base.GetType(); 15 if (!string.IsNullOrEmpty(propertyName) && type.GetTypeInfo().GetDeclaredProperty(propertyName) == null) 16 { 17 throw new ArgumentException("Property not found", propertyName); 18 } 19 } 20 protected virtual void RaisePropertyChanged([CallerMemberName] string propertyName = null) 21 { 22 PropertyChangedEventHandler propertyChanged = this.PropertyChanged; 23 if (propertyChanged != null) 24 { 25 propertyChanged(this, new PropertyChangedEventArgs(propertyName)); 26 } 27 } 28 protected virtual void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpression) 29 { 30 PropertyChangedEventHandler propertyChanged = this.PropertyChanged; 31 if (propertyChanged != null) 32 { 33 string propertyName = ObservableObject.GetPropertyName<T>(propertyExpression); 34 propertyChanged(this, new PropertyChangedEventArgs(propertyName)); 35 } 36 } 37 protected static string GetPropertyName<T>(Expression<Func<T>> propertyExpression) 38 { 39 if (propertyExpression == null) 40 { 41 throw new ArgumentNullException("propertyExpression"); 42 } 43 MemberExpression memberExpression = propertyExpression.Body as MemberExpression; 44 if (memberExpression == null) 45 { 46 throw new ArgumentException("Invalid argument", "propertyExpression"); 47 } 48 PropertyInfo propertyInfo = memberExpression.Member as PropertyInfo; 49 if (propertyInfo == null) 50 { 51 throw new ArgumentException("Argument is not a property", "propertyExpression"); 52 } 53 return propertyInfo.Name; 54 } 55 protected bool Set<T>(Expression<Func<T>> propertyExpression, ref T field, T newValue) 56 { 57 if (EqualityComparer<T>.Default.Equals(field, newValue)) 58 { 59 return false; 60 } 61 field = newValue; 62 this.RaisePropertyChanged<T>(propertyExpression); 63 return true; 64 } 65 protected bool Set<T>(string propertyName, ref T field, T newValue) 66 { 67 if (EqualityComparer<T>.Default.Equals(field, newValue)) 68 { 69 return false; 70 } 71 field = newValue; 72 this.RaisePropertyChanged(propertyName); 73 return true; 74 } 75 protected bool Set<T>(ref T field, T newValue, [CallerMemberName] string propertyName = null) 76 { 77 return this.Set<T>(propertyName, ref field, newValue); 78 } 79 }
下面我们来继承这个类编写一个Animal类对象。在这里我们用到RaisePropertyChanged三种不同的实现方式,达到一样的绑定效果。
1 public class Animal : ObservableObject 2 { 3 private string m_Cat; 4 public string Cat 5 { 6 get { return m_Cat; } 7 set { m_Cat = value; RaisePropertyChanged("Cat"); } 8 } 9 10 private string m_Dog; 11 public string Dog 12 { 13 get { return m_Dog; } 14 set { m_Dog = value; RaisePropertyChanged(); } 15 } 16 17 private string m_Tiger; 18 public string Tiger 19 { 20 get { return m_Tiger; } 21 set { m_Tiger = value; RaisePropertyChanged(() => this.Tiger); } 22 } 23 }
下面我们来建立model视图类。在该类中用的事件绑定和model对象实现,我们会在后续介绍。
1 public class MainPageViewModel : Core.ViewModelBase 2 { 3 public MainPageViewModel() 4 { 5 MyAnimal = new Animal(); 6 } 7 8 private Animal m_MyAnimal; 9 public Animal MyAnimal 10 { 11 get { return m_MyAnimal; } 12 set { m_MyAnimal = value; RaisePropertyChanged("MyAnimal"); } 13 } 14 15 public ICommand OKCommand 16 { 17 get 18 { 19 return new RelayCommand(() => 20 { 21 MyAnimal.Dog = "eating"; 22 MyAnimal.Cat = "sleeping"; 23 MyAnimal.Tiger = "hungry"; 24 }); 25 } 26 } 27 }
前台xaml。
1 <Grid DataContext="{Binding Path=MainPageViewModel}"> 2 <StackPanel> 3 <StackPanel Orientation="Horizontal"> 4 <TextBlock FontSize="25" Text="cat is:" /> 5 <TextBlock FontSize="25" Text="{Binding MyAnimal.Cat}" /> 6 </StackPanel> 7 <StackPanel Orientation="Horizontal"> 8 <TextBlock FontSize="25" Text="dog is:" /> 9 <TextBlock FontSize="25" Text="{Binding MyAnimal.Dog}" /> 10 </StackPanel> 11 <StackPanel Orientation="Horizontal"> 12 <TextBlock FontSize="25" Text="Tiger is:" /> 13 <TextBlock FontSize="25" Text="{Binding MyAnimal.Tiger}" /> 14 </StackPanel> 15 <Button Width="60" Content="OK" Command="{Binding OKCommand}" /> 16 </StackPanel> 17 </Grid>
运行效果。