WPF之Binding

本文详细介绍了WPF中的数据绑定机制,包括绑定的基本概念、数据流向控制、绑定路径指定及不同类型的绑定源,如CLR对象、集合、ADO.NET数据、XML数据等。此外还讲解了如何使用DataContext简化绑定流程,以及数据转换与验证的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

wpf致力于将业务逻辑层处于核心地位,展示层永远处于逻辑层的从属位置
wpf的这种能力源于
DataBinding 、与之配套的Dependency Property系统和DataTemplate

Target  <=>  Binding  <=>  Source

Binding的源:也就是数据的源头,只要求是个对象并通过属性公开自己的数据,它就能作为Binding源
例如:
1.把控件自己或者自己的容器或子级元素作为源
2.用一个控件作为另一个控件的源
3.把集合作为ItemsControl的数据源
4.使用XML作为TreeView或Menu的数据源
5.把多个控件关联到一个“数据制高点”上,甚至不给Binding指定数据源让它自己去找。

控制Binding的方向和数据的更新

控制Binding数据流向的属性是Model,它的类型是BindingModel
BindingModel枚举
TwoWay
OneWay
OnTime
OneWayToSource
Default 根据实际情况来定,可编辑的双向,只读的单向

Binding数据更新的属性是
UpdateSourceTrigger枚举
PropertyChanged
LostFocus
Explicit
Default
另Binding还有一个属性NotifyOnSourceUpdated、NotifyOnTargetUpdated两个bool属性
如果设为true,当源或目标更新时会激发相应的SourceUpdated事件和TargetUpdated事件


Binding的路径Path
来关注源中哪个属性的值,由path来指定,可以在Binding的构造器来指定,Path的实际类型是PropertyPath
我们知道用一个控件可以作为另一个控件的源
Path可以用来关注属性,而属性又分为无参属性、有参属性(索引器)
针对于有参属性
例如:textBox2显示textBox1的第四个文本字符。
<TextBox x:Name="textBox1" BorderBrush="Black"  Margin="5" />
<TextBox x:Name="textBox2" Text="{Binding Path=Text[3],ElementName=textBox1,Mode=OneWay}" Margin="5" BorderBrush="Black"/>

当一个集合或DataView作为源时,如果想默认的元素当做Path使用,需要这样的语法
List<string> list=new List<string>(){"Tom","Tim","Blog"};
this.textBox1.SetBinding(TextBox.TextProperty,new Binding("/"){ Source=list});            //Tom
this.textBox2.SetBinding(TextBox.TextProperty,new Binding("/Length"){ Source=list});    //3
this.textBox3.SetBinding(TextBox.TextProperty,new Binding("/[2]"){ Source=list});        //m
如果想把子集合中的元素当做Path,可以使用多级斜线的语法

没有Path的Binding
实例本身就是数据源,不需要Path来指明,例如string int等基本类型就是,实例本身就是数据,无法通过哪个属性来访问数据
例如:
<StackPanel>
    <StackPanel.Resource>
        <sys:String x:Key="myString">
            实例本身就是数据源
        </sys:String>
    </StackPanel.Resource>
    <TextBlock x:Name="textBox1" Text="{Binding Path=.,Source={ StatciResource ResourceKey=myString}}" />
</StackPanel>
Path可以省略掉

为Binding指定Path的几种方法
1、普通CLR类型单个对象指定为Source:包括FCL类型和用户自定义类型的对象,如果该类型实现了INotifyPropertyChanged接口,
则可通过在属性的set语句里激发PropertyChanged事件来通知Binding数据已被更新。
2、数组、List<T>、ObservableCollection<T>等集合类型,一般是把控件的ItemsSource属性使用Binding关联到一个集合对象上。
3、ADO.Net的对象DataTable和DataView对象
4、XmlDataProvider把XML指定为Source ,如TreeView、Menu 上上节提到的x:XData指令元素
5、把依赖对象指定为Source
6、把容器的DataContext设为Source,只设置Path,不设置Source,让这个Binding自己去找Source(会沿着控件树一层一层向外找,直到找到带有path指定属性的对象为止)
7、通过ElementName指定Source
8、通过Binding的RelativeSource属性相对的指定Source:当控件想关注自己的、自己容器的或者自己内部元素的某个值时采用这种办法。
9、把ObjectDataProvider对象指定为Source:当数据源不是通过属性而是通过方法暴漏给外界的时候
10、使用Linq检索的数据对象作为Binding的源。

