数据绑定是在应用程序 UI 与业务逻辑之间建立连接的过程。 如果绑定具有正确设置并且数据提供正确通知,则当数据更改其值时,绑定到数据的元素会自动反映更改。 数据绑定可能还意味着如果元素中数据的外部表现形式发生更改,则基础数据可以自动更新以反映更改。

数据绑定的基本概念

5. 数据绑定 - 图1

  1. 绑定目标:需要显示其他值的对象,必须是依赖属性,必须是依赖对象
  2. 绑定对象:Binding 该对象建立目标与源之间的数据流动
  3. 绑定源:提供显示值的对象,其属性可以使普通属性也可以是依赖属性
  4. 数据流的方向

5. 数据绑定 - 图2

  1. OneWay 绑定导致对源属性的更改会自动更新目标属性,但是对目标属性的更改不会传播回源属性。此绑定类型适用于绑定的控件为隐式只读控件的情况
  2. OneWayToSource 与 OneWay) 绑定相反;它在目标属性更改时更新源属性。 一个示例方案是您只需要从 UI 重新计算源值的情况。
  3. TwoWay 绑定导致对源属性的更改会自动更新目标属性,而对目标属性的更改也会自动更新源属性。 此绑定类型适用于可编辑窗体或其他完全交互式 UI 方案
  4. OneTime 绑定未在图中显示,该绑定会导致源属性初始化目标属性,但不传播后续更改。
  5. 若要实现TwoWay 则绑定源需要实现INotifyPropertyChanged接口,实现属性的更改通知。
  6. 触发源更新的原因TwoWay) 或 OneWayToSource) 绑定侦听目标属性的更改,并将这些更改传播回源。 这称为更新源。但是什么时候触发更改了,可以由UpdateSourceTrigger) 指定 | 值 | 作用 |

    | | —- | —- | —- | | LostFocus | 当控件失去焦点时触发,多用于TextBox控件 |

    | | PropertyChanged | 当属性值发生改变时,例如TextBox的值变化时 |

    | | Explicit | 当应用程序调用 UpdateSource) 时 |

    |

创建简单的绑定

  • 绑定对象 Binding 是目标与源之间的桥梁 ```csharp myProdp = new MyProdp();// 1.创建绑定源 Binding txtBinding = new Binding(“MyName”)// 2.设置绑定的源的属性路径, { UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,// 指定触发值修改的时机 Mode = BindingMode.TwoWay,// 指定绑定的方式,如果要实现双向绑定则源需要实现INotifyPropertyChanged 或继承自DependencyObject Source = myProdp// 指定绑定对象 };

BindingOperations.SetBinding(txtDp, TextBox.TextProperty, txtBinding);// 设置绑定,讲目标txtDP的Text属性与源建立绑定

  1. <a name="A0eIN"></a>
  2. ## 绑定通知的基本原理【个人理解用】
  3. 1. Binding类中讲目标与源建立关联,并通过反射绑定相关的NotifyPropertyChanged事件
  4. 1. 当源对象或目标对象属性发生改变时,触发Binding类中的NotifyPropertyChanged.Invoke方法,触发相关事件
  5. 1. 因为在binding时已经建立了事件关联,则会依次调用相关的事件方法。
  6. <a name="hxUOq"></a>
  7. ## 绑定的路径Path
  8. 1. 简单的绑定值:Path=PropertyName
  9. 1. 多级路径:Path=ShoppingCart.Order
  10. 1. 使用索引器:Path=ShoppingCart[0],Path=ShoppingCart.ShippingInfo[MailingAddress,Street]
  11. 1. 绑定集合本身,使用"/" Path=/Offices/ManagerName
  12. 1. 绑定自生".", 路径绑定到当前源,也可以省略
  13. <a name="wjOoG"></a>
  14. ## 绑定指定源
  15. 1. 绑定静态资源:StaticSource
  16. 1. 绑定中的静态资源,当源变化的时候不会改变已经绑定了的值
  17. 1. 绑定动态资源:DymaicSource
  18. 1. 动态绑定,允许源值在改变后,通知目标对象进行相应的变化
  19. 1. 绑定相对:RelativieSource
  20. 1. 相对源,主要用于精确查找父级,兄弟级的元素的相关属性,可以指定查找的层级,查找的控件类型等
  21. 1. Mode:
  22. 1. FindAncestor 引用数据绑定元素的父链中的上级,
  23. 1. PreviousData 允许在当前显示的数据项列表中绑定上一个数据项
  24. 1. TemplatedParent 引用应用了模板的元素,其中此模板中存在数据绑定元素。
  25. 1. Self引用正在其上设置绑定的元素,并允许你将该元素的一个属性绑定到同一元素的其他属性上。
  26. 1. AncestorType: 祖先的类型
  27. 1. AncestorLevel 要向上查找的层级
  28. ```xml
  29. <DockPanel Background="Red">
  30. <Grid Background="AliceBlue" Margin="10">
  31. <DockPanel Background="SaddleBrown" Margin="10">
  32. <TextBox Height="200" Width="200"
  33. Background="{Binding RelativeSource={RelativeSource AncestorType={x:Type DockPanel},AncestorLevel=2,Mode=FindAncestor},Path=Background}"></TextBox>
  34. </DockPanel>
  35. </Grid>
  36. </DockPanel>
  1. 使用DataContext作为绑定源,则子元素在没有指定其他Source的情况下,会向上查找对应的DataContext并查找其中的路径
  2. 可以反过来理解,在树形结构中,父级元素把自己的DataContext属性,向子元素进行了暴露
  3. 带来的好处是,编写控件时,如果子元素为私有的或未向外暴露的,可直接利用DataContext向其传值

    数据提供者

  4. ObjectDataProvider: 从应用程序中提取另外一个类中获取信息

  5. 创建需要的对象,并为构造函数提供参数
  6. 能够调用相关方法,并向他传递参数
  7. 能够异步的创建对象

    1. // 后台定义类
    2. public class StudentStore
    3. {
    4. public ObservableCollection<Student> LoadUserByName(string name="")
    5. {
    6. var list = Student.SeedStudent();
    7. if (!string.IsNullOrEmpty(name))
    8. {
    9. list = list.Where(p => p.Names.Contains(name)).ToList();
    10. }
    11. return new ObservableCollection<Student>(list);
    12. }
    13. }
    14. // 在资源文件里定义
    15. <ObjectDataProvider x:Key="studentProvider" MethodName="LoadUserByName" ObjectType="{x:Type local:StudentStore}">
    16. <ObjectDataProvider.MethodParameters>
    17. <system:String>0</system:String>
    18. </ObjectDataProvider.MethodParameters>
    19. </ObjectDataProvider>
    20. // 这样来使用
    21. <StackPanel Orientation="Horizontal">
    22. <TextBox MinWidth="120" Text="{Binding Source={StaticResource studentProvider}, Path=MethodParameters[0],UpdateSourceTrigger=PropertyChanged,Mode=OneWayToSource,BindsDirectlyToSource=True}"></TextBox>
    23. </StackPanel>
    24. <ListBox ItemsSource="{Binding Source={StaticResource studentProvider}}" Grid.Row="4">
    25. <ListBox.ItemTemplate>
    26. <DataTemplate>
    27. <TextBlock Text="{Binding Names}"></TextBlock>
    28. </DataTemplate>
    29. </ListBox.ItemTemplate>
    30. </ListBox>
  8. XMLDataProvider

