ObjectDataProvider就是把对象作为数据源提供给Binding。同XmlDataProvider的父类都是DataSourceProvider抽象类。
事例一:
XAML:
<Grid Background="LightBlue">
<Button Click="Button_Click"/>
</Grid>
C#代码:
private void Button_Click(object sender, RoutedEventArgs e)
{
ObjectDataProvider odp = new ObjectDataProvider();
odp.ObjectInstance = new Calculator();
odp.MethodName = "Add";
odp.MethodParameters.Add("100");
odp.MethodParameters.Add("200");
MessageBox.Show(odp.Data.ToString());
}
事例二:
XAML:
<StackPanel Background="LightBlue">
<TextBox x:Name="textBoxArg1" Margin="5"/>
<TextBox x:Name="textBoxArg2" Margin="5"/>
<TextBox x:Name="textBoxResult" Margin="5"/>
</StackPanel>
C#代码:
public MainWindow()
{
InitializeComponent();
SetBinding();
}
private void SetBinding()
{
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,
BindsDirectlyToSource = true,
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
};
Binding bindingToArg2 = new Binding("MethodParameters[1]")
{
Source = odp,
BindsDirectlyToSource = true,
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
};
Binding bindingToResult = new Binding(".") { Source = odp };
this.textBoxArg1.SetBinding(TextBox.TextProperty, bindingToArg1);
this.textBoxArg2.SetBinding(TextBox.TextProperty, bindingToArg2);
this.textBoxResult.SetBinding(TextBox.TextProperty, bindingToResult);
}
另外一种方法来创建被包装的对象,就是告诉ObjectDataProvider将被包装对象的类型和希望调用的构造器,让ObjectDataProvider自己去创建被包装的对象,代码大概如下:
odp.ObjectType = typeof(YourClass);
odp.ConstructorParameters.Add(arg1);
odp.ConstructorParameters.Add(arg2);
因为在XAML里创建和使用对象比较麻烦、可读性差,所以一般会在XAML里使用这种指定类型和构造器的办法。
BindsDirectlyToSource = true这句的意思是告诉Binding对象只负责把从UI元素收集到的数据写入其直接Source(即ObjectDataProvider对象)而不是被ObjectDataProvider包装着的对象。
注意:在把ObjectDataProvider对象当作Binding的Source来使用时,该对象本身就代表数据,故Path使用"."而非Data属性。
当不能确定作为Source的对象是什么,但知道它与作为Binding目标的对象在UI布局上有相对关系,此时就需要使用Binding的RelativeSource属性。该属性的数据类型为RelativeSource类,通过这个类的几个静态或非静态方法就可以控制它搜索相对数据源的方式。
XAML:
<Grid x:Name="g1" Background="Red" Margin="10">
<DockPanel x:Name="d1" Background="Orange" Margin="10">
<Grid x:Name="g2" Background="Yellow" Margin="10">
<DockPanel x:Name="d2" Background="LawnGreen" Margin="10">
<TextBox x:Name="textBox1" FontSize="24" Margin="10"/>
</DockPanel>
</Grid>
</DockPanel>
</Grid>
C#代码:
public MainWindow()
{
InitializeComponent();
RelativeSource rs = new RelativeSource(RelativeSourceMode.FindAncestor);
rs.AncestorLevel = 1;
rs.AncestorType = typeof(Grid);
Binding binding = new Binding("Name") { RelativeSource = rs };
this.textBox1.SetBinding(TextBox.TextProperty, binding);
}
或在XAML中插入等效代码:
Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}, AncestorLevel=1}, Path="Name"}"
AncestorLevel属性指的是以Binding目标控件为起点的层级偏移量;AncestorType属性告诉Binding寻找哪个类型的对象作为自己的源,不是这个类型的对象会被跳过。