2021-01-18

备注:类关系结构图如下:

2021-01-18

Binding

Binding基础

  1. Binding作用:目的是实时变化,Binding的源是逻辑层的对象,Binding目标是UI层控件对象。

  2. 一个例子:

    class Student: INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private string name;
        public string Name
        {
            get{return name;}
            set
            {
                name = value;
                // 激发事件
                this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Name"))
            }
        }
    }
    
    // xaml中
    <TextBox x:Name="textBoxName" BorderBrush="Black" Margin="5" />
    <Button Content="Add Age" Margin="5" Click="Button_Click" />
    
    // xaml对应的cs中
    Student stu;
    public Window1()
    {
        InitialComponent();
    
        // 准备数据源
        stu = new Student();
    
        // 准备Binding
        Binding binding = new Binding();
        binding.Source = stu;
        binding.Path = new PropertyPath("Name");
    
        // 使用Binding链接数据源和Binding目标
        BindingOperations.SetBinding(this.textBoxName, TextBox.TextProperty, binding);
    
        // 备注:上面这些也可以合并成为一句
        this.textBoxName.SetBinding(TextBox.TextProperty, new Binding("Name"){Source = stu = new Student()});
    }
    
    private void Button_Click(object sender, RoutedEventArgs e)
    {
        stu.Name += "Name";
    }
    
    • 效果展示:
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TKigaQJu-1610970699025)(./images/06_01.png )]
  3. 模型:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OXI6swNl-1610970699029)(./images/06_02.png )]

Binding的源和路径

备注:上面写的Student就可以作为源,下面讨论不同情况下的源。

1. 把控件作为Binding源与Binding标记扩展

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SsOAf6h2-1610970699039)(./images/06_03.png )]
备注:

  • 在C#代码中可以访问XAML代码中声明的变量但XAML代码中无法访问C#代码中声明的变量,因此,想要在XAML中建立UI元素和逻辑层对象的Binding还要颇费周折,把逻辑层对象声明为XAML代码中资源,然后使用资源。
  • 上面也可简写成如下,参数->构造函数:
    <TextBox x:Name="textBox1" Text="{Binding Value, ElementName=slider1}" BorderBrush="Black" Margin="5" />
  • 与之等价的C#代码是
    this.textBox1.SetBinding(TextBox.TextProperty, new Binding("Value"){ElementName="slider1"});
2. 控制Binding的方向及数据更新
  • 控制Binding数据流向的属性是Mode,他的类型是BindingMode枚举。BindingMode可取值为TwoWay、OneWay、OneWayToSource和Default。这里Default值是指Binding的模式会根据目标的实际情况来确定,比如若是可编辑的(如TextBox.Text属性),Default就采用双向模式;若是只读的(如TextBlock.Text)则采用单向模式。
  • Binding的另一个属性—UpdateSourceTrigger,他的类型是UpdateSourceTrigger枚举,可取值为PropertyChanged、LostFocus、Explicit和Default。
3. Binding的路径(Path)
  • 一般情况下:
    <TextBox x:Name="textBox1" Text="{Binding Path=Value, ElementName=slider}">
    等效的C#代码是:
    Binding binding = new Binding(){Path=new PropertyPath("Value"), Source=this.slider};
    this.textBox1.SetBinding(TextBox.TextProperty, binding);
    或者:
    this.textBox1.SetBinding(TextBox.TextProperty, new Binding("Value"){Source=this.slider});
    
  • 文本长度
    Text.Length 替代上面的 Value
    备注:同时 Mode="OneWay"  和 Source位置一样,作为字段名赋值
    
  • 集合类型的索引器又称为带参属性,既然是属性,索引器也能作为Path来使用。
    Text.[3] 或者 Text[3]
4. 没有path的binding

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q7OOVE48-1610970699042)(./images/06_04.png )]

备注:

  • Binding源本身就是数据且不需要path来指定,典型的string、int等基本类型就是这样。
  • 所以在xaml代码中可以写".“也可以不写,但是在C#代码中”."不能省略。
  • 上面代码在C#代码中如下:
    // xaml中不写如下
    Text = "{Binding Source={StaticResource ResourceKey = myString}}"
    // C#如下
    string myString = "菩提本无树,明镜亦非台。本来无一物,何处染尘埃。"
    this.textBlock.SetBinding(TextBlock.TextProperty, new Binding("."){Source= myString});
    
