我有什么
我有一个由UserControl
和TextBox
组成的ListBox
。 ListBox
通过ItemsSource
ObservableCollection
将DataContext
绑定到ListCollectionView
,ListBox
带有自定义排序和过滤器,我们将在下面看到。控件的目的是在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):
因为它们是按字母顺序排序时包含字母“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>
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}}上应用过滤器,而不是在原始未排序的集合上应用过滤器?
答案 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);
}
}
然后您的集合视图已经排序并且可以应用过滤。