使用集合绑定及集合视图

  1. 使用集合进行绑定时,如果源在更改后要通知目标进行变化则需要实现 INotifyCollectionChanged)事件,或者直接使用ObservableCollection 类
    1. // Window窗体
    2. students = new ObservableCollection<Student>(Student.SeedStudent());
    3. this.DataContext = new{Students = students};
    4. // xaml
    5. <Border BorderBrush="SkyBlue" BorderThickness="0,0,0,1">
    6. <StackPanel Orientation="Horizontal">
    7. <TextBlock Text="学生姓名: " VerticalAlignment="Center"></TextBlock>
    8. <TextBox Text="{Binding Path=SelectedItem.Names,ElementName=lstStudetns,Mode=OneWay}" VerticalContentAlignment="Center" Name="stuName" VerticalAlignment="Center" MinWidth="120" Height="30" Margin="5"></TextBox>
    9. <Button Click="Button_Click" Content="添加学生" HorizontalAlignment="Right"></Button>
    10. </StackPanel>
    11. </Border>
    12. <ListBox ItemsSource="{Binding Students}" Name="lstStudetns" Grid.Row="1">
    13. <ListBox.ItemTemplate>
    14. <DataTemplate>
    15. <TextBlock Text="{Binding Names}"></TextBlock>
    16. </DataTemplate>
    17. </ListBox.ItemTemplate>
    18. </ListBox>
    b. 集合视图是位于绑定源集合顶部的一层,您可以通过它使用排序、筛选和分组查询来导航和显示源集合,而无需更改基础源集合本身。 集合视图还维护着一个指向集合中的当前项的指针。 如果源集合实现了 INotifyCollectionChanged) 接口,则 CollectionChanged) 事件引发的更改将传播到视图。由于视图不会更改基础源集合,因此每个源集合可以有多个关联的视图。相关的集合类型
源集合类型 集合视图类型 注释
IEnumerable) 基于 CollectionView) 的内部类型 无法对项进行分组。【尽量避免使用】
IList) ListCollectionView) 最快。
IBindingList) BindingListCollectionView)