一一介绍:
6、没有Source的Binding——使用DataContext作为Binding的源
Binding就会自动向UI元素树的上层去寻找可用的DataContext
如下代码:
<StackPanel>
    <StackPanel.DataContext>
        <local:Teacher Name="hailiang" Age="25" Sex="" />
    </StackPanel.DataContext>
    <Grid>
        <StackPanel>
            <TextBlock Text="{Binding Path=Name}" Margin="5"/>
            <TextBlock Text="{Binding Path=Age}" Margin="5"/>
            <TextBlock Text="{Binding Path=Sex}" Margin="5"/>
        </StackPanel>
    </Grid>
</StackPanel>
当实例本身就是数据源时,Path=.,可以没有path,如果某个DataContext是个简单类型对象时,也可以不指定Source
如下代码:
<Grid>
    <Grid.DataContext>
        <sys:String >Hello DataContext!!</sys:String>
    </Grid.DataContext>
    <StackPanel>
        <TextBlock Text="{Binding}" Margin="5"/>
    </StackPanel>
</Grid>
Binding沿着UI元素向上找,其实Binding没有那么智能,只是DataContext是一个依赖属性
这里依赖属性很重要的一个特点是:
当你没有为控件的某个依赖属性显示赋值时,控件会把自己容器的属性值借过来当做自己的属性值
实际工作中DataContext的用法非常灵活:
(1)、当UI上的多个控件使用Binding关注同一个对象时,可以使用DataContext。
(2)、当作为Source的对象不能被直接访问时——比如B窗体的控件想把A窗体的控件当做自己的Binding源时,但A窗体的控件是private访问级别,这时候就可以
把这个控件(控件的值)作为A窗体的DataContext(这个属性石public访问级别)从而暴漏出来


2、使用集合对象作为列表控件的ItemsSource
使用集合类型作为列表控件的ItemsSource时一般会考虑用ObserableCollection<T>代替List<T>,因为其实现了INotifyCollectionChanged和INotifyPropertyChanged接口
能把集合的变化立刻显出来。

3、使用ADO.NET做为Binding的源
一般是
DataTable dt=Bussiness.GetData();
listBox1.ItemsSource=dt.DefaultView;
这里的DefaultView是个DataView类型的对象。DataView实现了IEnumerable接口,所以可以给ItemsSource赋值。
DataTable不能直接拿来为ItemsSource赋值
但是可以将DataTable对象放到一个对象的DataContext属性里,并把ItemsSource与一个既没有Path有没有Source的Binding关联起来,Binding能自动找到DefaultView并
当做自己的Source
代码:
DataTable dt=this.Load();
this.listViewStudents.DataContext=dt;
this.listViewStudents.SetBinding(ListView.ItemsSourceProperty,new Binding());

GridView可以为GridViewColumn设置HeaderTemplate和CellTemplate,他们的类型都是DataTemplate。

4、使用Xml数据作为Binding源
迄今为止 .net framework提供了2套处理xml数据的类库
1、符合DOM标准的类库:包括XmlDocument、XmlElement、XmlNode、XmlAttribute
2、Linq:包括XDocument、XElement、XNode、XAttribute
注意:当使用Xml作为Binding的Source时,这里讲使用XPath,而非Path

XPath=@Id 这里使用@符号表示的是XML元素的Attribute

把XML数据和XmlDataProvider对象直接写在XAML代码里,那么XML数据要放在<x:XData>...</x:XData>中。代码中用到HierarchicalDataTemplate
代码如下:
<Window.Resources>
        <XmlDataProvider x:Key="xdp" XPath="FileSystem/Folder">
            <x:XData>
                <FileSystem xmlns="">
                    <Folder Name="Books">
                        <Folder Name="C#">
                            <Folder Name="CLR via C#" />
                            <Folder Name="C#本质论"/>
                        </Folder>
                        <Folder Name=".NET">
                            <Folder Name="Silverlight" />
                            <Folder Name="WPF" />
                        </Folder>
                        <Folder Name="DataBase">
                            <Folder Name="SQL Server 2008" />
                            <Folder Name="MySql"/>
                            <Folder Name="Oracle"/>
                        </Folder>
                    </Folder>
                </FileSystem>
            </x:XData>
        </XmlDataProvider>
    </Window.Resources>