5. 没有Source的Binding–使用DataContext作为Binding源
  • DataContext属性被定义在FrameworkElement类里,这个类是WPF控件的基类,所以UI元素树的每个节点都有DataContext。

  • 当一个Binding只知道自己的Path而不知道自己的Source时,他会沿着UI元素树一路向书的根部找过去。

  • 如果找到根部还没有找到Path对应的Source,那这个Binding就没有Source,因而也不会得到数据。

  • 示例代码:

    public class Student
    {
      public int Id{get; set;}
      public string Name{get; set;}
      public int Age{get; set;}          
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gWiKQKw1-1610970699044)(./images/06_05.png )]

  • 当Binding的Source本身就是数据,不需要使用属性来暴露数据,Binding的Path可以设置为“.”,也可以省略不写,同时Source也可以省略不写,所以有如下:

    <StackPanel>
        <TextBlock Text="{Binding}" Margin="5" />
        <TextBlock Text="{Binding}" Margin="5" />
        <TextBlock Text="{Binding}" Margin="5" />
    </StackPanel>
    
6. 使用集合对象作为列表控件的 ItemSource

备注:

  • WPF中列表控件们派生自 ItemsControl 类,自然也就继承了 ItemsSource 属性,ItemsSource 属性可以接受一个 IEnumerable 接口派生类作为自己的值(所有可以被迭代遍历的集合都实现了这个接口)。

代码例子:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Md9fSY4I-1610970699047)(./images/06_06.png)]

7. 使用 DataTable 数据作为 Binding 的源

代码例子:

  • ListBox 中 DataTable 作为源
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2QGU5ZM3-1610970699048)(./images/06_07.png )]
    备注:this.listBoxStudents.ItemsSource = dt.DefaultView;,其中的 DataTable 的 DefaultView 属性是一个 DataView 类型的对象,DataView 类实现了 IEnumerable 接口,所以可以被赋值给 ListBox.ItemsSource 属性。
  • ListView 控件中 DataTable 作为源(自定义格式)。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fbTMxMtS-1610970699052)(./images/06_08.png )]
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NSPRKhl2-1610970699053)(./images/06_09.png )]

备注:DataTable 不能直接拿来作为 ItemsSource 赋值,不过,当你把 DataTable 对象放在一个对象的 DataContext 属性里,是可以的,如下:

private void Button_Click(object sender, RoutedEventArgs e)
{
    DataTable dt = this.Load();

    this.listViewStudents.DataContext = dt;
    this.listViewStudents.SetBinding(ListView.ItemsSourceProperty, new Binding());
}
8. 使用 XML 数据作为 Binding 的源
  1. 代码例子1
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hFQb2vqp-1610970699055)(./images/06_11.png )]
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KUUaa3ZH-1610970699057)(./images/06_12.png )]
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VGv5ubmd-1610970699058)(./images/06_13.png )]

    备注:

    • 需要注意的是,当使用 XML 数据作为 Binding 的 suorce 时,我们将使用 XPath 属性,而不是 Path 属性来指定数据的来源。
    • 使用 @ 符号加字符串表示 XML 元素的 Attribute,不加 @ 符号的字符串表示子级元素。
  2. 代码例子2
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1Y7oWEB3-1610970699060)(./images/06_14.png )]
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BHsbGNkb-1610970699062)(./images/06_15.png )]

    备注:需要注意的是,如果把 XmlDataProvider 直接放在 XML 代码里,那么它的 XML 数据需要放在 <x:XData> ... </x:XData>标签里。

