• WPF数据视图


    将集合绑定到ItemsControl控件时,会不加通告的在后台创建数据视图——位于数据源和绑定的控件之间。数据视图是进入数据源的窗口,可以跟踪当前项,并且支持各种功能,如排序、过滤、分组。

    这些功能和数据对象本身是相互独立的,这意味着可在窗口的不同部分使用不同的方式绑定相同的数据。例如,可将同一个集合绑定到两个不同的列表,并对集合进行过滤以显示不同的记录。(来自于WPF编程宝典。我实测下来,绑定自同一个数据源的ItemsControl控件会共享一个View,当对该View进行筛选、排序时,会应用到所有绑定到该数据源的控件。)

    获取视图的方法:

    1. ListCollectionView? view = CollectionViewSource.GetDefaultView(filterListBox.ItemsSource) as ListCollectionView;
    2. ListCollectionView? view = CollectionViewSource.GetDefaultView(Orders) as ListCollectionView;

    可以看到,可以直接通过数据源来获取视图,这也表明,绑定到同一个数据源的控件会公用一个视图。

    视图有 MoveCurrentToPrevious()、MoveCurrentToNext() 方法,可以用于视图导航。

    1. private void cmdPrev_Click(object sender, RoutedEventArgs e)
    2. {
    3. View?.MoveCurrentToPrevious();
    4. }
    5. private void cmdNext_Click(object sender, RoutedEventArgs e)
    6. {
    7. View?.MoveCurrentToNext();
    8. }
    9. private void view_CurrentChanged(object? sender, EventArgs e)
    10. {
    11. lblPosition.Text = "Record " + (View?.CurrentPosition + 1).ToString() + " of " + View?.Count.ToString();
    12. cmdPrev.IsEnabled = View?.CurrentPosition > 0;
    13. cmdNext.IsEnabled = View?.CurrentPosition < View?.Count - 1;
    14. }

    视图排序

    1. View.SortDescriptions.Add(new SortDescription("Volume", ListSortDirection.Ascending));
    2. View.SortDescriptions.Add(new SortDescription("Price", ListSortDirection.Descending));

    视图分组

    1. "groupListBox" ItemsSource="{Binding Path=Orders}">
    2. "{Binding Price}"> - "{Binding Volume}">
    3. "{Binding Path=Name}" FontWeight="Bold" Foreground="White" Background="LightGreen" Margin="0,5,0,0" Padding="3"/>
    View.GroupDescriptions.Add(new PropertyGroupDescription("Volume"));

    视图过滤

    1. public class ProductByPriceFilterer
    2. {
    3. public ProductByPriceFilterer(decimal minimumPrice)
    4. {
    5. MinimumPrice = minimumPrice;
    6. }
    7. public decimal MinimumPrice { get; set; }
    8. public bool FilterItem(Object item)
    9. {
    10. Order? order = item as Order;
    11. if (order != null)
    12. {
    13. return order.Price > MinimumPrice;
    14. }
    15. return false;
    16. }
    17. }
    18. public partial class MainWindow : Window
    19. {
    20. public MainWindow()
    21. {
    22. InitializeComponent();
    23. View = (ListCollectionView)CollectionViewSource.GetDefaultView(Orders);
    24. View.IsLiveFiltering = true;
    25. View.LiveFilteringProperties.Add("Price");
    26. }
    27. public ObservableCollection Orders { get; set; } = new();
    28. private ListCollectionView? View;
    29. public decimal MinPrice { get; set; } = 200;
    30. private ProductByPriceFilterer? filterer;
    31. private void cmdFilter_Click(object sender, RoutedEventArgs e)
    32. {
    33. if (View != null)
    34. {
    35. filterer = new ProductByPriceFilterer(MinPrice);
    36. View.Filter = new Predicate<object>(filterer.FilterItem);
    37. }
    38. }
    39. private void cmdRemoveFilter_Click(object sender, RoutedEventArgs e)
    40. {
    41. if (View != null)
    42. {
    43. View.Filter = null;
    44. }
    45. }
    46. }

    完整代码文件:

    MainWindow.xaml

    1. "DataView.MainWindow"
    2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    6. xmlns:local="clr-namespace:DataView"
    7. mc:Ignorable="d"
    8. Title="MainWindow" Height="450" Width="800">
    9. "myGrid">
    10. "Auto"/>
    11. "0" Grid.Column="0" >
    12. "Horizontal">
    13. "lblPosition" VerticalAlignment="Center">
    14. "navigateListBox" DisplayMemberPath="Price" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Path=Orders}"/>
    15. "0" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
    16. "0" Grid.Column="1" Text="{Binding Path=MinPrice}">
    17. "filterListBox" DisplayMemberPath="Price" ItemsSource="{Binding Path=Orders}"/>
    18. "1" Grid.Column="0">
    19. "groupListBox" ItemsSource="{Binding Path=Orders}">
    20. "{Binding Price}"> - "{Binding Volume}">
    21. "{Binding Path=Name}" FontWeight="Bold" Foreground="White" Background="LightGreen" Margin="0,5,0,0" Padding="3"/>

    MainWindow.xaml.cs

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Collections.ObjectModel;
    4. using System.ComponentModel;
    5. using System.Globalization;
    6. using System.Runtime.CompilerServices;
    7. using System.Windows;
    8. using System.Windows.Controls;
    9. using System.Windows.Data;
    10. namespace DataView;
    11. public class ViewModelBase : INotifyPropertyChanged
    12. {
    13. public event PropertyChangedEventHandler? PropertyChanged;
    14. protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    15. {
    16. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    17. }
    18. protected virtual bool SetProperty<T>(ref T member, T value, [CallerMemberName] string? propertyName = null)
    19. {
    20. if (EqualityComparer.Default.Equals(member, value))
    21. {
    22. return false;
    23. }
    24. member = value;
    25. OnPropertyChanged(propertyName);
    26. return true;
    27. }
    28. }
    29. public class Order : ViewModelBase
    30. {
    31. public decimal price = 0;
    32. public decimal Price { get => price; set => SetProperty(ref price, value); }
    33. public int volume = 0;
    34. public int Volume { get => volume; set => SetProperty(ref volume, value); }
    35. public DateTime orderDate = DateTime.Now;
    36. public DateTime OrderDate { get => orderDate; set => SetProperty(ref orderDate, value); }
    37. public string image = string.Empty;
    38. public string Image { get => image; set => SetProperty(ref image, value); }
    39. }
    40. public class ProductByPriceFilterer
    41. {
    42. public ProductByPriceFilterer(decimal minimumPrice)
    43. {
    44. MinimumPrice = minimumPrice;
    45. }
    46. public decimal MinimumPrice { get; set; }
    47. public bool FilterItem(Object item)
    48. {
    49. Order? order = item as Order;
    50. if (order != null)
    51. {
    52. return order.Price > MinimumPrice;
    53. }
    54. return false;
    55. }
    56. }
    57. public class PriceRangeProductGrouper : IValueConverter
    58. {
    59. public int GroupInterval { get; set; }
    60. public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    61. {
    62. decimal price = (decimal)value;
    63. if (price < GroupInterval)
    64. {
    65. return string.Format("Less than {0:C}", GroupInterval);
    66. }
    67. else
    68. {
    69. int interval = (int)price / GroupInterval;
    70. int lowerLimit = interval * GroupInterval;
    71. int upperLimit = (interval + 1) * GroupInterval;
    72. return string.Format("{0:C} to {1:C}", lowerLimit, upperLimit);
    73. }
    74. }
    75. public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    76. {
    77. throw new NotSupportedException("This converter is for grouping only.");
    78. }
    79. }
    80. public partial class MainWindow : Window
    81. {
    82. public MainWindow()
    83. {
    84. InitializeComponent();
    85. myGrid.DataContext = this;
    86. InitOrders();
    87. InitView();
    88. }
    89. public void InitOrders()
    90. {
    91. Order order1 = new Order();
    92. Order order2 = new Order();
    93. Order order3 = new Order();
    94. Order order4 = new Order();
    95. order1.Price = 100;
    96. order1.Volume = 100;
    97. order1.Image = "image1.gif";
    98. order2.Price = 1000;
    99. order2.Volume = 100;
    100. order2.Image = "image2.gif";
    101. order3.Price = 10000;
    102. order3.Volume = 10000;
    103. order3.Image = "image3.gif";
    104. order4.Price = 100000;
    105. order4.Volume = 10000;
    106. order4.Image = "image4.gif";
    107. Orders.Add(order1);
    108. Orders.Add(order2);
    109. Orders.Add(order3);
    110. Orders.Add(order4);
    111. }
    112. private void InitView()
    113. {
    114. View = (ListCollectionView)CollectionViewSource.GetDefaultView(Orders);
    115. if(View != null)
    116. {
    117. View.CurrentChanged += new EventHandler(view_CurrentChanged);
    118. View.SortDescriptions.Add(new SortDescription("Volume", ListSortDirection.Ascending));
    119. View.SortDescriptions.Add(new SortDescription("Price", ListSortDirection.Descending));
    120. View.GroupDescriptions.Add(new PropertyGroupDescription("Volume"));
    121. View.IsLiveFiltering = true;
    122. View.LiveFilteringProperties.Add("Price");
    123. }
    124. }
    125. public ObservableCollection Orders { get; set; } = new();
    126. private ListCollectionView? View;
    127. private void cmdPrev_Click(object sender, RoutedEventArgs e)
    128. {
    129. View?.MoveCurrentToPrevious();
    130. }
    131. private void cmdNext_Click(object sender, RoutedEventArgs e)
    132. {
    133. View?.MoveCurrentToNext();
    134. }
    135. private void view_CurrentChanged(object? sender, EventArgs e)
    136. {
    137. lblPosition.Text = "Record " + (View?.CurrentPosition + 1).ToString() + " of " + View?.Count.ToString();
    138. cmdPrev.IsEnabled = View?.CurrentPosition > 0;
    139. cmdNext.IsEnabled = View?.CurrentPosition < View?.Count - 1;
    140. }
    141. public decimal MinPrice { get; set; } = 200;
    142. private ProductByPriceFilterer? filterer;
    143. private void cmdFilter_Click(object sender, RoutedEventArgs e)
    144. {
    145. if (View != null)
    146. {
    147. filterer = new ProductByPriceFilterer(MinPrice);
    148. View.Filter = new Predicate<object>(filterer.FilterItem);
    149. }
    150. }
    151. private void cmdRemoveFilter_Click(object sender, RoutedEventArgs e)
    152. {
    153. if (View != null)
    154. {
    155. View.Filter = null;
    156. }
    157. }
    158. private void IncreaseButton_Click(object sender, RoutedEventArgs e)
    159. {
    160. foreach(var order in Orders)
    161. {
    162. order.Price *= 10;
    163. }
    164. }
    165. private void DecreaseButton_Click(object sender, RoutedEventArgs e)
    166. {
    167. foreach (var order in Orders)
    168. {
    169. order.Price /= 10;
    170. }
    171. }
    172. }

  • 相关阅读:
    攻防世界-web-file_include
    按键精灵中的字符串常用的场景
    ubus调试小结
    scrapy框架——架构介绍、安装、项目创建、目录介绍、使用、持久化方案、集成selenium、去重规则源码分析、布隆过滤器使用、redis实现分布式爬虫
    计算机组成原理之指令
    Android 无法mkdir()创建文件夹
    mongoDB mapreduce使用总结
    全球与中国液体壁纸行业需求趋势及投资策略分析报告2022-2028年
    矿区井下智慧用电安全监测解决方案
    XXE漏洞详解
  • 原文地址:https://blog.csdn.net/xunmeng2002/article/details/132617593