DynamicObject 在WPF 中的应用

在.Net Framework 4引入了dynamic关键字。它是在运行时才确定对象的类型。在运行的时候确定类型的好处是,少了一些装箱,拆箱操作。

WPF中也有动态对象概念,那就是DynamicObject,它继承于IDynamicMetaObjectProvider这个接口。DynamicObject这个类能实现动态的给属性赋值和取值。它提供给我们两个TrySetMemberTryGetMember方法。我们只要重写这两个方法,来设置我们需要的属性。

我们自定义一个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;
    }

 

代码下载
posted @ 2012-06-14 00:20  Lee's Blog  阅读(3581)  评论(3编辑  收藏  举报