zl程序教程

您现在的位置是:首页 >  后端

当前栏目

WPF的数据绑定详细介绍

WPF数据 详细 介绍 绑定
2023-06-13 09:14:46 时间

一、WPF数据绑定的概要

数据绑定:是应用程序UI与业务逻辑之间建立连接的过程。如果绑定正确设置并且数据提供正确通知,则当数据的值发生更改时,绑定到数据的视觉元素会自动反映更改。数据绑定可能还意味着如果视觉元素中数据的外部表现形式发生更改,则基础数据可以自动更新以反映更改。

例如:如果用户编辑TextBox元素中的值,则基础数据值会自动更新以反映该更改。

1.数据绑定涉及到两个方面:

一个是绑定源,一个是绑定目标。绑定源即控件绑定所使用的源数据,绑定目标即数据显示的控件。

2.对于绑定源,在WPF可以是以下四种:

•CLR对象:可以绑定到CLR类的公开的属性、子属性、索引器上。
•ADO.Net对象:例如DataTable、DataView等。
•XML文件:使用XPath进行解析。
•DependencyObject:绑定到其依赖项属性上,即控件绑定控件。
对于绑定目标,必须是WPF中的DependencyObject,将数据绑定到其依赖项属性上。

二、     绑定的模式

1. 根据数据流的方向,WPF中的数据绑定分为以下四种:

OneWay绑定:对源属性的更改会自动更新目标属性,但是对目标属性的更改不会传播回源属性。此绑定类型适用于绑定的控件为隐式只读控件的情况。

TwoWay绑定:对源属性的更改会自动更新目标属性,而对目标属性的更改也会自动更新源属性。此绑定类型适用于可编辑窗体或其他完全交互式UI方案。

OneWayToSource与OneWay相反;它在目标属性更改时更新源属性。

OneTime绑定:该绑定会导致源属性初始化目标属性,但不传播后续更改。

注释:如果无需监视目标属性的更改,则使用OneWay绑定模式可避免TwoWay绑定模式的系统开销。

大多数属性都默认为OneWay绑定,但是一些依赖项属性,通常为用户可编辑的控件的属性,如TextBox的Text属性和CheckBox的IsChecked属性,默认为TwoWay绑定。

如果要知道依赖项属性绑定在默认情况下是单向还是双向的编程方法可使用GetMetadata获取属性的属性元数据,然后检查BindsTwoWayByDefault属性的布尔值。

示例代码:

复制代码代码如下:


<Pagex:Class="WpfDemo.Page1"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   Title="Page1"HorizontalAlignment="Center">

       <GridName="GridTable"Height="360"Background="Silver">

           <Grid.RowDefinitions>

               <RowDefinition></RowDefinition>

               <RowDefinition></RowDefinition>

               <RowDefinition></RowDefinition>

               <RowDefinition></RowDefinition>

           </Grid.RowDefinitions>

           <Grid.ColumnDefinitions>

           <ColumnDefinitionWidth="130"></ColumnDefinition>

           <ColumnDefinitionWidth="150"></ColumnDefinition>

           <ColumnDefinitionWidth="20"></ColumnDefinition>

           </Grid.ColumnDefinitions>

       <LabelWidth="130"Height="25" Grid.Row="0"Grid.Column="0" Name="label1">TwoWay</Label>

       <TextBoxWidth="150"Height="25" Grid.Row="0"Grid.Column="1" Name="textBox4"Text="{BindingElementName=scrollBar1,Path=Value,Mode=TwoWay}"/>

       <LabelWidth="130"Height="25" Grid.Row="1"Grid.Column="0" Name="label2">OneWay</Label>

       <TextBoxWidth="150"Height="25" Grid.Row="1"Grid.Column="1"  Name="textBox1"Text="{BindingElementName=scrollBar1,Path=Value,Mode=OneWay}"/>

       <LabelWidth="130"Height="25" Grid.Row="2"Grid.Column="0" Name="label3">OneWayToSource</Label>

       <TextBoxWidth="150"Height="25" Grid.Row="2"Grid.Column="1"  Name="textBox2"Text="{BindingElementName=scrollBar1,Path=Value,Mode=OneWayToSource}"/>

       <LabelWidth="130"Height="25" Grid.Row="3"Grid.Column="0" Name="label4">OneTime</Label>

       <TextBoxWidth="150"Height="25" Grid.Row="3"Grid.Column="1"  Name="textBox3"Text="{BindingElementName=scrollBar1,Path=Value,Mode=OneTime}"/>

        <ScrollBarValue="30"Minimum="0"Grid.RowSpan="4"Grid.Row="0"Grid.Column="2"Maximum="100"Name="scrollBar1"Width="18"Height="{BindingElementName=GridTable,Path=Height}"/>

       </Grid>

