1

私が持っているもの

私はとでUserControlできTextBoxていListBoxます。以下に示すように、はカスタムの並べ替えとフィルターを使用して、経由でListBoxItemsSourceバインドされています。コントロールの目的は、のテキストを含むソースコレクション内のアイテム(s)のみを表示することです。そのために、にフィルターを適用します。ObservableCollectionDataContextListCollectionViewListBoxstringTextBoxListCollectionView

さらに2つの制約があります。1、私の元のコレクションはアルファベット順にソートされていませんが、に表示されるアイテムListBoxはを使用していListCollectionView CustomSortます。2、の文字列に一致する最初の5つの項目(アルファベット順にソート)のみを表示する必要がありますTextBox。そのためにフィルターを適用しますListCollectionView

期待

私のコレクションが私の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つの項目が表示されることを期待しています( CurrentUICulturefr-FRに設定されています)。

  • アップル
  • アップル
  • チェリー
  • Etape
  • Étape

アルファベット順に並べ替えると、文字「e」を含む最初の5つの項目であるためです。ただし、アプリケーションで次のアイテムを取得します。

  • アップル
  • 葡萄
  • パイナップル
  • スイカ

これは、私のコレクションの最初の5つのアイテムであり、文字「e」が含まれているため、アルファベット順に並べ替えられています。

私のコード

これが私が持っているものと私の問題を理解するためのコードです。事実上、以下のコピー/貼り付けを使用してのみ機能するはずです(名前空間とに注意してください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返されるアイテムの最大数を超えていない場合、そのアイテムはフィルターに含まれます。ListCollectionViewこのメソッドをデバッグすると、フィルターはではなくで実行されているように見えますが、アイテムはコレクションの元の順序で参照され、並べ替えられた順序では参照されていないことがわかりますObservableCollection<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 に答える