XmlDataProvider
提供了一种简单的方式绑定到一段XML,无论这段XML在内存中的片段或一个完成文件中。
<Page.Resources> <XmlDataProvider x:Key="xmlData" XPath="Images"> <!-- 数据岛包含在x:XData标记中 --> <x:XData> <!-- XML 数据岛 --> <!-- 用空xmlns标记XML根节点,否则会被默认命名空间影响,XPath查询无法工作 --> <Images xmlns=""> <Image ID="1"> <Name>a.jpg</Name> <Image ID="3"> <Name>c.jpg</Name> </Image> </Image> <Image ID="2"> <Name>b.jpg</Name> </Image> </Images> </x:XData> </XmlDataProvider> <!-- XML文件位于一个独立文件中 --> <XmlDataProvider x:Key="xmlData2" XPath="Images" Source="Images.xml" /> <!-- 将整个XML数据绑定到一个可以理解层次结构TreeView或Menu的元素上 --> <!-- 绑定XML的根节点Images样式模板 --> <!-- 未定义显示x:Key,因为默认以DataType值做了资源键名 --> <!-- DataType对应XML元素名称 --> <HierarchicalDataTemplate DataType="Images" ItemsSource="{Binding XPath=*}"> <TextBlock Text="All Images" Background="DeepPink"/> </HierarchicalDataTemplate> <!-- 绑定XML的子节点Image样式模板 --> <HierarchicalDataTemplate DataType="Image" ItemsSource="{Binding XPath=*}"> <TextBlock FontWeight="Bold" Text="{Binding XPath=.}"/> </HierarchicalDataTemplate> <!-- 绑定XML元素Image的子节点Name样式模板 --> <DataTemplate DataType="Name"> <TextBlock Foreground="CornflowerBlue" Text="{Binding XPath=.}"/> </DataTemplate> </Page.Resources> <StackPanel> <ListBox ItemsSource="{Binding Source={StaticResource xmlData2}, XPath=Image/Name}"/> <TreeView ItemsSource="{Binding Source={StaticResource xmlData},XPath=.}"/> <Menu ItemsSource="{Binding Source={StaticResource xmlData},XPath=.}" /> </StackPanel>
ObjectDataProvider
可以提供一个.Net对象为数据源,提供附加能力:
1.声明式的使用带参构造函数实例化对象
<Page x:Class="WPF_Test.Page4" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:local="clr-namespace:WPF_Test" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300" Title="Page4"> <Page.Resources> <!-- ObjectDataProvider star --> <local:PhotoList x:Key="photos"/> <ObjectDataProvider x:Key="ObjectProvider" ObjectInstance="{StaticResource photos}" /> <ObjectDataProvider x:Key="ObjectProvider_2" ObjectType="{x:Type local:PhotoList}"> <!-- 用带一个参数的构造函数实例化PhotoList对象 --> <ObjectDataProvider.ConstructorParameters> <sys:Int32>2</sys:Int32> </ObjectDataProvider.ConstructorParameters> </ObjectDataProvider> <!-- ObjectDataProvider end --> </Page.Resources> <StackPanel> <ListBox SelectedValuePath="ID" DisplayMemberPath="Name" ItemsSource="{Binding Source={StaticResource ObjectProvider_2}}"/> </StackPanel> </Page>
//PhotoList pl = Resources["photos"] as PhotoList; PhotoList pl = ((ObjectDataProvider)Resources["ObjectProvider_2"]).Data as PhotoList; pl.Add(new PhotoModel("lulu", "66" + pl.Value.ToString(), 4, DateTime.Now));
2.绑定到源对象的一个方法
<Page.Resources> <!-- 绑定一个静态方法 --> <ObjectDataProvider x:Key="ObjectProvider" ObjectType="{x:Type local:PhotoList}" MethodName="GetValue"> <ObjectDataProvider.MethodParameters> <sys:String>lulu</sys:String> </ObjectDataProvider.MethodParameters> </ObjectDataProvider> </Page.Resources> <StackPanel> <TextBlock Text="{Binding Source={StaticResource ObjectProvider}}"></TextBlock> </StackPanel>
namespace WPF_Test { //PhotoList继承ObservableCollection可实现自动跟新绑定值 class PhotoList : System.Collections.ObjectModel.ObservableCollection<PhotoModel> { public static string GetValue(string value) { return value; } } }
3.有更多选项做异步数据绑定
自定义数据流
Binding支持目标属性直接被用户修改,可以让数据回源。
通过设置Mode属性实现,其值可为:
BindingMode.OneWay:当源改变,目标会跟新
TwoWay:源或目标改变,导致另一方面被跟新
OneWayToSource:OneWay反向方式,目标改变,源会更新
OneTime:同OneWay,但源的改变不会被反映在目标上
注:TextBox.Text无法绑定到集合的Count属性,因为Count属性只读,TextBox.Text默认为TwoWay绑定,需要提供一个可读写属性,需显示修改TextBox绑定模式为OneWay
由于绑定模式的不同,值转换器才有Convert也有ConverBack,当采用TwoWay是,2个方法都有机会调用。
当使用了TwoWay或OneWayToSource绑定方式后,可指定何时跟新,如何跟新数据源。
通过Binding的UpdateSourceTrigger属性指定:
UpdateSourceTrigger.PropertyChanged:只要目标属性值改变,源跟新
LostFocus:目标属性值改变后,目标失去焦点,源跟新
Explicit:显示调用BindingExpression.UpdateSource时,源跟新(通过调用Element的GetBindingExpression方法获取)
向绑定添加验证规则
跟新数据,会调用验证检查,用规则否决本次跟新后,将数据标记无效。
数据标记无效后,默认会给元素一个新控件模板,显示红色系框。可通过Validation.ErrorTemplate附加属性实现自定义模板。
数据标记无效后,元素Validation.HasError变为true,同时Validation.Error附加事件也会触发(仅当Binding的NotifyOnValidationError设置为true才有效)
<TextBlock Name="txtBlock_1" Text="lulu"></TextBlock> <TextBox Name="txt_1"> <TextBox.Text> <Binding ElementName="txtBlock_1" Path="Text"> <Binding.ValidationRules> <!-- 自定义验证法则,在文本失去焦点后,数据转换前调用,将不合法数据标记为无效 --> <local:JpgValidation /> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox>
class JpgValidation:ValidationRule { public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo) { string fileName = value.ToString(); //检查字符串是否以.jpg结束 if (!fileName.EndsWith(".jpg", StringComparison.InvariantCultureIgnoreCase)) { return new ValidationResult(false, "不为jpg文件"); } return new ValidationResult(true, null); } }
使用不相交源
1.CompositeCollection
提供独立集合或单个集合中的任意项混合。
<Page.Resources> <local:PhotoList x:Key="photoList"/> <CompositeCollection x:Key="CompositeCollection"> <!-- 将photoList中的项目作为CompositeCollection的一部分,而非photoList --> <!-- 因此CompositeCollection中项总数为2+photoList.Count --> <CollectionContainer Collection="{Binding Source={StaticResource photoList}}"/> <!-- PhotoModel为含无参构函的类 --> <local:PhotoModel/> <local:PhotoModel/> </CompositeCollection> </Page.Resources> <ListBox ItemsSource="{Binding Source={StaticResource CompositeCollection}}" />
2.MultiBinding
允许多个绑定汇集,输出给一个单独目标值。
<Page.Resources> <local:PhotoListConverter x:Key="photoListConverter" /> <local:PhotoModel x:Key="pmodel1"/> <local:PhotoModel x:Key="pmodel2"/> </Page.Resources> <TextBox> <!-- 通过指定转换器,汇合多个绑定值,付给一个元素,设定绑定模式OneWay --> <MultiBinding Converter="{StaticResource photoListConverter}" Mode="OneWay"> <Binding Source="{StaticResource pmodel1}" Path="Name" /> <Binding Source="{StaticResource pmodel2}" Path="Name" /> </MultiBinding> </TextBox>
public class PhotoListConverter : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) { string total = string.Empty; foreach (object value in values) { total += value.ToString()+","; } return total.TrimEnd(','); } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } }
3.PriorityBinding
也封装多个Binding对象,让多个Binding为设置目标而竞争!
如果绑定到一个较慢数据源,在等待时可能需要一个较快的临时数据版本。
<Page.Resources> <local:SlowSpeed x:Key="HightPri"/> <local:MediumSpeed x:Key="MediumPri"/> <local:FastSpeed x:Key="LowPri"/> </Page.Resources> <TextBlock> <TextBlock.Text> <!-- 最后显示结果为SlowSpeed --> <PriorityBinding> <!-- 优先级最高,操作耗时最多,应设为异步操作,避免UI阻塞 --> <Binding Source="{StaticResource HightPri}" Path="Value" IsAsync="True" /> <!-- 操作耗时较多,应设为异步操作,避免UI阻塞 --> <Binding Source="{StaticResource MediumPri}" Path="Value" IsAsync="True" /> <!-- 优先级最低,操作耗时最少,应设为同步操作 --> <Binding Source="{StaticResource LowPri}" Path="Value" /> </PriorityBinding> </TextBlock.Text> </TextBlock>
public class SlowSpeed { private string _value; public string Value { get { return _value; } set { _value = value; } } public SlowSpeed() { Thread.Sleep(5000); _value = "SlowSpeed"; } } public class MediumSpeed { private string _value; public string Value { get { return _value; } set { _value = value; } } public MediumSpeed() { Thread.Sleep(1000); _value = "MediumSpeed"; } } public class FastSpeed { private string _value; public string Value { get { return _value; } set { _value = value; } } public FastSpeed() { _value = "FastSpeed"; } }