</Page>


根据程序执行结果,我们可以得到以下结论:

对于OneWay绑定:在界面中显示的数据可以随数据源的值的变化而变化,但更改界面的数据不会影响到数据源。

对于TwoWay绑定:界面中显示的数据及数据源的数据可以双向显示及更新。

对于OneWayToSource绑定:初始时界面的数据为空;更改界面的数据可以影响数据源的值,但是更改数据源的值不会体现在界面上。

对于OneTime绑定:在界面中显示的为数据源的初始值,更改数据源的值的时候,不会更改界面的数据显示;更改界面的数据也不会影响到数据源的数据。

三、绑定目标值影响绑定源值条件

问题:绑定源的值是在您编辑文本的同时进行更新,还是在您结束编辑文本并将鼠标指针从文本框移走后才进行更新呢?或者在您需要更新的情况下在手动的更新呢?

1.UpdateSourceTrigger属性是确定触发源更新的原因。

下图中右箭头的点演示UpdateSourceTrigger属性的角色:

TwoWay及OneWayToSource是由绑定目标到绑定源方向,若实现绑定目标的值更改影响绑定源的值方式,只需要设置相应控件绑定时的UpdateSourceTrigger的值,其值有三种:

PropertyChanged:当绑定目标属性更改时,立即更新绑定源。

LostFocus:当绑定目标元素失去焦点时,更新绑定源。

Explicit:仅在调用UpdateSource方法时更新绑定源。

注释:多数依赖项属性的UpdateSourceTrigger值的默认值为PropertyChanged,而Text属性的默认值为LostFocus。

2.示例

复制代码代码如下:


XAML:

 


<Pagex:Class="WpfDemo.Changed"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   Title="Changed">

   <GridName="GridTable"Height="250"Background="Silver"Width="350">

       <Grid.RowDefinitions>

           <RowDefinition></RowDefinition>

           <RowDefinition></RowDefinition>

           <RowDefinition></RowDefinition>

           <RowDefinition></RowDefinition>

       </Grid.RowDefinitions>

       <Grid.ColumnDefinitions>

           <ColumnDefinitionWidth="100"></ColumnDefinition>

           <ColumnDefinitionWidth="150"></ColumnDefinition>

           <ColumnDefinitionWidth="100"></ColumnDefinition>

       </Grid.ColumnDefinitions>

       <TextBlockGrid.Row="0"Width="90"Height="25"Grid.Column="0"Name="label1"Text="PropertyChanged:"></TextBlock>

       <TextBlockGrid.Row="1" Width="90"Height="25"Grid.Column="0"Name="label2"Text="LostFocus:"></TextBlock>

       <TextBlockGrid.Row="2" Width="90"Height="25"Grid.Column="0"Name="label3"Text="Explicit:"></TextBlock>

       <TextBoxGrid.Row="0"Width="150"Height="25"Text="{BindingPath=UserName,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Grid.Column="1"Name="TextBox1"/>

       <TextBoxGrid.Row="1"Width="150"Height="25"Text="{BindingPath=UserName,Mode=TwoWay,UpdateSourceTrigger=LostFocus}"  Grid.Column="1" Name="TextBox2"/>

       <TextBoxGrid.Row="2"Width="150"Height="25"Text="{BindingPath=UserName,Mode=TwoWay,UpdateSourceTrigger=Explicit}"  Grid.Column="1" Name="txtExplicit"/>

       <TextBlockGrid.Row="3"Width="90"Height="25" Grid.Column="0"Name="lblResult"Text="结果:"></TextBlock>

       <TextBlockGrid.Row="3"Width="90"Height="25" Grid.Column="1"Name="lblDisplay"Text="{BindingPath=UserName,Mode=OneWay}"></TextBlock>

       <ButtonName="btnChanged"Width="90"Height="25"Grid.Row="3"Grid.Column="2">Explicit</Button>

   </Grid>

