自定義Binding
當(dāng)為Binding設(shè)置了繼承System.ComponentModel.INotifyPropertyChanged的數(shù)據(jù)源類,Binding會自動偵聽來自這個接口的PropertyChanged事件。
<TextBox x:Name="textbox" Width="200" Height="100"></TextBox>
<Button Content="click me" Click="Button_Click"></Button>
...
using System.ComponentModel;
public partial class Window1 : Window
{
Student stu;
public Window1()
{
InitializeComponent();
stu = new Student();
Binding binding = new Binding("Name") { Source = stu };
//Binding binding = new Binding(){ Path = new PropertyPath("Name"), Source = stu };
textbox.SetBinding(TextBox.TextProperty, binding);
//BindingOperations.SetBinding(textbox, TextBox.TextProperty, binding);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
stu.Name += "Name";
}
}
class Student : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string name;
public string Name
{
get { return name; }
set
{
name = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name"));
}
}
}
控件間的Binding
<TextBox x:Name="textbox2" Width="200" Text="{Binding Path=Value,ElementName=slider}"></TextBox>
<!--<TextBox x:Name="textbox2" Width="200" Text="{Binding Value,ElementName=slider}"></TextBox>-->
<Slider x:Name="slider" Maximum="100" Minimum="0" Width="100"></Slider>
等價于
<TextBox x:Name="textbox2" Width="200"></TextBox>
<Slider x:Name="slider" Maximum="100" Minimum="0" Width="100"></Slider>
...
textbox2.SetBinding(TextBox.TextProperty, new Binding() { Path = new PropertyPath("Value"), ElementName = "slider" });
//textbox2.SetBinding(TextBox.TextProperty, new Binding("Value") { ElementName = "slider" });
此處ElementName = "slider"與Source = slider等價
Binding方向與數(shù)據(jù)更新
Mode
決定數(shù)據(jù)綁定的方向,其值為BindingMode枚舉
public enum BindingMode
{
TwoWay = 0,
OneWay = 1,//只讀
OneTime = 2,
OneWayToSource = 3,//只寫
Default = 4
}
UpdateSourceTrigger
決定數(shù)據(jù)更新的時機,其值為UpdateSourceTrigger枚舉
public enum UpdateSourceTrigger
{
Default = 0,
PropertyChanged= 1,
LostFocus = 2,
Explicit = 3
}
Binding的路徑 (Path)
由于Binding的重載,Path值可以作為new Binding()實例的入?yún)⒁部梢宰鳛榭杖雲(yún)嵗膶傩?。以下語法等效:
{Binding Text}
{Binding Path=Text}
...
new Binding("Text"){}
new Binding(){Path=new PropertyPath("Text")}
Path屬性決定需要暴露的屬性值,其值可以為PropertyPath實例,且支持多級路徑
Path = new PropertyPath("Text.Length")
- 當(dāng)
Source本身就是數(shù)據(jù)(如string或int),不需要Path指明時,Path的值可設(shè)為".",在XAML中可直接省略Path屬性
Binding的Source
使用DataContext作為Binding的源
UI樹的每一個節(jié)點都有DataContext,當(dāng)一個Binding只有Path沒有Source時會從當(dāng)前元素開始向上逐級查找具有該Path屬性的DataContext(其實本質(zhì)上是DataContext作為一個依賴屬性,當(dāng)標(biāo)簽不存在某屬性時會從其父級元素繼承)
<WrapPanel x:Name="WrapPanel" VerticalAlignment="Center">
<WrapPanel.DataContext>
<local:Man Age="20"></local:Man>
</WrapPanel.DataContext>
<TextBox Text="{Binding Age}" Width="200"></TextBox>
</WrapPanel>
//或
WrapPanel.DataContext=new Man(){Age="20"};
-
DataContext屬性是直接采用其中的對象,而不是把對象作為其一個屬性 - 更常用的是直接對頂層的
DataContext賦值為當(dāng)前實例
<Button Content="{Binding A}" Width="50"></Button>
...
public partial class Dependency : Window
{
public string A
{
get; set;
}
public Dependency()
{
InitializeComponent();
A = "Click Me";
DataContext = this;
}
}
注意通過DataContext只能初始化數(shù)據(jù)到視圖并單向綁定,雙向綁定還是需要事件機制
列表控件與ItemsSource
通常使用System.ComponentModel.ObservableCollection類型代替List,因其實現(xiàn)了INotifyPropertyChanged接口,其變化可以通知到視圖界面。
<ListBox x:Name="myListBox" Height="100"></ListBox>
...
myListBox.ItemsSource = new ObservableCollection<string>() { "A","B","C" };
//或
myListBox.ItemsSource = new ObservableCollection<object>() { new { name = "apple", diam = 4 },new { name = "banana", diam = 5 } };
myListBox.DisplayMemberPath = "name";
若不設(shè)置DisplayMemberPath,也可通過對ItemTemplate屬性賦值(DataTemplate類型)可以詳細(xì)設(shè)置列表內(nèi)容
<ListBox x:Name="myListBox" Height="100">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBox Text="{Binding name}" Width="200"></TextBox>
<TextBox Text="{Binding diam}" Width="200"></TextBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
導(dǎo)入XML作為Binding的數(shù)據(jù)源
使用XPath而非Path指定來源。
<?xml version="1.0" encoding="utf-8"?>
<StudentList>
<Student Id="1">
<Name>Tom</Name>
</Student>
<Student Id="2">
<Name>Jack</Name>
</Student>
</StudentList>
...
<ListView x:Name="myListView" Height="100">
<ListView.View>
<GridView>
<GridViewColumn Header="Id" Width="80" DisplayMemberBinding="{Binding XPath=@Id}"></GridViewColumn>
<GridViewColumn Header="Name" Width="80" DisplayMemberBinding="{Binding XPath=Name}"></GridViewColumn>
</GridView>
</ListView.View>
</ListView>
...
XmlDocument doc = new XmlDocument();
doc.Load(@"../../student.xml");
XmlDataProvider xdp = new XmlDataProvider()
{
Document = doc,
XPath = @"/StudentList/Student"
};
myListView.DataContext = xdp;
myListView.SetBinding(ListView.ItemsSourceProperty, new Binding());
-
XmlDataProvider用于將XML作為數(shù)據(jù)源提供給Binding - 加
@的是XML的屬性,不加的是其子級元素。 - 也可不聲明
doc,改為xdp.Source=new Uri(@"../../student.xml")
內(nèi)聯(lián)XML作為Binding的數(shù)據(jù)源
<Grid.Resources>
<XmlDataProvider x:Key="ExpenseDataSource" XPath="Expenses">
<x:XData>
<Expenses xmlns="">
<Person Name="Mike" Department="Legal">
<Expense ExpenseType="Lunch" ExpenseAmount="50" />
<Expense ExpenseType="Transportation" ExpenseAmount="50" />
</Person>
<Person Name="Lisa" Department="Marketing">
<Expense ExpenseType="Document printing"
ExpenseAmount="50"/>
<Expense ExpenseType="Gift" ExpenseAmount="125" />
</Person>
<Person Name="John" Department="Engineering">
<Expense ExpenseType="Magazine subscription"
ExpenseAmount="50"/>
<Expense ExpenseType="New machine" ExpenseAmount="600" />
<Expense ExpenseType="Software" ExpenseAmount="500" />
</Person>
<Person Name="Mary" Department="Finance">
<Expense ExpenseType="Dinner" ExpenseAmount="100" />
</Person>
</Expenses>
</x:XData>
</XmlDataProvider>
<DataTemplate x:Key="nameItemTemplate">
<Label Content="{Binding XPath=@Name}"/>
</DataTemplate>
</Grid.Resources>
...
<ListBox Name="peopleListBox" Grid.Column="1" Grid.Row="2"
ItemsSource="{Binding Source={StaticResource ExpenseDataSource}, XPath=Person}"
ItemTemplate="{StaticResource nameItemTemplate}">
</ListBox>
ObjectDataProvider
將對象/方法返回值作為數(shù)據(jù)源提供給 Binding
RelativeSource 通過標(biāo)簽位置關(guān)系進行綁定
ElementName通過元素名綁定,Source通過元素對象/數(shù)據(jù)對象綁定,RelativeSource則通過標(biāo)簽的位置關(guān)系進行綁定
Binding 的數(shù)據(jù)校驗
為ValidationRules屬性添加ValidationRule類型對象
Binding 的數(shù)據(jù)轉(zhuǎn)換
為Converter屬性創(chuàng)建繼承IValueConverter的類
多路Binding
MultiBinding類可以將一組Binding對象(可以分別擁有自己的校驗與轉(zhuǎn)換機制)聚合起來,最后共同決定傳出的數(shù)據(jù)。
以下demo為textBox1和textBox2數(shù)據(jù)一致且textBox3和textBox4數(shù)據(jù)一致時button為可點擊狀態(tài) :
<StackPanel>
<TextBox x:Name="textBox1" Height="23" Margin="5"></TextBox>
<TextBox x:Name="textBox2" Height="23" Margin="5"></TextBox>
<TextBox x:Name="textBox3" Height="23" Margin="5"></TextBox>
<TextBox x:Name="textBox4" Height="23" Margin="5"></TextBox>
<Button x:Name="button" Content="click me" Width="80" Margin="5"></Button>
</StackPanel>
...
public partial class Multi : Window
{
public Multi()
{
InitializeComponent();
SetMultiBinding();
}
private void SetMultiBinding()
{
Binding b1 = new Binding("Text") { Source = textBox1 };
Binding b2 = new Binding("Text") { Source = textBox2 };
Binding b3 = new Binding("Text") { Source = textBox3 };
Binding b4 = new Binding("Text") { Source = textBox4 };
MultiBinding mb = new MultiBinding() { Mode=BindingMode.OneWay };
mb.Bindings.Add(b1);//順序敏感
mb.Bindings.Add(b2);
mb.Bindings.Add(b3);
mb.Bindings.Add(b4);
mb.Converter = new myConverter();
button.SetBinding(Button.IsEnabledProperty, mb);
}
}
public class myConverter : IMultiValueConverter
{
public object Convert(object[] values,Type targetType,object param,CultureInfo culture)
{
if(!values.Cast<string>().Any(text=>string.IsNullOrEmpty(text))
&& values[0].ToString()==values[1].ToString()
&& values[2].ToString() == values[3].ToString())
{
return true;
}
return false;
}
public object[] ConvertBack(object value,Type[] targetType, object param, CultureInfo culture)
{
throw new NotImplementedException();
}
}