在已排序的ListCollectionView / ObservableCollection上应用过滤器

时间:2013-02-16 10:09:04

标签: c# wpf sorting filter listcollectionview

我有什么

我有一个由UserControlTextBox组成的ListBoxListBox通过ItemsSource ObservableCollectionDataContext绑定到ListCollectionViewListBox带有自定义排序和过滤器,我们将在下面看到。控件的目的是在string中仅显示源集合中包含TextBox中文本的项目(ListCollectionView s)。为此,我在ListBox上应用了一个过滤器。

我有两个额外的限制。 1,我的原始集合没有按字母顺序排序,但ListCollectionView中显示的项目使用的是CustomSort TextBox。 2,我必须只显示与ListCollectionView中的字符串匹配的前5个项目(按字母顺序排序)。我为此DataContext应用了一个过滤器。

期望

假设我的收藏在我的this.AllItems = new ObservableCollection<string> { "Banana", "Watermelon", "Peach", "Grape", "Apple", "Pineapple", "Cherry", "Durian", "Rambutan", "Strawberry", "Raspberry", "Lemon", "Orange", "Sugar cane", "Guava", "Tomato", "Coconut", "Melon", "Äpple", "Glaçon", "Etape", "Étape" };

中定义
TextBox

在我的ListBox中,我输入了字母'e'(所有比较都是不区分大小写的)。我希望CurrentUICulture显示以下5个项目(CurrentUICulture设置为fr-FR):

  • 苹果
  • 苹果
  • 樱桃
  • Etape酒店
  • ETAPE

因为它们是按字母顺序排序时包含字母“e”的前5个项目。但是我在我的申请中得到以下项目:

  • 苹果
  • 葡萄
  • 菠萝
  • 西瓜

因为它们是我的收藏中的前5个项目,其中包含字母“e” THEN 按字母顺序排序。

我的代码

这是代码,以了解我有什么和我的问题。 它应该只使用下面的复制/粘贴(谨防命名空间和MainWindow)。我正在使用C#4.0。

1)<Window x:Class="MyNamespace.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525" xmlns:local="clr-namespace:MyNamespace"> <Window.Resources> <local:FoobarViewModel x:Key="Foobar"/> </Window.Resources> <StackPanel> <local:Foobar DataContext="{StaticResource Foobar}" AllItems="{Binding AllItems}"/> </StackPanel> </Window>

DataContext

2)用作public class FoobarViewModel : INotifyPropertyChanged { private ObservableCollection<string> allItems; public event PropertyChangedEventHandler PropertyChanged; public FoobarViewModel() { this.AllItems = new ObservableCollection<string> { "Banana", "Watermelon", "Peach", "Grape", "Apple", "Pineapple", "Cherry", "Durian", "Rambutan", "Strawberry", "Raspberry", "Lemon", "Orange", "Sugar cane", "Guava", "Tomato", "Coconut", "Melon", "Äpple", "Glaçon", "Etape", "Étape" }; } public ObservableCollection<string> AllItems { get { return this.allItems; } set { this.allItems = value; this.OnPropertyChanged("AllItems"); } } private void OnPropertyChanged(string propertyName) { var handler = this.PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(propertyName)); } } }

的类
UserControl

3)<UserControl x:Class="MyNamespace.Foobar" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <Grid> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <TextBox x:Name="textbox" Grid.Row="0"/> <ListBox x:Name="listbox" Grid.Row="1"/> </Grid> </UserControl>

的XAML
UserControl

4)最后,最重要的是Foobar public partial class Foobar : UserControl { #region Fields public static readonly DependencyProperty AllItemsProperty = DependencyProperty.Register( "AllItems", typeof(IEnumerable<string>), typeof(Foobar), new PropertyMetadata(AllItemsChangedCallback)); private const int MaxItems = 5; #endregion #region Constructors public Foobar() { InitializeComponent(); textbox.KeyUp += TextboxKeyUp; } #endregion #region Properties public IEnumerable<string> AllItems { get { return (IEnumerable<string>)this.GetValue(AllItemsProperty); } set { this.SetValue(AllItemsProperty, value); } } #endregion #region Methods private void TextboxKeyUp(object sender, KeyEventArgs e) { TextBox localTextBox = sender as TextBox; if (localTextBox != null) { var items = ((ListCollectionView)listbox.ItemsSource).SourceCollection; if (items.Cast<string>().Any(x => x.ToLower(CultureInfo.CurrentUICulture).Contains(localTextBox.Text.ToLower(CultureInfo.CurrentUICulture)))) { this.ApplyFilter(); listbox.Visibility = Visibility.Visible; } else { listbox.Visibility = Visibility.Collapsed; } } } private static void AllItemsChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e) { Foobar control = sender as Foobar; if (control != null) { List<string> source = new List<string>((IEnumerable<string>)e.NewValue); ListCollectionView view = (ListCollectionView)CollectionViewSource.GetDefaultView(source); view.CustomSort = new CustomSort(); control.listbox.ItemsSource = view; control.ApplyFilter(); } } private void ApplyFilter() { ListCollectionView view = (ListCollectionView)listbox.ItemsSource; int index = 0; view.Filter = x => { bool result = x.ToString().ToLower(CultureInfo.CurrentUICulture).Contains(textbox.Text.ToLower(CultureInfo.CurrentUICulture)); if (result) { index++; } return index <= MaxItems && result; }; } #endregion private class CustomSort : IComparer { public int Compare(object x, object y) { return String.Compare(x.ToString(), y.ToString(), CultureInfo.CurrentUICulture, CompareOptions.IgnoreCase); } } } 背后的代码。

ApplyFilter

整个代码按预期工作,但在TextBox方法中进行的过滤除外。基本上,这种方法只是根据ListCollectionView中的任何内容检查集合中的每个项目,并且如果没有超过返回的最大项目数,则该项目将包含在过滤器中。当我调试此方法时,我可以看到项目按照集合的原始顺序进行浏览,而不是按排序顺序进行浏览,尽管过滤器似乎是在ObservableCollection<string>而不是ListCollectionView上完成的。 / p>

似乎首先应用过滤器,然后进行排序。我希望首先应用排序然后进行过滤。

我的问题

如何在已排序的{{1}}上应用过滤器,而不是在原始未排序的集合上应用过滤器?

1 个答案:

答案 0 :(得分:1)

为什么不在创建集合视图之前创建通用IComparer<T>并使用Enumerable.OrderBy<TSource, TKey>(IEnumerable<TSource>, Func<TSource, TKey>, IComparer<TKey>)扩展方法。

所以你最终得到的结果是:

private static void AllItemsChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
    Foobar control = sender as Foobar;
    if (control != null)
    {
        var newEnumerable = (IEnumerable<string>)e.NewValue;
        var sorted = newEnumerable.OrderBy(s => s, new CustomSort());
        var source = new List<string>(sorted);

        var view = (ListCollectionView)CollectionViewSource.GetDefaultView(source);
        control.listbox.ItemsSource = view;
        control.ApplyFilter();
    }
}

private class CustomSort : IComparer<string>
{
    public int Compare(string x, string y)
    {
        return String.Compare(x, y, CultureInfo.CurrentUICulture, CompareOptions.IgnoreCase);
    }
}

然后您的集合视图已经排序并且可以应用过滤。