[四] WPF灵魂-Binding

[四] Binding

所有内容均出自于《深入浅出WPF》一书,其作者在b站也有相关视频,本文为个人读后总结,仅供参考

WPF 最重要的部分,灵魂所在,当连接一端的数据发生变化时,他会自动的在另一端刷新数据,它的存在相当于 web 中的前后端分离,至此,设计师只管UI,开发者只管业务逻辑,binding 成为它们中的桥梁,一端连接 UI,一端连接业务,重要的是,这都是自动的,这个桥梁不需要工程师去维护和设计,由此开始数据驱动UI

Bing的基础使用

binding: 翻译为 绑定,关键为 ,原意中还包含 关联 的意思

binding是桥梁,两端分别是 (source 从哪里来) ,目标(target 到哪里去),一般情况下,binding负责把逻辑层的数据运输到 UI 层,即数据驱动 UI

演示:

1.首先准备一个 Stu 类,这个类的 实例 将作为数据源,同时由于 binding 是一种自动机制,可以自动把变化传递给 UI,但是需要数据在变化是通知它一声,所以这里的类需要继承 INotifyPropertyChanged 通知类,当数据变化时,变化的属性 将会发出 PropertyChanged 事件,binding 会监听这个事件。

class Stu : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private string name;

        public string Name
        {
            get { return name; }
            set { 
                name = value;
                if (PropertyChanged != null)
                {
                    PropertyChanged.Invoke(this,new PropertyChangedEventArgs("Name"));
                }
            }
        }

    }

建立前端页面,后台建立binding 对象,然后把binding 绑到UI上

// UI
<StackPanel>
        <TextBox x:Name="tb" Margin="5"/>
        <Button Content="添加" Click="Button_Click" Margin="5" />
    </StackPanel>
//后台代码
		Stu stu = new Stu();
        public MainWindow()
        {
            InitializeComponent();
            Binding binding = new Binding();   //创建 binding 对象
            binding.Source = stu;              //为 binding 添加数据源
            binding.Path = new PropertyPath("Name"); //Path 是指我要绑定数据源中的哪一个属性,他的类型是 PropertyPath ,所以我们也new 一个													//	PropertyPath,并指向 Name 属性       
            //通过 BindingOperations 把 binding 设置到Textbox上 ,这里要注意的是,binding 虽然是桥梁,但它不是一边连接数据源,一边连接目标,而是binding 连接数据源,然后把 binding 添加到目标上
            BindingOperations.SetBinding(this.tb,TextBox.TextProperty,binding); 
            
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            stu.Name += "hello"; //点击按钮,数据变化,通知 binding, 传递到 UI,UI变化
        }

效果:

补充:

把数据源和目标连接到一起使用了 BindingOperations.SetBinding(),现在解析一下

  • 第一个参数:指定 binding 的目标 ,这里就指定了 tb (textbox)
  • 第二个参数:指定数据要运输到目标的那个属性上,与binding 的 Path 很像, 这里接受一个 DependencyProperty 对象(依赖对象),他是一个比较特殊的对象,专门用来和 binding 联动,这一类属性就可以被数据驱动, UI 的控件基本都实现了这个属性,也可以自定义依赖属性
  • 第三个参数:指定那个 binding 实例将数据源和目标联系起来

Binding 的源(source)

指定 binding 的源包括指定 source 和 path

binding 数据源的要求:

  • Object 对象
  • 属性公开 (可以被访问到)

控件作为 binding的源 + binding 扩展标记的用法

<StackPanel>
        <TextBox Text="{Binding ElementName=slider1, Path=Value}" Margin="10"/>
        <Slider x:Name="slider1" Maximum="100" Margin="10"/>
    </StackPanel>

控件作为 binding的源 需要使用 ElementName 来指定源

控制 binding 的方向及数据更新

控制数据流动方向: mode 属性,该类型是 Binding 枚举类

**数据更新:**在默认情况下,只有文本框失去焦点,数据才会更新,控制数据更新的的是 UpdateSourceTrigger 属性,它的类型是 UpdateSourceTrigger 枚举类,当把值改为 PropertyChanged 时,就可以实现实时变化了

演示:

这里将TextBox 的 Text 属性和 Slider 的 Value 属性绑定,并且设置 mode 为双向,UpdateSourceTrigger=PropertyChanged 实时更新,当我们滑动滑块时,文本框中的数值就会发生变化,并且一旦在文本框中修改值,立即就能同步到 salider 上

 <TextBox Text="{Binding ElementName=slider1,Path=Value,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Margin="5"/>
        <Slider x:Name="slider1" Maximum="100" Margin="5"/>

Binding 的路径

Path 用于指定我们需要绑定数据源的哪个属性

Path 的类型是

1.Path 的不同写法:

 //xmal: 
 <TextBox Text="{Binding ElementName=slider1,Path=Value}"/>
 //c#
 Binding binding = new Binding() { Path = new PropertyPath("Value"), Source = slider1 };
 this.textBox.SetBinding(TextBox.TextProperty, binding);   
 //c# 简写 Binding 有很多重载,拥有直接接受path参数的构造器
 Binding binding = new Binding("Value") {  Source = slider1 };
 this.textBox.SetBinding(TextBox.TextProperty, binding);
 // 继续简化
 this.textBox.SetBinding(TextBox.TextProperty, new Binding("Value") {  Source = slider1 });

2.Path 支持多级路径

 // 通过 . 号可以像后台代码一样逐级往下点 
 <TextBox x:Name="textBox" Text="{Binding ElementName=tb1,Path=Text.Length,Mode=OneWay}"/>
 <TextBox x:Name="tb1"/>
 //等效 c# 代码
 Binding binding = new Binding("Text.Length") {  Source = tb1 };
 this.textBox.SetBinding(TextBox.TextProperty, binding);     

 //集合类型的索引器又称为带参属性,只要是属性就可以作为 Path
 //xaml:
 <TextBox x:Name="textBox" Text="{Binding ElementName=tb1,Path=Text.[2],Mode=OneWay}"/>
 <TextBox x:Name="tb1"/>
 //c#
 Binding binding = new Binding("Text.[2]") {  Source = tb1 };
 this.textBox.SetBinding(TextBox.TextProperty, binding);
 //简写 后台同理
 <TextBox x:Name="textBox" Text="{Binding ElementName=tb1,Path=Text[2],Mode=OneWay}"/>
 <TextBox x:Name="tb1"/>

3.Path 支持集合

Path 支持多级路径,那么同样扩展一下,集合也可以使用,多级集合也可以

简单使用:

//xaml:
<StackPanel>
    <TextBox x:Name="tb1" Margin="5"/>
    <TextBox x:Name="tb2" Margin="5"/>
    <TextBox x:Name="tb3" Margin="5"/>
</StackPanel>
//c#
 List<string> list = new List<string>() { "Hello", "Boy" };

 this.tb1.SetBinding(TextBox.TextProperty, new Binding("/") { Source = list });
 this.tb2.SetBinding(TextBox.TextProperty, new Binding("/Length") { Source = list, Mode = BindingMode.OneWay });
 this.tb3.SetBinding(TextBox.TextProperty, new Binding("/[2]") { Source = list, Mode = BindingMode.OneWay });

效果:

进阶 – 集合的集合:

public MainWindow()
        {
            InitializeComponent();
            County county = new County() { 
                Name = "中国", 
                citys = new List<City>() {  new City() { Name = "西安" } , new City() { Name = "汉中" }} };
            List<County> list = new List<County>() { county};
            this.tb1.SetBinding(TextBox.TextProperty, new Binding("/Name") { Source = list });
            this.tb2.SetBinding(TextBox.TextProperty, new Binding("/citys.Name") { Source = list});
        

        }

    }
    class County {
        public string Name { get; set; }
        public List<City> citys { get; set; }
    }
    class City {
        public string Name { get; set; }
    }

可以省略 Path 的情况

在数据源本身就是数据的情况下,就可以省略 path 比如 string 字符串示例本身就是数据,在zaml中,此时 path 可以用 . 代替。也可以直接不写,但是在 后台代码中,path 就只能使用 . 。

 <Window.Resources>
        <c:String x:Key="str">Hello</c:String>
 </Window.Resources>
 <StackPanel>
 			<TextBlock Text="{Binding Path=.,Source={StaticResource ResourceKey=str}}"></TextBlock>
 </StackPanel>

