Binding的Source从何而来?
I. Binding to Object
1. Binding data using ObjectDataProvider
AC:Let’s say there is a CLR based data bound object that you are trying to implement. For example a collection of Tool objects, e.g.
- An Object called Tool that contains a bunch of properties that we want to bind (in this case it just contains a description)
- A Collection of Tool objects(ToolsCollection); for example to populate a list box etc
- A Factory to get the reference of ToolsCollection objects which have collection<Tools>
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CLRDataBinding { public class Tool : INotifyPropertyChanged { private string _description = ""; public string Description { get { return _description; } set { if (_description != value) { _description = value; NotifyPropertyChanged("Description"); } } } public Tool(string description) { _description = description; } public event PropertyChangedEventHandler PropertyChanged; protected void NotifyPropertyChanged(string propName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propName)); } } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Collections.ObjectModel; namespace CLRDataBinding { public class ToolsCollection : ObservableCollection<Tool> { public ToolsCollection() { CreateToolsData(); } private void CreateToolsData() { for (int loop = 0; loop < 1000; loop++) { this.Add(new Tool("Tool " + loop.ToString())); } } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CLRDataBinding { public class Factory { public ToolsCollection MyToolsCollection { get { return new ToolsCollection(); } } } }
<Window x:Class="CLRDataBinding.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:clr="clr-namespace:CLRDataBinding" Title="MainWindow" Width="525" Height="350"> <Window.Resources> <ObjectDataProvider x:Key="FactoryDP" ObjectType="{x:Type clr:Factory}" /> <DataTemplate x:Key="ToolItemTemplate"> <StackPanel> <TextBlock Text="{Binding Description}" /> </StackPanel> </DataTemplate> </Window.Resources> <Grid> <ListBox ItemTemplate="{StaticResource ToolItemTemplate}" ItemsSource="{Binding Path=MyToolsCollection, Source={StaticResource FactoryDP}}" /> </Grid> </Window>
之前一篇文章 对象数据绑定 里提到三个例子,第一个例子用了ObjectDataProvider没有用DataContext,第二个例子用了DataContext没有用 ObjectDataProvider,第三个例子既用了ObjectDataProvider也用了DataContext,但没有提到它们的区别,正 好在Beatriz Costa的blog上看到一篇好文章解释了为什么需要 ObjectDataProvider的问题。
ObjectDataProvider能实现四个特殊功能:
1. 传递参数到构造函数中
使用下面的XAML语句定义一个ObjectDataProvider,它会自动调用MySource类的默认构造函数初始化类
<ObjectDataProvider ObjectType="{x:Type local:MySource}" x:Key="odp1"/>
如果MySource类的构造函数允许传入参数的话,就可以这样定义ObjectDataProvider:
<ObjectDataProvider ObjectType="{x:Type local:MySource}" x:Key="odp1">
<ObjectDataProvider.ConstructorParameters>
<system:String>Jupiter</system:String>
</ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>
2. 绑定到方法
ObjectDataProvider 除了 ObjectType的属性外还有MethodName的属性,MethodName属性将ObjectDataProvider绑定到方法,相当于是对数据源的包装,另外也可以定义方法的传入参数:
<ObjectDataProvider ObjectInstance="{StaticResource odp1}" MethodName="WeightOnPlanet" x:Key="odp2">
<ObjectDataProvider.MethodParameters>
<system:Double>95</system:Double>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
3. 替换数据对象
和使用自己在resource中定义对象不同,ObjectDataProvider可以使里很简单的更换绑定的数据对象,只需要更换一下绑定的数据对象的名字。而使用自己在resource中定义对象,即使定义了相同的x:Key也不能达到自动更新的目的。
另,这里使用DataContext也能达到与ObjectDataProvider同样的效果。
4. 建立异步的数据对象
ObjectDataProvider有IsAsynchronous 可以将数据对象定义为异步的。
默认情况下ObjectDataProvider是同步的,XmlDataProvider是异步。
2. Binding DataContext in Window.DataContext (ObjectDataProvider not needed)
AuctionItem.cs: define Class AuctionItem : INotifyPropertyChanged
MyApp.xaml.cs: define Class AuctionItems : ObservableCollection<AuctionItem>
Window1.xaml.cs: use [CollectionViewSource cv = root.DataContext as CollectionViewSource;] to action in button event handler
Binding Process:
1)Define DataContext in Window1.xaml:
<Window.DataContext> <CollectionViewSource> <CollectionViewSource.Source> <local:AuctionItems/> </CollectionViewSource.Source> </CollectionViewSource> </Window.DataContext>
2) Binding Listbox:
<ListBox ItemsSource="{Binding Path=.}" IsSynchronizedWithCurrentItem="True">
3) Binding label: (the current item in listbox)
<Label Content="{Binding Path=/}" >
4) DataTemplate
<DataTemplate DataType="{x:Type local:AuctionItem}" > ... <Image> <Image.Source> <Binding Path="Image"/> </Image.Source> </Image> ...
More: this sample also shows how to use Converter and MultiBinding
3. Binding DataContext in controls to Global ObjectDataProvider
sample:
MyApp.xaml:
<Application.Resources> ... <ObjectDataProvider x:Key="Employees" ObjectType="{x:Type local:MSEmployeeCollection}"/> </Application.Resources>
Window1.xaml:
<StackPanel Margin="10" DataContext="{StaticResource Employees}"> ... <Button Content="{Binding Path=[0]}" /> <ComboBox ItemsSource="{Binding}" SelectedIndex="0" /> </StackPanel>
1) DataContext another format : binding to StackPanel
2) Button bind to single item [0]
3) ComblBox bind to multi items, so no Path
II. Binding to XML
1. Use Source include .xml file to XmlDataProvider
<?xml version='1.0' encoding='utf-8'?> <StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Margin="10"> <StackPanel.Resources> <XmlDataProvider x:Key="Blog" Source="http://home.wangjianshuo.com/index.xml"/> <DataTemplate x:Key="TitleTemplate"> <TextBlock Text="{Binding XPath=title}"/> </DataTemplate> </StackPanel.Resources> <Label Content="{Binding Source={StaticResource Blog}, XPath=/rss/channel/title}" FontSize="24" FontWeight="Bold" /> <Label Content="{Binding Source={StaticResource Blog}, XPath=/rss/channel/description}" FontSize="18" /> <DockPanel DataContext="{Binding Source={StaticResource Blog}, XPath=/rss/channel/item}" > <ListBox DockPanel.Dock="Left" ItemsSource="{Binding}" ItemTemplate="{StaticResource TitleTemplate}" IsSynchronizedWithCurrentItem="True" /> <TextBox Name="Contents" Text="{Binding XPath=description}" TextWrapping="Wrap" Width="Auto" /> </DockPanel> </StackPanel>
2. Use x:XData define the xml structure inside current XmlDataProvider
<StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <StackPanel.Resources> <XmlDataProvider x:Key="FavoriteColors"> <x:XData> <Colors xmlns=""> <Color>Blue</Color> <Color>Black</Color> <Color>Green</Color> <Color>Red</Color> </Colors> </x:XData> </XmlDataProvider> </StackPanel.Resources> <TextBlock HorizontalAlignment="Center" FontWeight="Bold"> XML Example </TextBlock> <ListBox Width="200" Height="300" ItemsSource="{Binding Source={StaticResource FavoriteColors}, XPath=/Colors/Color}"> </ListBox> </StackPanel>
III. Binding to Control
1:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <Canvas> <TextBox Name="theTextBox" Text="Hello" /> <TextBlock Canvas.Top="25"> <TextBlock.Text> <Binding ElementName="theTextBox" Path="Text" /> </TextBlock.Text> </TextBlock> </Canvas> </Window>
2:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <Canvas> <TextBox Name="theTextBox" Text="Hello" /> <TextBlock Canvas.Top="25" Text="{Binding ElementName=theTextBox, Path=Text}" /> </Canvas> </Window>
IX. Binding to ADO.NET
Similar to binding to object, create a dataset and return ds to DataContext.