1

我有的

我有一个UserControl由 aTextBox和 a组成的ListBox。正如我们将在下面看到的,它通过自定义排序和过滤器绑定ListBox到a ItemsSourcein ObservableCollectiona 。该控件的目的是在源集合中仅显示包含. 为此,我在.DataContextListCollectionViewListBoxstringTextBoxListCollectionView

我有两个额外的限制。1,我的原始收藏没有按字母顺序排序,但显示的项目ListBox使用ListCollectionView CustomSort. 2,我必须只显示与TextBox. 我为此应用了一个过滤器ListCollectionView

期待

假设我的收藏在 my 中定义为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我输入了字母“e”(所有比较都是不区分大小写的)。我希望ListBox显示以下 5 项(CurrentUICulture设置为 fr-FR):

  • 苹果
  • 苹果
  • 樱桃
  • 胶带
  • 胶带

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

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

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

我的代码

这是了解我所拥有的和我的问题的代码。它实际上只能使用以下内容的复制/粘贴来工作(注意命名空间和CurrentUICulture)。我正在使用 C# 4.0。

1) 的MainWindow

<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>

2)用作类DataContext

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));
        }
    }
}

3) 我的 XAMLUserControl

<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>

4)最后,最重要的是我的UserControl 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如果没有超过返回的最大项目数,则该项目将包含在过滤器中。当我调试这个方法时,我可以看到项目是按集合的原始顺序浏览的,而不是按排序顺序浏览的,尽管过滤器似乎是ListCollectionViewObservableCollection<string>.

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

我的问题

如何将过滤器应用于已排序ListCollectionView而不是原始未排序集合?

4

1 回答 1

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);
    }
}

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

于 2013-02-16T10:23:11.743 回答