</Page>


复制代码代码如下:
C#:


namespaceWpfDemo

{

   publicpartialclassChanged:Page

   {

       #regionproperties

       publicUserModelCurrentUser

       {

           get;set;

       }

       #endregion

       #regionConstructor

       publicChanged()

       {

           InitializeComponent();

           this.Loaded+=newRoutedEventHandler(Changed_Loaded);

           this.btnChanged.Click+=newRoutedEventHandler(btnChanged_Click);

       }

       #endregion

       #regionChanged_Loaded

       voidChanged_Loaded(objectsender,RoutedEventArgse)

       {

           this.CurrentUser=newUserModel(){UserName="swd"};

           this.DataContext=this.CurrentUser;

       }

       #endregion

       #regionbtnLogon_Click

       voidbtnChanged_Click(objectsender,RoutedEventArgse)

       {

         this.txtExplicit.GetBindingExpression(TextBox.TextProperty).UpdateSource();

       }

       #endregion

   }

   publicclassUserModel

   {

       publicstringUserName

       {

           get;set;}

   }

}


程序执行结果如上所述。


四、     数据提供程序

1.XmlDataProvider:

XmlDataProvider访问XML数据的方式有以下三种:

可以使用XmlDataProvider类嵌入内联XML数据。

可以将Source属性设置为XML数据文件的Uri。

可以将Document属性设置为XmlDocument。

注释:当XmlDocument.NodeChanged事件发生时,XmlDataProvider执行所有绑定的完全刷新。特定节点不进行优化。

默认情况下,XmlDataProvider.IsAsynchronous属性设置为true,表示默认情况下XmlDataProvider检索数据并异步生成XML节点的集合。

以下将介绍使用上面所述的三种方式显示xml数据:

示例

复制代码代码如下:
Xaml:


<Pagex:Class="WpfDemo.xmlBinding"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   Title="xmlBinding"xmlns:local="clr-namespace:WpfDemo">

   <Page.Resources>

       <XmlDataProviderx:Key="XmlFile"Source="Students.xml"XPath="/Students"></XmlDataProvider>

       <XmlDataProviderx:Key="InnerXmlStu"XPath="/Students">

           <x:XData>

               <Studentsxmlns="">

                   <Student><name>swd</name></Student>

                   <Student><name>awd</name></Student>

                   <Student><name>asd</name></Student>

               </Students>

           </x:XData>

       </XmlDataProvider>

   </Page.Resources>

   <Grid>

       <Grid.RowDefinitions>

           <RowDefinition></RowDefinition>

           <RowDefinition></RowDefinition>

           <RowDefinition></RowDefinition>

       </Grid.RowDefinitions>

       <Grid.ColumnDefinitions>

           <ColumnDefinitionWidth="100"></ColumnDefinition>

           <ColumnDefinitionWidth="150"></ColumnDefinition>

       </Grid.ColumnDefinitions>

       <TextBlockGrid.Row="0"Grid.Column="0" Height="25"Width="100" Text="引用XML文件"></TextBlock>

       <TextBlockGrid.Row="1"Grid.Column="0"Height="25"Width="100"  Text="内嵌XML"></TextBlock>

       <TextBlockGrid.Row="2"Grid.Column="0" Height="25"Width="100" Text="动态XML"></TextBlock>

       <ListBoxName="lisbXmlFile"Grid.Row="0"Grid.Column="1"Height="100"Width="150"ItemsSource="{BindingSource={StaticResourceXmlFile},XPath=Student/name}">

       </ListBox>

       <ListBoxName="lisbInnerXml"Grid.Row="1"Grid.Column="1" Height="100"Width="150"ItemsSource="{BindingSource={StaticResourceInnerXmlStu},XPath=Student/name}">

       </ListBox>

       <ListBoxName="lisbXmlDoc"Grid.Row="2"Grid.Column="1" Height="100" Width="150"ItemsSource="{BindingXPath=Student/name}">

       </ListBox>

   </Grid>

</Page>


复制代码代码如下:
XML:


