私が持っているもの
私はとでUserControl
できTextBox
ていListBox
ます。以下に示すように、はカスタムの並べ替えとフィルターを使用して、経由でListBox
にItemsSource
バインドされています。コントロールの目的は、のテキストを含むソースコレクション内のアイテム(s)のみを表示することです。そのために、にフィルターを適用します。ObservableCollection
DataContext
ListCollectionView
ListBox
string
TextBox
ListCollectionView
さらに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つの項目が表示されることを期待しています( CurrentUICulture
fr-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
ですか?