绑定对象:
数据绑定关键是System.Windows.Data.Binding对象,会将2个属性粘在一起,并在它们间建立通信信道。
1.代码实现绑定
//创建一绑定对象 Binding binding = new Binding(); //设置数据源对象(此处数据源来至textBox1) binding.Source = textBox1; //设置数据源对象的哪一个属性提供数据 binding.Path = new PropertyPath("Text"); //将textBlock1的依赖属性Text和绑定对象关联 textBlock1.SetBinding(TextBlock.TextProperty, binding); //绑定的另一种方法,第一个参数为DependencyObject对象 //支持不是从FrameworkElement或FrameworkContentElement继承而来的对象数据绑定 //BindingOperations.SetBinding(textBlock1, TextBlock.TextProperty, binding); //移除绑定 //BindingOperations.ClearBinding(textBlock1, TextBlock.TextProperty);
2.XAML中实现绑定
<TextBox Height="23" HorizontalAlignment="Left" Margin="12,12,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" /> <!-- 此处Binding用无参构函构造,后在设置其2个属性值 --> <TextBlock Height="23" HorizontalAlignment="Left" Margin="12,41,0,0" Name="textBlock1" VerticalAlignment="Top" Text="{Binding ElementName=textBox1, Path=Text}" /> <!-- 另一绑定方式:此处Binding用含一个参数构函构造,后在设置其1个属性值 --> <TextBlock Height="23" HorizontalAlignment="Left" Margin="12,41,0,0" Name="textBlock1" VerticalAlignment="Top" Text="{Binding Text, ElementName=textBox1}" />
注意:XAML使用Binding的ElementName属性设置源对象,其实这2个属性都有效,只是在XAML中使用ElementName更方便,只需给源元素名就可以了。
在XAML中设置Source,目标对象必须被定义在某Resources资源中。
<Page.Resources> <TextBox x:Key="textBox1" Text="lulu"></TextBox> </Page.Resources> <StackPanel> <!-- Binding使用Source指定目标元素 --> <TextBlock Height="23" HorizontalAlignment="Left" Margin="12,41,0,0" Name="textBlock1" VerticalAlignment="Top" Text="{Binding Text, Source={StaticResource textBox1}}" /> </StackPanel>
与普通.NET属性绑定:
<Page.Resources> <!-- 定义一个集合 --> <ResourceDictionary x:Key="dictionary"> <Color x:Key="1" A="255" R="255" G="255" B="255"/> <Color x:Key="2" A="255" R="255" G="0" B="0"/> </ResourceDictionary> </Page.Resources> <!-- Label值显示2,因为集合不是FrameworkElement也不是FrameworkContentElement,因此放在逻辑资源里通过StaticResource访问 --> <Label Content="{Binding Count, Source={StaticResource dictionary}}" />
注:使用普通.NET属性为数据源绑定,因为没有依赖属性的自动变更机制,源属性值改变,目标属性不会变化。
绑定整个对象:
源属性(即Binding的Path属性)是可选的!省略该属性将制动绑定整个对象。
<!-- Label值显示:(集合) --> <Label Content="{Binding Source={StaticResource dictionary}}" />
绑定到集合:
class PhotoModel { private string _path; private string _name; private Image _image; public string Path { get { return _path; } set { _path = value; } } public string Name { get { return _name; } set { _name = value; } } //构造函数 public PhotoModel(string path, string name) { _path = path; _name = name; } //重写ToString(),可不指定元素DisplayMemberPath属性,显示需要显示的属性值 public override string ToString() { return _path; } } //PhotoList继承ObservableCollection使得非依赖属性可实现自动跟新绑定值 class PhotoList : System.Collections.ObjectModel.ObservableCollection<PhotoModel> { }
/// <summary> /// BindPage.xaml 的交互逻辑 /// </summary> public partial class BindPage : Page { PhotoList pl; public BindPage() { //创建数据源 pl = new PhotoList(); pl.Add(new PhotoModel("img/a.jpg", "a.jpg")); pl.Add(new PhotoModel("img/b.jpg", "b.jpg")); pl.Add(new PhotoModel("img/c.jpg", "c.jpg")); //将数据源添加到当前页的逻辑资源中 Resources.Add("photoList", pl); InitializeComponent(); } #region Event private void Button_Click(object sender, RoutedEventArgs e) { //修改数据源 pl.RemoveAt(pl.Count-1); } #endregion }
<Page.Resources> <SolidColorBrush x:Key="red" Color="red" /> <!-- 定义字典集合 --> <ResourceDictionary x:Key="dic" /> </Page.Resources> <StackPanel> <Label Content="{Binding Count, Source={StaticResource dic}}" /> <!-- 配置DisplayMemberPath要显示的PhotoModel元素中Name属性值 --> <!-- 配置IsSynchronizedWithCurrentItem可同步选中同一数据源Selector控件选择项 --> <ListBox x:Name="listBox1" DisplayMemberPath="Name" ItemsSource="{Binding Source={StaticResource photoList}}" IsSynchronizedWithCurrentItem="True"/> <ListBox x:Name="listBox2" DisplayMemberPath="Path" ItemsSource="{Binding Source={StaticResource photoList}}" IsSynchronizedWithCurrentItem="True"/> <Button Height="33" Width="83" Content="修改数据源" Click="Button_Click" /> </StackPanel>
DataContext共享源:
对于同一页面不同控件,绑定到同一源对象上,WPF可指定一隐式数据源,而不用显示调用Binding标记每个Source,RelativeSource,ElementName。这种隐式数据源也叫数据上下文(DataContext)。设置父元素的DataContext属性为这个源对象。
<StackPanel Name="parent" DataContext="{StaticResource photoList}"> <!-- 配置DisplayMemberPath要显示的PhotoModel元素中Name属性值 --> <!-- 配置SelectedValuePath选中的Value值属性 --> <!-- 配置IsSynchronizedWithCurrentItem可同步选中同一数据源Selector控件选择项 --> <ListBox x:Name="listBox1" SelectedValuePath="Name" DisplayMemberPath="Name" ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True"/> <ListBox x:Name="listBox2" DisplayMemberPath="Path" ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True"/> <Button Height="33" Width="83" Content="修改数据源" Click="Button_Click" /> </StackPanel>
用代码实现DataContext
InitializeComponent(); //在InitializeComponent()后调用,否者parent元素对象还未构建成功,会报错 //用此种方法可以避免将数据源对象保存为一资源 parent.DataContext = pl;
控制呈现:
1.使用数据模板
数据模板是一种用户界面。
<StackPanel Name="parent"> <ListBox x:Name="listBox1" SelectedValuePath="Name" ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True" SelectionChanged="listBox2_SelectionChanged"> <ListBox.ItemTemplate> <!-- 数据模板,用于显示一个图片 --> <!-- DataTemplate通常被当做一资源,可为多个元素共享,无论属性元素在哪里 --> <DataTemplate DataType="Button"> <!-- 注:此Path非彼Path,是指PhotoModel对象中Path属性 --> <Image Source="{Binding Path}"/> </DataTemplate> </ListBox.ItemTemplate> </ListBox> <!-- button显示:img/a.jpg --> <Button Name="button1" Height="33" Width="83" Content="{Binding Path}" Click="Button_Click" /> </StackPanel>
2.使用值转换器
namespace WPF_Test { //绑定值笔刷转换器 class CountToBackgroundConverter : IValueConverter { #region IValueConverter 成员 //源实例转换为目标实例 public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture) { //检查当前转换目标是否为Brush对象 if(targetType != typeof(Brush)) { throw new InvalidOperationException("转换目标必须为Brush!"); } int id = int.Parse(value.ToString()); switch(id) { case 1: return Brushes.YellowGreen; case 2: return Brushes.Orange; case 3: return Brushes.SkyBlue; default: //返回用户定义的参数 return parameter; } } //目标实例转换为源实例 public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new System.NotImplementedException(); } #endregion } //格式化绑定值转换器 class FormatConverter : IValueConverter { #region IValueConverter 成员 public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { int num = int.Parse(value.ToString()); return num + (num <= 1 ? "Item" : "Items"); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } #endregion } }
<!-- 在当前Page中引用自定义命名空间 --> xmlns:custom="clr-namespace:WPF_Test" <Page.Resources> <!-- 定义自定义类 --> <custom:CountToBackgroundConverter x:Key="myConverter"/> <custom:FormatConverter x:Key="myFormatConverter"/> </Page.Resources> <StackPanel Name="parent"> <ListBox x:Name="listBox1" SelectedValuePath="Name" ItemsSource="{Binding}"> <ListBox.ItemTemplate> <DataTemplate DataType="Button"> <StackPanel> <!-- 使用Binding的Converter属性插入自定义值转换器,将绑定数据ID值转换为需要的Brushes类型 --> <Label Background="{Binding ID, ConverterParameter=Red, Converter={StaticResource myConverter}}" Content="{Binding Name}"/> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> <!-- 每次源值改变,Convert方法都会调用 --> <Label Content="{Binding Count, Converter={StaticResource myFormatConverter}}" /> <!-- button显示:img/a.jpg --> <Button Name="button1" Height="33" Width="83" Content="{Binding Path}" Click="Button_Click" /> </StackPanel>