<StackPanel>
    <TreeView ItemsSource="{Binding Source={StaticResource ResourceKey=xdp}}">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding XPath=Folder}">
                    <TextBlock Text="{Binding XPath=@Name}"/>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
    </TreeView>
</StackPanel>

10、使用Linq作为Binding的源
Linq的查询结果是个IEnumberable<T>类型的对象
可以作为列表控件的ItemsSource来使用

9、使用ObjectDataProvider作为Binding的Source
把对象作为数据源提供给Binding
ObjectDataProvider作用是用来包装一个以方法暴露数据的对象
代码:
ObjectDataProvider odp = new ObjectDataProvider();
    odp.ObjectInstance = new Calculator();
    odp.MethodName = "Add";
    odp.MethodParameters.Add("0");
    odp.MethodParameters.Add("0");

    Binding bindingToArg1 = new Binding("MethodParameters[0]")
    {
        Source = odp,
        //告诉Binding只负责把从UI元素收集到的数据写入Source(ObjectDataProvider对象)
         BindsDirectlyToSource=true,
         //一有更新立即传回Source
         UpdateSourceTrigger=UpdateSourceTrigger.PropertyChanged
    };
    this.txtNum1.SetBinding(TextBox.TextProperty, bindingToArg1);

    txtNum2.SetBinding(TextBox.TextProperty, new Binding("MethodParameters[1]")
    {
        Source = odp,
        BindsDirectlyToSource = true,
        UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
    });

    txtResult.SetBinding(TextBox.TextProperty, new Binding(".") { Source = odp });

8、使用Binding的RelativeSource
略 

Binding对数据的转换与校验

Binding在Target与Source之间起着桥梁的作用,但是桥上也有关卡,用来  数据转换盒数据校验
数据校验:Binding的ValidationRules属性
继承抽象类:ValidationRule,重写override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)该方法
数据转换:Binding的Converter属性
实现IValueConverter接口
public infterface IValueConverter
{
    //当数据从Binding的Source流向Target时Converter被调用
    /**
     *参数1:未转型之前的值
     *参数2:确定方法的返回类型,outputType
     *参数3:把额外的信息传入方法
     *另外Binding的Model属性会影响这2个方法的调用
     */
    object Converter(object value,Type targetType,object parameter,CultureInfo culture);
    //当数据从Binding的Target流向Source时ConverterBack被调用
    object ConvertBack(object value,Type targetType,object parameter,CultureInfo culture);
}


MultiBinding
与Binding一样都是以BindingBase为基类
其有个属性Bindings类型为Collection<BindingBase>
例如,注册用户时有重复输入用户名或密码,来控制提交按钮的启用与禁用
Converter:
class LogonMultiBindingConverter:IMultiValueConverter    //实现的是IMultiBindingConverter接口
    {
        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            //throw new NotImplementedException();
            if (!values.Cast<string>().Any(text=>string.IsNullOrEmpty(text))&&
                values[0].ToString()==values[1].ToString()&&
                values[2].ToString()==values[3].ToString()
                )
            {
                return true;
            }

            return false;
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
后台代码:
    Binding b1 = new Binding("Text") { Source=txt1 };
    Binding b2 = new Binding("Text") { Source = txt2 };
    Binding b3 = new Binding("Text") { Source = txt3 };
    Binding b4 = new Binding("Text") { Source = txt4 };

    MultiBinding bindings = new MultiBinding() { Mode=BindingMode.OneWay};
    //MultiBinding对添加子级Binding的顺序是敏感的
    bindings.Bindings.Add(b1);
    bindings.Bindings.Add(b2);
    bindings.Bindings.Add(b3);
    bindings.Bindings.Add(b4);

    bindings.Converter = new LogonMultiBindingConverter();

    btnSubmit.SetBinding(Button.IsEnabledProperty, bindings);
    
    

 

转载于:https://www.cnblogs.com/hailiang2013/archive/2013/04/22/3035348.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值