在.Net Framework 4引入了dynamic關鍵字。它是在運行時才確定對象的類型。在運行的時候確定類型的好處是,少了一些裝箱,拆箱操作。
WPF中也有動態對象概念,那就是DynamicObject,它繼承於IDynamicMetaObjectProvider這個接口。DynamicObject這個類能實現動態的給屬性賦值和取值。它提供給我們兩個TrySetMember和TryGetMember方法。我們只要重寫這兩個方法,來設置我們需要的屬性。
我們自定義一個DynamicBindingProxy泛型類:
public class DynamicBindingProxy<T> : DynamicObject, INotifyPropertyChanged { private static readonly Dictionary<string, Dictionary<string, PropertyInfo>> properties = new Dictionary<string, Dictionary<string, PropertyInfo>>();//存放T 的屬性 private readonly T _instance; private readonly string _typeName; public DynamicBindingProxy(T instance) { _instance = instance; var type = typeof(T); _typeName = type.FullName; if (!properties.ContainsKey(_typeName)) SetProperties(type, _typeName); } private static void SetProperties(Type type, string typeName) { var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); var dict = props.ToDictionary(prop => prop.Name); properties.Add(typeName, dict); } public override bool TryGetMember(GetMemberBinder binder, out object result) { if (properties[_typeName].ContainsKey(binder.Name)) { result = properties[_typeName][binder.Name].GetValue(_instance, null); return true; } result = null; return false; } public override bool TrySetMember(SetMemberBinder binder, object value) { if (properties[_typeName].ContainsKey(binder.Name)) { properties[_typeName][binder.Name].SetValue(_instance, value, null); if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(binder.Name)); } return true; } return false; } public event PropertyChangedEventHandler PropertyChanged; }
定義一個我們需要的實體類:
public class Person { public string Name { get; set; } public int Age { get; set; } public DateTime Birthday { get; set; } }
實現動態屬性變更:
public partial class MainWindow : Window { private Person _person; private DynamicBindingProxy<Person> _bindingProxy; public MainWindow() { InitializeComponent(); _person=new Person(){Name = "xiaoli",Age = 23,Birthday = DateTime.Now}; _bindingProxy=new DynamicBindingProxy<Person>(_person);//動態綁定實體屬性 DataContext = _bindingProxy; } private void UpdateName(object sender, RoutedEventArgs e) { ((dynamic)_bindingProxy).Name = newText.Text; } }
xaml:
<Grid HorizontalAlignment="Left" VerticalAlignment="Top"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <UniformGrid Rows="4" Columns="2"> <TextBlock TextWrapping="Wrap"><Run Text="Name"/></TextBlock> <TextBox TextWrapping="Wrap" Text="{Binding Name}"/> <TextBlock TextWrapping="Wrap"><Run Text="Age"/></TextBlock> <TextBox TextWrapping="Wrap" Text="{Binding Age}"/> <TextBlock TextWrapping="Wrap"><Run Text="Birthday"/></TextBlock> <TextBox TextWrapping="Wrap" Text="{Binding Birthday}"/> </UniformGrid> <StackPanel HorizontalAlignment="Left" Orientation="Horizontal" Grid.Row="1" Margin="0,10,0,0"> <TextBox TextWrapping="Wrap" Margin="0,0,10,0" Width="150" Name="newText"/> <Button Content="Change Name" Click="UpdateName" /> </StackPanel> </Grid>
這樣就保持了我們的Entity是一個POCO了。
很多時候,我們修改了我們的屬性。這時候我們要知道這些屬性是否被修改過了,即已經Dirty了。我們在我們定義的DynamicBindingProxy類中增加一個Changes集合來存儲變更的屬性。然后在重寫的TrySetMember方法中增加那些被更改的屬性。
public class DynamicBindingProxy<T> : DynamicObject, INotifyPropertyChanged { private static readonly Dictionary<string, Dictionary<string, PropertyInfo>> properties = new Dictionary<string, Dictionary<string, PropertyInfo>>();//存放T 的屬性 private readonly T _instance; private readonly string _typeName;
public Dictionary<string, object> Changes { get; private set; }
//存儲變更的屬性
public bool IsDirty { get { return Changes.Count > 0; } }
//重置臟數據 public void Reset() { Changes.Clear(); NotifyPropertyChanged("IsDirty"); } public DynamicBindingProxy(T instance) { _instance = instance; var type = typeof(T); _typeName = type.FullName; if (!properties.ContainsKey(_typeName)) SetProperties(type, _typeName); Changes = new Dictionary<string, object>(); } private static void SetProperties(Type type, string typeName) { var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); var dict = props.ToDictionary(prop => prop.Name); properties.Add(typeName, dict); } public override bool TryGetMember(GetMemberBinder binder, out object result) { if (properties[_typeName].ContainsKey(binder.Name)) { result = properties[_typeName][binder.Name].GetValue(_instance, null); return true; } result = null; return false; } public override bool TrySetMember(SetMemberBinder binder, object value) { if (properties[_typeName].ContainsKey(binder.Name)) { properties[_typeName][binder.Name].SetValue(_instance, value, null);
Changes[binder.Name] = value;
NotifyPropertyChanged(binder.Name); return true; } return false; } private void NotifyPropertyChanged(string name) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(name)); } } public event PropertyChangedEventHandler PropertyChanged; }
代碼下載