我有的
我有一个UserControl
由 aTextBox
和 a组成的ListBox
。正如我们将在下面看到的,它通过自定义排序和过滤器绑定ListBox
到a ItemsSource
in ObservableCollection
a 。该控件的目的是在源集合中仅显示包含. 为此,我在.DataContext
ListCollectionView
ListBox
string
TextBox
ListCollectionView
我有两个额外的限制。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
如果没有超过返回的最大项目数,则该项目将包含在过滤器中。当我调试这个方法时,我可以看到项目是按集合的原始顺序浏览的,而不是按排序顺序浏览的,尽管过滤器似乎是ListCollectionView
在ObservableCollection<string>
.
似乎首先应用过滤器,然后是排序。我希望先应用排序,然后再应用过滤。
我的问题
如何将过滤器应用于已排序ListCollectionView
而不是原始未排序集合?