<?xmlversion="1.0"encoding="utf-8"?>

<Students>

 <Student>

   <name>swd</name>

   <score>110</score>

 </Student>

 <Student>

   <name>asd</name>

   <score>120</score>

 </Student>

 <Student>

   <name>awd</name>

   <score>130</score>

 </Student>

</Students>


通过以上示例我想大家应该很容易理解与应用。


2.ObjectDataProvider:

ObjectDataProvider使您能够在XAML中创建可用作绑定源的对象,并为您提供以下属性,以对对象执行查询并绑定到结果。

使用ConstructorParameters属性将参数传递给对象的构造函数。

使用MethodName属性调用一个方法。

使用MethodParameters属性将参数传递给该方法。然后,可以绑定到该方法的结果。

使用ObjectType指定将提供数据绑定源的对象。

使用ObjectInstance属性来指定现有的对象实例作为源

注释:还可以使用IsAsynchronous属性指定是在辅助线程还是在活动上下文中执行对象创建。也就是是否异步检索数据。

示例:

复制代码代码如下:
XAML:

C#:


namespaceWpfDemo

{

   #regionCObjectDataProvider

   publicpartialclassCObjectDataProvider:Page

   {

       publicCObjectDataProvider()

       {InitializeComponent();}

   }

   #endregion

   #regionCountry

   publicclassCountry

   {

       #regionName

       publicstringName

       {get;set;}

       #endregion

       #regionProvinceList

       publicList<Province>ProvinceList

       {get;set;}

       #endregion

       #regionGetAllCity

       publicstaticList<Country>GetAllCity()

       {

           returnnewList<Country>{

            newCountry

            {

                Name="中国",

                ProvinceList=newList<Province>

                {

                  newProvince{Name="福建省",

                                CityList=newList<City>{newCity{Name="福州市"},newCity{Name="厦门市"},newCity{Name="漳州市"},newCity{Name="泉州市"}}

               },

               newProvince{Name="江苏省",

                  CityList=newList<City>{

                  newCity{Name="苏州市"},newCity{Name="南京市"},newCity{Name="扬州市"},newCity{Name="无锡市"}}

               },

               newProvince{Name="江西省",

                    CityList=newList<City>{newCity{Name="南昌市"},newCity{Name="九江市"}}}}

            }

           };

       }

       #endregion

   }

   #endregion

#regionProvince

   publicclassProvince

   {

       #regionName

       publicstringName

       {get;set;}

       #endregion

       #regionCityList

       publicList<City>CityList

       {get;set;}

       #endregion

   }

   #endregion

   #regionCity

   publicclassCity

   {

       #regionName

       publicstringName

       {get;set;}

       #endregion

   }

#endregion

}


五、类型转换与数据校验

1.IValueConverter接口

提供一种将自定义逻辑应用于绑定的方式。

在Binding时,数据源对象到目标对象之间(或者目标对象到数据源对象)可能需要某种转换。这时只需实现IValueConverter接口自定义值转换器即可。

接口原型定义:

publicinterfaceIValueConverter
{
   objectConvert(objectvalue,TypetargetType,objectparameter,CultureInfoculture);
   objectConvertBack(objectvalue,TypetargetType,objectparameter,CultureInfoculture);
}

参数value是要转换的值,typeTarget是转换后的值类型,parameter是Binding类的ConverterParameter传递过来的参数。

Convert方法:数据绑定引擎在将值从绑定源传播给绑定目标时,调用此方法。

ConvertBack方法:数据绑定引擎在将值从绑定目标传播给绑定源时,调用此方法。

ValueConversion属性作用是告诉自定义转换器类可以转换的源数据和目标数据的类型(ValueConversion属性将在稍后的示例中看到)。

2.ValidationRule类

提供一种为检查用户输入的有效性而创建自定义规则的方法。

ValidationRule:所有自定义验证规则的基类。提供了让用户定义验证规则的入口。

ExceptionValidation:表示一个规则,该规则检查在绑定源属性更新过程中引发的异常。它是一个内置的规则,它检查在绑定源属性更新过程中引发的异常。

ValidationResult:数据验证结果的表现方式。ValidationRule对象的Validate方法执行完毕后通过ValidationResult来表示验证的结果。这里包含了错误信息—ErrorContent,数据是否有效—IsValid。ValidResult为ValidationResult的有效实例。

