zoukankan      html  css  js  c++  java
  • WPF之数据绑定基类

    数据绑定方法

    在使用集合类型作为列表控件的ItemsSource时一般会考虑使用ObservalbeCollection,它实现了INotifyCollectionChangedINotifyPropertyChanged接口,能把集合的变化立刻通知显示它的列表控件,改变会立刻显现出来。

    在使用自定义类型作为界面的数据源时,自定义类型需要自己实现INotifyPropertyChanged接口,一般会把INotifyPropertyChanged接口的实现放到一个基类中。

    BindableBase基类

    下面的BindableBase类来自Prism,代码如下:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    
    /// <summary>
    /// 实现 <see cref="INotifyPropertyChanged"/>
    /// </summary>
    public abstract class BindableBase : INotifyPropertyChanged
    {
        /// <summary>
        /// 属性值更改时发生
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;
    
        /// <summary>
        /// 检查属性是否已与设置值相等,设置属性并仅在必要时通知侦听器。
        /// </summary>
        /// <typeparam name="T">属性的类型</typeparam>
        /// <param name="storage">对同时具有getter和setter的属性的引用</param>
        /// <param name="value">属性的所需值</param>
        /// <param name="propertyName">用于通知侦听器的属性的名称,此值是可选的,从支持CallerMemberName的编译器调用时可以自动提供。</param>
        /// <returns>如果值已更改,则为True;如果现有值与所需值匹配,则为false。</returns>
        protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
        {
            if (EqualityComparer<T>.Default.Equals(storage, value)) return false;
    
            storage = value;
            RaisePropertyChanged(propertyName);
    
            return true;
        }
    
        /// <summary>
        /// 检查属性是否已与设置值相等,设置属性并仅在必要时通知侦听器。
        /// </summary>
        /// <typeparam name="T">属性的类型</typeparam>
        /// <param name="storage">对同时具有getter和setter的属性的引用</param>
        /// <param name="value">属性的所需值</param>
        /// <param name="propertyName">用于通知侦听器的属性的名称,此值是可选的,从支持CallerMemberName的编译器调用时可以自动提供。</param>
        /// <param name="onChanged">属性值更改后调用的操作。</param>
        /// <returns>如果值已更改,则为True;如果现有值与所需值匹配,则为false。</returns>
        protected virtual bool SetProperty<T>(ref T storage, T value, Action onChanged, [CallerMemberName] string propertyName = null)
        {
            if (EqualityComparer<T>.Default.Equals(storage, value)) return false;
    
            storage = value;
            onChanged?.Invoke();
            RaisePropertyChanged(propertyName);
    
            return true;
        }
    
        /// <summary>
        /// 引发此对象的PropertyChanged事件。
        /// <param name="propertyName">用于通知侦听器的属性的名称,此值是可选的,从支持CallerMemberName的编译器调用时可以自动提供。</param>
        protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
        {
            OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
        }
    
        /// <summary>
        /// 引发此对象的PropertyChanged事件。
        /// </summary>
        /// <param name="args">PropertyChangedEventArgs参数</param>
        protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
        {
            PropertyChanged?.Invoke(this, args);
        }
    }
    

    上面的代码有两点需要注意:

    • 使用EqualityComparer .Default属性创建通用比较,而不是使用Object.Equals()。Default属性检查类型T是否实现System.IEquatable(Of T)接口,如果是,则返回使用该实现的EqualityComparer(Of T)。否则,它返回一个EqualityComparer(Of T),它使用T提供的Object.Equals和Object.GetHashCode的替代。

    • 使用CallerMemberName特性代替常规的属性名称(也可以使用nameof()运算符关键字来传递属性名称),CallerMemberNameAttribute 类允许获取方法调用方的方法或属性名称,而不必使用字符串文字、 慢速反射代码、复杂的表达式树逻辑或代码编织。

    使用BindableBase

    实现一个StudentViewModel类(继承BindableBase),代码如下

    class StudentViewModel : BindableBase
    {
        private int _id;
        public int Id
        {
            get { return _id; }
            set { SetProperty(ref _id, value); }
        }
    
        private string _name;
        public string Name 
        {
            get { return _name; }
            set { SetProperty(ref _name, value,new Action(()=> { Id++; })); }
        }
    
        private int _age;
        public int Age
        {
            get { return _age; }
            set { SetProperty(ref _age, value,nameof(this.Age)); }
        }
    }
    

    主界面的XAML代码如下:

    <StackPanel x:Name="stack" Background="LightBlue">
        <StackPanel.DataContext>
            <local:StudentViewModel Id="6" Age="29" Name="Tim"/>
        </StackPanel.DataContext>
        <Grid>
            <StackPanel>
                <TextBox Text="{Binding Path=Id}" Margin="5"/>
                <TextBox Text="{Binding Path=Name}" Margin="5"/>
                <TextBox Text="{Binding Path=Age}" Margin="5"/>
                <Button Content="Age+" Click="Button_Click"/>
            </StackPanel>
        </Grid>
    </StackPanel>
    

    主界面的后台实现Button_Click,代码如下:

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        ((StudentViewModel)this.stack.DataContext).Age ++;
    }
    

    运行程序,查看数据绑定的效果:

    参考资料

    PrismLibrary/Prism/BindableBase.cs
    INotifyPropertyChanged,.NET 4.6方式
    MVVM模式解析和在WPF中的实现(二)数据绑定

  • 相关阅读:
    部署kube-prometheus,添加邮件报警
    kubernetes1.8开启swagger-ui
    使用alien命令让deb包和rpm包互相转换
    debian开启cgroup memory子系统
    debian9使用systemd部署etcd集群
    装饰器
    mysql根据经纬度求两地距离
    使用rem编写自适应屏幕网页造成div被span撑高的解决办法
    java绘图合并图像AlphaComposite模式测试
    spring data jpa查询部分字段、多余附加字段
  • 原文地址:https://www.cnblogs.com/timefiles/p/WpfBindableBase.html
Copyright © 2011-2022 走看看