5. 数据绑定 - 图3

  1. studentsView = new ListCollectionView(students);// 创建列表视图
  2. 数据转换
  3. public bool Contains(object student)
  4. {
  5. if (!string.IsNullOrEmpty(txtSearch.Text))
  6. {
  7. var nowStu = student as Student;
  8. return nowStu.Names.Contains(txtSearch.Text);
  9. }
  10. return true;
  11. }
  12. private void btnSortByName_Click(object sender, RoutedEventArgs e)
  13. {
  14. // 设置排序
  15. studentsView.SortDescriptions.Add(new SortDescription("Names", ListSortDirection.Ascending));
  16. }
  17. private void btnCanceSortByName_Click(object sender, RoutedEventArgs e)
  18. {
  19. // 清除排序
  20. studentsView.SortDescriptions.Clear();
  21. }
  22. private void searchName_Click(object sender, RoutedEventArgs e)
  23. {
  24. // 设置过滤
  25. studentsView.Filter = new Predicate<object>(Contains);
  26. }

数据转换

应用场景:

  • 数据应根据区域性以不同方式显示。 例如,可能需要根据在特定区域性中使用的值或标准,来实现货币转换器或日历日期/时间转换器。
  • 使用的数据不一定会更改属性的文本值,但会更改其他某个值(如图像的源,或显示文本的颜色或样式)。 在这种情况下,可以通过转换可能不合适的属性绑定(如将文本字段绑定到表单元格的 Background 属性)来使用转换器。
  • 将多个控件或控件的多个属性绑定到相同数据。 在这种情况下,主绑定可能仅显示文本,而其他绑定则处理特定的显示问题,但仍使用同一绑定作为源信息。 ```csharp // 使用转换器 // 静态资源 // 使用转换器 //定义转换器 public class BoolToColoerConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if ((bool)value) {

    1. return "Yellow";

    }

    1. return "White";

    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {

    1. throw new NotImplementedException();

    } }

  1. <a name="zdMhY"></a>
  2. ## 数据验证
  3. 接受用户输入的大多数应用程序都需要具有验证逻辑,以确保用户输入了需要的信息。 验证检查可以基于类型、范围、格式或其他应用程序特定的要求<br />内置的验证规则
  4. - [ExceptionValidationRule](https://msdn.microsoft.com/zh-cn/library/ms609872(v=vs.100)) 检查在更新绑定源属性的过程中引发的异常。
  5. - [DataErrorValidationRule](https://msdn.microsoft.com/zh-cn/library/bb788425(v=vs.100)) 对象检查由实现 [IDataErrorInfo](https://msdn.microsoft.com/zh-cn/library/743swcz7(v=vs.100)) 接口的对象所引发的错误
  6. - 自定义验证规则,继承抽象类ValidationRule
  7. <a name="fflFt"></a>
  8. #### 创建绑定
  9. ```csharp
  10. <TextBox Text="{Binding Age,UpdateSourceTrigger=PropertyChanged,ValidatesOnExceptions=True}">
  11. </TextBox>// 指定在验证绑定数据错误时引发异常

使用自定义数据验证,并在界面上进行显示

  1. public class AageValidRuler : ValidationRule
  2. {
  3. public AageValidRuler()
  4. {
  5. MaxAge = 100;
  6. MinAge = 10;
  7. }
  8. public int MaxAge { get; set; }
  9. public int MinAge { get; set; }
  10. public override ValidationResult Validate(object value, CultureInfo cultureInfo)
  11. {
  12. int studentAge = 0;
  13. if (int.TryParse(value.ToString(), out studentAge) && studentAge >= MinAge && studentAge <= MaxAge)
  14. {
  15. return ValidationResult.ValidResult;
  16. }
  17. return new ValidationResult(false, "年龄输入非法");
  18. }
  19. }
  1. <TextBox MaxWidth="120">
  2. <TextBox.Style>
  3. <Style TargetType="TextBox">
  4. <Style.Triggers>// 触发器
  5. <Trigger Property="Validation.HasError" Value="true">
  6. <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self},Path=(Validation.Errors)[0].ErrorContent}"/>// 显示错误提示内容
  7. </Trigger>
  8. </Style.Triggers>
  9. </Style>
  10. </TextBox.Style>
  11. <Validation.ErrorTemplate >// 定义显示错误模版
  12. <ControlTemplate>
  13. <DockPanel>
  14. <TextBlock Foreground="Red" FontSize="20">!</TextBlock>
  15. <AdornedElementPlaceholder/>
  16. </DockPanel>
  17. </ControlTemplate>
  18. </Validation.ErrorTemplate>
  19. <TextBox.Text>
  20. <Binding Path="Age" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True" >//
  21. <Binding.ValidationRules>// 使用自定义验证规则
  22. <local:AageValidRuler MinAge="10" MaxAge="120"/>
  23. </Binding.ValidationRules>
  24. </Binding>
  25. </TextBox.Text>
  26. </TextBox>