ValidationError:表示一个验证错误,该错误在ValidationRule报告验证错误时由绑定引擎创建。

复制代码代码如下:
XAML:


<Pagex:Class="WpfDemo.TypeConvertAndValidationRule"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   Title="TypeConvertAndValidationRule"

     xmlns:src="clr-namespace:WpfDemo">

   <GridHeight="250"Width="360"Background="Silver">

       <Grid.RowDefinitions>
   <RowDefinition>
   </RowDefinition>
   <RowDefinition>
   </RowDefinition>
   <RowDefinition>
   </RowDefinition>
      </Grid.RowDefinitions>

       <Grid.ColumnDefinitions>
              <ColumnDefinition>
   </ColumnDefinition>
   <ColumnDefinition>
   </ColumnDefinition>
       </Grid.ColumnDefinitions>

       <TextBlockHeight="25"Width="100"Text="生日" Grid.Row="0"Grid.Column="0"></TextBlock>

       <TextBoxName="txtBirthday"Height="25"Width="150" Grid.Row="0"Grid.Column="1">

           <TextBox.Text>

               <BindingPath="Birthday"UpdateSourceTrigger="LostFocus"Mode="TwoWay">

                  <Binding.ValidationRules><src:ValidationDateTimeRule/></Binding.ValidationRules>

                  <Binding.Converter><src:MyConverterOfBirthFormat/></Binding.Converter>

               </Binding>
          </TextBox.Text>

          <TextBox.ToolTip>
               <BindingRelativeSource="{RelativeSourceSelf}"Path="(Validation.Errors)       [0].ErrorContent"></Binding>
          </TextBox.ToolTip>      </TextBox>

       <TextBlockHeight="25"Width="150"Grid.Row="1"Text="{BindingPath=Birthday,Mode=OneWay}"Grid.Column="1"></TextBlock>

       <TextBlockHeight="25"Width="100"Text="电子邮件格式检查"Grid.Row="2"Grid.Column="0"></TextBlock>

       <TextBoxHeight="25"Width="150"Grid.Row="2"Grid.Column="1">

           <TextBox.Text>

               <BindingPath="EMail">

                   <Binding.ValidationRules><ExceptionValidationRule/></Binding.ValidationRules>

               </Binding>           </TextBox.Text>

           <TextBox.ToolTip>

                  <BindingRelativeSource="{RelativeSourceSelf}"Path="(Validation.Errors)[0].ErrorContent"></Binding>

           </TextBox.ToolTip>      </TextBox>

   </Grid>

</Page>


复制代码代码如下:
C#:


namespaceWpfDemo

{

   #regionTypeConvertAndValidationRule

   publicpartialclassTypeConvertAndValidationRule:Page

   {

       publicTypeConvertAndValidationRule()

       {

           InitializeComponent();

           this.DataContext=newUserInfo{Name="swd",Birthday=System.Convert.ToDateTime("1987/10/21"),EMail="swd@126.com"};

       }

   }

   #endregion

   #regionUserInfo

   publicclassUserInfo

   {

       #regionName

       publicstringName

       {get;set;}

       #endregion

       #regionBirthday

       publicDateTimeBirthday

       {get;set;}

       #endregion

       #regionEMail

       privatestringemail;

       publicstringEMail

       {

           get

           {returnemail;}

           set

           {

               this.email=value;

               Regexr=newRegex(@"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$");

               if(!r.IsMatch(value))

               {

                   thrownewApplicationException("电子邮件格式有误!");

               }

           }

       }

       #endregion

   }

   #endregion

六、     绑定集合对象

1.      ICollectionView接口

允许集合具有当前记录管理、自定义排序、筛选和分组这些功能。比如排序,分组,筛选,导航以及其它自定义视图,并且这不会影响到你的后台数据的实际存储。

2.      ObservableCollection<T>类

表示一个动态数据集合,在添加项、移除项或刷新整个列表时,此集合将提供通知。

3.      WPFMVVM概要

MVVM(Model-View-ViewModel)是由MVC,MVP演变而来。MVVM分离了逻辑与界面,解放业务逻辑。