目录

 

1.关于INotifyPropertyChanged

2.自定义同步事件

3.集合的同步 ObservableCollection


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中的数据绑定

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