为 binding 指定源的几种方式

上面是如何通过 path 寻找属性,现在我们关注如何指定数据 source

binding 的源是数据的来源,只要一个对象包含数据并且能通过属性把数据暴漏出来,就可以作为 binding 的源,这里不过多解释,纯做记录,加粗表示个人用的较多

  • 普通的单个对象:包括 .net自带类型的的对象和自定义对象,如果类型实现了 INotifyPropertyChange 对象,就可以激发事件通知 bInding 更新
  • 集合对象:包括 数组 List 等,经常把此类数据作为 ItemControl 派生类的数据源使用,一般是赋给ItemSource
  • ADO.Net数据对象:包括DataTable 和 DataView 等
  • XmlDataProvider 把XML 数据指定为数据源:XML 使用广泛,尤其在级联式的控件中就可以把 XML作为源
  • 使用 Linq 检索结果作为源:Linq 类似于lamda表达式,在指定源时可以使用linq 来简化操作
  • 把依赖对象指定为 Source :前面有讲到,依赖属性是比较特殊的属性,专门用来和Binding联动,既可以作为目标,也可以作为源
  • 把容器的 DateContext 作为源:在没有指定源时, binding 会沿着控件树向上寻找Source
  • 通过 ElementName 指定 Source:在c#代码中可以直接指定对象作为源,但是 zaml 中无法访问对象,可以使用 ElementName 指定对象的名字
  • 通过 Binding 的 RelativeSource :把自己或者自己的属性指定为自己的源
  • 把 ObjectDataProvider 对象指定为源:特殊,把方法的返回值作为源,使用时需要创建ObjectDataProvider 对象,和方法所属类对象,把这个类传给ObjectDataProvider 并设定参数,就可以使用ObjectDataProvider 对象了
binding 没有 source 的情况

前面说过,WPF 的控件是一棵树,**每一个节点都有一个 DateContext 的属性,**当控件没有指定 source 时,他就会寻找沿着这棵树一直往上找 DateContext 的属性,看里面有没有对应的属性,找到就返回,没有找到就一直延续,直到树根还没找到,那就没有数据

 <Grid>
        <StackPanel >
            <StackPanel.DataContext>
                <local:Stu Name="Hello" Age="23"></local:Stu>
            </StackPanel.DataContext>
            <TextBox Text="{Binding Path=Name}" Margin="5"> </TextBox>
            <TextBox Text="{Binding Path=Age}" Margin="5"></TextBox>
        </StackPanel>
    </Grid>

  • Binding 本身也有 path为参数的构造函数,所以在只有一个 path 时可以省略
 <Grid>
        <StackPanel >
            <StackPanel.DataContext>
                <local:Stu Name="Hello" Age="23"></local:Stu>
            </StackPanel.DataContext>
            <TextBox Text="{Binding Name}" Margin="5"> </TextBox>
            <TextBox Text="{Binding Age}" Margin="5"></TextBox>
        </StackPanel>
    </Grid>
  • path 本身就是数据,不需要通过属性暴漏数据时**,可以用 . ,也可以省略**
 <Grid>
        <StackPanel >
            <StackPanel.DataContext>
                <c:String >Hello</c:String>
            </StackPanel.DataContext>
            <TextBox Text="{Binding}" Margin="5"> </TextBox>
            <TextBox Text="{Binding}" Margin="5"></TextBox>
        </StackPanel>
    </Grid>

现在来细看一下指定源的几种方式

1.集合对象
  1. WPF 列表式控件派生自 ItemControl 类,继承了 ItemSource 的这个属性,**而 ItemSource 属性可以接受一个 IEnumerable 类型的值 **(可以接受集合的原因)
  2. 并且 ItemControl 的派生类都具有他们的的条目容器,比如 LIstBox 的条目容器是 ListBoxItem

演示:

//zaml:
<StackPanel Background="AliceBlue">
        <TextBox x:Name="tb1" Margin="5" />
        <ListBox x:Name="lb1" Margin="5"/>
 </StackPanel>