8. 使用 LINQ 数据作为 Binding 的源
  1. LINQ 查询的结果是一个 IEnumerable 类型的对象,而 IEnumerable 又派生自 IEnumerable,所以它可以作为列表控件的 ItemsSource 来使用。
  2. 不同情况下写法。
    • 直接泛型
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7JCqg3EI-1610970699064)(./images/06_16.png )]
    • 如果数据存放在一个已经填充好的 DataTable 对象里
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LddfDW3F-1610970699065)(./images/06_11.png )]
    • 如果数据存放在 XML 文件里,如下
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qRiQzWvI-1610970699067)(./images/06_18.png )]
9. 使用 Binding 的 RelativeSource
  1. 有明确数据源的时候我们可以通过 Source 和 ElementName 赋值的方法让 Binding 与之关联,有些时候不确定对象叫什么名字,只知道他们 UI 之间的依赖关系,可以使用 RelativeSource 属性。
  2. 代码例子:
    • 寻找祖先
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XOd01OHD-1610970699069)(./images/06_19.png )]
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3efR2QW5-1610970699070)(./images/06_20.png )]
    • 寻找自己
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U1xvwdbB-1610970699073)(./images/06_21.png )]
    • 备注:RelativeSource 类中的 Mode 属性的类型是 RelativeSourceMode 枚举,他的取值有: PreviousData、TemplatedParent、Self、FindAncestro

Binding 的数据校验和转换

Binding 的数据校验

下面程序是 UI 上有一个 TextBox 和 Slider,然后后台 Binding 关联起来,Slider 是源,TextBox 为目标,Slider 取值范围是0-100,需要校验的是 TextBox 里输入的值是不是0-100范围内。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KyvE9eMX-1610970699080)(./images/06_22.png )]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ABwxoCY7-1610970699081)(./images/06_23.png )]

备注:

  • TextBox 中输入0-100程序显示正常,但是输入这个区间之外的值或不能被解析的值时,TextBox 会显示红色边框,表示值时错误的,不能传递给 Source。
  • Binding 只在 Target 被外部方法更新时才校验数据,而来自 Binding 的 Source 数据更新 Target 是不会校验的,如果想改变这种行为,可以将校验条件的 ValidatesOnTargetUpdated 属性设为 true。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pgo1pORE-1610970699082)(./images/06_24.png )]
  • 默认校验错误之后,错误信息是不显示的,如果想显示,需要将 Binding 对象的 NotifyOnValidationError 属性设置 true,这样 error 信号才会在 UI 元素树上往上传递。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kvRpHU7x-1610970699083)(./images/06_25.png )]
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z70QIRNL-1610970699084)(./images/06_26.png )]
Binding 的数据转换
  1. 引入
    • 作用于 Source 和 Target 之间,其实现的接口,Convert 是正方向,ConvertBack 是反方向,就是从 Target 到 Source 需要经过的转换。Binding 对象的 Mode 属性会影响到两个方法的调用,如果是 TwoWay 两个方法都有可能被调用,如果是 OneWay 则只有一个 Convert 被调用。
    • 简单的转换WPF已经帮我们做了,有些复杂的需要我们自己写。
  2. 代码例子
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jXRfvc5a-1610970699085)(./images/06_27.png )]
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7qUPA960-1610970699086)(./images/06_28.png )]

备注:Convert 参数:第一参数 object 可以在方法体内对实际类型进行判断。第二个参数用于确定方法的返回类型。第三个参数用于把额外的信息传入方法,若需要传递多个信息可以把信息彷如一个集合对象来传入方法。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wmfpQuTS-1610970699087)(./images/06_29.png )]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WhsfaYzB-1610970699088)(./images/06_30.png )]

MultiBinding (多路 Binding)

  1. 引入:有时候 UI 需要显示的信息由不止一个数据来源决定,这时候就需要使用 MultiBinding。凡是能使用 Binding 对象的场合都能使用 MultiBinding,其类型是 Collection,通过这个属性把一组 Binding 对象聚合起来,出在这个集合中的 Binding 对象可以拥有自己的数据校验和转换机制。

  2. 代码例子

    • 需求:考虑这样一个需求,有一个用于新用户注册的UI(包含4个 TextBox 和一个 Button),还有如下一些限定:
      • 第一、二个 TextBox 输入用户名,需求内容一致。
      • 第三、四个 TextBox 输入用户 E-Mail,要求内容一致。
      • 当 TextBox 的内容全部符合要求的时候,Button 可用。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dgXJzL23-1610970699089)(./images/06_31.png )]
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q5BLKNId-1610970699091)(./images/06_32.png )]

    • 备注:
      • MultiBinding 对于添加子级 Binding 的顺序是敏感的,因为这个顺序决定了汇集到 Converter 里数据的顺序。
      • MultiBinding 的 Converter 实现的是 INultiValueConverter 接口。
上一篇:WPF实现消息中心


下一篇:[Vue基础实战]自定义指令练习