1、依赖属性、依赖对象、xaml标记扩展
https://docs.microsoft.com/en-us/dotnet/desktop/wpf/data/data-binding-overview?view=netframeworkdesktop-4.8
不论要绑定什么元素,也不论数据源是什么性质,每个绑定都始终遵循下图所示的模型。
形象地讲,Binding就像一个盒子,盒子里装了一些机关用于过滤和控制数据,盒子两端各接着一根管子,管子是由管壳和管芯构成的,看上去就像下面的图:
当脑子里有了这样一个形象之后,遵循下面的步骤就OK了:
如果有必要,可以在3与4之间设置Binding的“关卡”们。其实,第3步之后的顺序不是固定的,只是这个步骤比较好记——一概向右连接。所得结果看上去是这样:
通常情况下,每个绑定具有四个组件:
DependencyObject
。)D.P.的全称是“Dependency Property”,直译过来就是“依赖式属性”,意思是说它自己本身是没有值的,它的值是“依赖”在其它对象的属性值上、通过Binding的传递和转换而得来的。表现在例子里,它就是Target上的被数据所驱动的联动属性了!
这里是等价的C#代码,我把它写在了Window1的构造函数里:
public Window1() { InitializeComponent(); // 1. 我打算用slider1作为Source // 2. 我打算用textBox1作为Target Binding binding = new Binding(); binding.Source = this.slider1; //默认源是 binding.Path = new PropertyPath("Value"); //Value是属性名称 Path不能xml类,xmldom要用xpath this.textBox1.SetBinding(TextBox.TextProperty, binding); }
有意思的是,Source端的操作,接管子和插管芯分两步,而Target端却是在SetBinding方法中一步完成。注意啦,TextBox.TextProperty就是一个Dependency Property的庐山真面目!
上面的代码稍有简化的余地,那就是把Path的设定转移到Binding的构造中去:
public Window1() { InitializeComponent(); // 1. 我打算用slider1作为Source // 2. 我打算用textBox1作为Target Binding binding = new Binding("Value"); binding.Source = this.slider1;//默认源是DataContent this.textBox1.SetBinding(TextBox.TextProperty, binding); }
我们还可以为binding设些“关卡”:
public Window1() { InitializeComponent(); // 1. 我打算用slider1作为Source // 2. 我打算用textBox1作为Target Binding binding = new Binding("Value"); binding.Source = this.slider1;//默认源是DataContent binding.Mode = BindingMode.TwoWay;//4种依赖源模式 binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;//4种方式更新源 this.textBox1.SetBinding(TextBox.TextProperty, binding); }
Windows Presentation Foundation (WPF) 数据绑定支持以下绑定源类型:
.NET 公共语言运行时 (CLR) 对象
可以绑定到任何公共语言运行时 (CLR) 对象的公共属性、子属性和索引器。 绑定引擎使用 CLR 反射来获取属性值。 实现了 ICustomTypeDescriptor 或具有已注册 TypeDescriptionProvider 的对象也可以使用绑定引擎。
有关如何实现可用作绑定源的类的详细信息,请参阅本文后面的在对象上实现绑定源。
动态对象
可以绑定到对象的可用属性和索引器,该对象实现 IDynamicMetaObjectProvider 接口。 如果可以访问代码中的成员,则可以绑定到该成员。 例如,如果动态对象使用户可以通过 someObjet.AProperty
访问代码中的成员,则可以通过将绑定路径设置为 AProperty
来绑定到该成员。
ADO.NET 对象
可以绑定到 ADO.NET 对象,例如 DataTable。 ADO.NET DataView 实现 IBindingList 接口,该接口提供绑定引擎侦听的更改通知。
XML 对象
可以绑定到 XmlNode、XmlDocument 或 XmlElement,并对其运行 XPath
查询。 访问 XML 数据(标记中的绑定源)的便捷方法是使用 XmlDataProvider 对象。 有关详细信息,请参阅使用 XMLDataProvider 和 XPath 查询绑定到 XML 数据 (.NET Framework)。
使用 LINQ to XML,还可以绑定到 XElement 或 XDocument,或者绑定到对这些类型的对象运行查询而得到的结果。 使用 LINQ to XML 访问 XML 数据(标记中的绑定源)的便捷方法是使用 ObjectDataProvider 对象。 有关详细信息,请参阅绑定到 XDocument、XElement 或 LINQ for XML 查询结果 (.NET Framework)。
DependencyObject 对象
可以绑定到任何 DependencyObject 的依赖属性。 有关示例,请参阅绑定两个控件的属性 (.NET Framework)。
如果使用的是 OneWay 或 TwoWay 绑定(因为当绑定源属性动态更改时,希望自己的 UI 随之更新),必须实现适当的属性更改通知机制。 对于 CLR 或动态类,建议的机制是实现 INotifyPropertyChanged 接口。 有关详细信息,请参阅实现属性更改通知。
如果创建的 CLR 对象未实现 INotifyPropertyChanged,必须安排自己的通知系统,才能确保绑定中所用的数据保持最新状态。 可以通过支持要更改通知的每个属性的 PropertyChanged
模式来提供更改通知。 若要支持此模式,请为每个属性定义一个 PropertyNameChanged 事件,其中 PropertyName 是属性的名称。 每次更改属性时都会引发该事件。
如果绑定源实现了其中一个通知机制,将会自动进行目标更新。 如果绑定源由于某种原因未提供正确的属性更改通知,则可以选择使用 UpdateTarget 方法来显式更新目标属性。
自定义数据源:
在我们项目组日常的工作中,经常需要自己写一个类,并且拿它的实例当作数据源。怎样才能让一个类成为“合格的”数据源呢?
要诀就是:
为这个类定义一些Property,相当于为Binding提供Path
让这个类实现INotifyPropertyChanged接口。实现这个接口的目的是当Source的属性值改变后通知Binding(不然人家怎么知道源头的数据变了并进行联动协同呢?),好让Binding把数据传输给Target——本质上还是使用事件机制来做,只是掩盖在底层、不用程序员去写event handler了。
public class MyMarkupExtension : MarkupExtension, INotifyPropertyChanged { public MyMarkupExtension() { } private string firstStr; public String FirstStr { get => firstStr; set { firstStr = value; NotifyPropertyChanged(); } } public MyMarkupExtension(string firstStr) { } private void NotifyPropertyChanged([CallerMemberName] String propertyName = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public String SecondStr { get; set; } public event PropertyChangedEventHandler? PropertyChanged; public override object ProvideValue(IServiceProvider serviceProvider) { return FirstStr + " " + SecondStr; } }
应用
Binding binding = new Binding(); binding.Source = myMarkupExtension; binding.Path = new PropertyPath("FirstStr"); teb.SetBinding(TextBlock.TextProperty, binding);
OK,此时,你可以尝试使用MyMarkupExtension类的实例作为数据源了!MyMarkupExtension 同时也解除扩展标记接口,因此也是一个自定义扩展标记。
原理:利用依赖对象提供的ClearValue()方法解除绑定
BindingOperations类包装依赖对象的ClearValue()方法后提供了.ClearBinding()\ClearAllBinding(),用法如下:
BindingOperations.ClearBinding(lbl, Label.FontSizeProperty);//解除lable元素的FontSizeProperty依赖属性的绑定 BindingOperations.ClearAllBindings(lbl);//解除lable元素的所有属性的绑定
在数据绑定中,绑定源对象是指用户从其获取数据的对象。 本文讨论可以用作绑定源的对象类型,如 .NET CLR 对象、XML 和 DependencyObject 对象。
数据绑定是一个WPF特性,因此你可以把任何依赖对象属性绑定到目标值。然而,数据绑定的源不是必须是依赖属性;它可以是任何属性类型,只要这个属性类型能被应用程序数据提供器识别就可以。PropertyPath特别是用于ObjectDataProvider,对象数据提供器用来从公共运行时(CLR)对象和它们的属性中获取绑定源。
记住,XML数据绑定不适用PropertyPath,因为它在Binding中不用Path,而是XPath。你可以用XPath和有效的XPath语法指向数据的XML DOM。XPath也是被规定为字符串,但是不在这里讲解。
理解数据绑定中的属性路径的关键是你能把绑定定位到它的属性值,或者你可以绑定到列表或者集合属性。如果你要绑定到集合,比如绑定一个ListBox,ListBox会根据集合中项目的数量自动进行扩展。
绑定对象数据提供器ObjectDataProvider绑定方法
使用ObjectDataProvider对象作为Binding的Source
语法
<object Path="[index1,index2...]" ... /> <object Path="propertyName[index,index2...]" ... /> <Rectangle Fill="{Binding ColorGrid[20,30].SolidColorBrushResult}" ... /> <Binding Path="propertyName.propertyName2" ... /> <object Path="propertyName/propertyNameX" ... /> <object Path="[index1,index2...]" ... /> <object Path="propertyName[index,index2...]" ... />
详细请点击