//c#   
InitializeComponent();
List<Stu> stus = new List<Stu>()
{
    new Stu(){  ID = 1, Name = "Tim"},
    new Stu(){  ID = 2, Name = "LIk"},
};
this.lb1.ItemsSource = stus;
this.lb1.DisplayMemberPath = "Name";

Binding binding = new Binding("SelectedItem.ID") { Source = this.lb1 };
this.tb1.SetBinding(TextBox.TextProperty, binding);
//实体类         
class Stu
{
    public int ID { get; set; }
    public String Name { get; set; }
}

给ItemsSource指定集合数据源,路径设置为 “Name”, TextBox 绑定ListBox 的 SelectedItem.ID,显示被选中的item的对象(Stu)的 ID

在 ItemsSource 中没看见 Binding 但是却实现了 binding 的效果,事实上,当DisplayMemberPath被赋值时,就会自动创建 Binding 并以DisplayMemberPath的值作为 path, 创建的 Binding 数据源就是 List中的每一个对象,path 就是DisplayMemberPath的值,目标就是 每一个 ListBoxItem(容器),默认情况下 ListBoxItem 的内部就是一个 TextBox,所以 Binding 实际绑定的是ListBoxItem下的TextBox

事实上大概就是下面这个样子,效果和 指定DisplayMemberPath后一样

<ListBox x:Name="lb1" Margin="5">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Name}"/>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

在使用集合类型的控件时,一般会使用 ObservableCollection 替代List ,因为 ObservableCollection 自带通知

2. XmlDataProvider 把XML 数据指定为数据源

.net 提供了Dom(Document Object Mode 文档对象类型) 和 LINQ (Language-Integreted Query 语言集成查询)两种类库操作xml

这里以dom 为准

Binding 数据转换和校验 - MultiBinding(多路 Binding)

 </DataTemplate>
</ListBox.ItemTemplate>
```

在使用集合类型的控件时,一般会使用 ObservableCollection 替代List ,因为 ObservableCollection 自带通知

2. XmlDataProvider 把XML 数据指定为数据源

.net 提供了Dom(Document Object Mode 文档对象类型) 和 LINQ (Language-Integreted Query 语言集成查询)两种类库操作xml

这里以dom 为准

Binding 数据转换和校验 - MultiBinding(多路 Binding)

MultiBinding(多路 Binding) 和单 binding 并无区别,就是把多个 binding 集中在一起,统一操作,比如为 MultiBinding 设置一个校验器,他就可以应用到所有添加的 单个 binding 上

WPF 01-BootstrapperShell是一种用于启动和初始化WPF应用程序的框架。它是指示WPF应用程序在启动时应执行的代码的入口点。通常情况下,我们可以在App.xaml.cs文件中找到它。 BootstrapperShell提供了一种将应用程序的各个部分组织在一起的方式,以便在启动时执行特定的操作。这些操作可以包括设置应用程序的默认样式、添加全局资源、注册服务和创建主窗口等。通过将所有这些相关的代码集中到一个地方,我们可以更好地管控应用程序的启动过程。 通常情况下,BootstrapperShell会执行以下几个步骤: 1. 创建应用程序的主窗口:这个步骤通常在App.xaml.cs文件的OnStartup方法中完成。我们可以在这里创建一个MainWindow实例,并将其设置为应用程序的主窗口。 2. 设置应用程序的默认样式:WPF应用程序通常使用样式来定义应用程序中各个控件的外观和行为。在BootstrapperShell中,我们可以通过添加资源字典来设置应用程序的默认样式。 3. 注册服务和初始化其他组件:在应用程序启动时,我们可能需要注册一些服务或初始化其他组件,以便在应用程序中的其他地方使用。在BootstrapperShell中,我们可以执行这些操作。 4. 处理未捕获的异常:在应用程序中可能会发生未捕获的异常,我们可以通过在BootstrapperShell中实现Application.DispatcherUnhandledException事件处理程序来捕获和处理这些异常。 总而言之,WPF 01-BootstrapperShell是一种用于组织和管理WPF应用程序启动过程的框架。它提供了一个入口点来集中所有与应用程序启动相关的代码和操作,从而更好地控制应用程序的行为和外观。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值