我有一个listview,从数据库中提取一堆信息。有已知的值分配给这些将是有用的排序,但我似乎只能按一个项目排序一次。下面是工作代码:
private bool prodAreaFilter(object item)
{
return (item as PCdata).ProductionArea.IndexOf(((ComboBoxItem)prodAreaComboBox.SelectedItem).Content.ToString(), StringComparison.OrdinalIgnoreCase) >= 0;
}
private void prodArea_Combo_SelectionChanged(object sender, RoutedEventArgs e)
{
CollectionView viewPA = (CollectionView)CollectionViewSource.GetDefaultView(lstView.ItemsSource);
viewPA.Filter = prodAreaFilter;
CollectionViewSource.GetDefaultView(lstView.ItemsSource).Refresh();
//MessageBox.Show(((ComboBoxItem)prodAreaComboBox.SelectedItem).Content.ToString());
}
private bool typeFilter(object item)
{
return (item as PCdata).Type.IndexOf(((ComboBoxItem)typeComboBox.SelectedItem).Content.ToString(), StringComparison.OrdinalIgnoreCase) >= 0;
}
private void type_Combo_SelectionChanged(object sender, RoutedEventArgs e)
{
CollectionView viewType = (CollectionView)CollectionViewSource.GetDefaultView(lstView.ItemsSource);
viewType.Filter = typeFilter;
CollectionViewSource.GetDefaultView(lstView.ItemsSource).Refresh();
//MessageBox.Show(((ComboBoxItem)typeComboBox.SelectedItem).Content.ToString());
}
它们不是一起过滤,而是覆盖另一个。我认为这是由于它们各自有自己的过滤逻辑造成的,所以我试图将它们合并到一个过滤器中:
private bool Filter(object item)
{
return (item as PCdata).Type.IndexOf(((ComboBoxItem)typeComboBox.SelectedItem).Content.ToString(), StringComparison.OrdinalIgnoreCase) >= 0;
(item as PCdata).ProductionArea.IndexOf(((ComboBoxItem)prodAreaComboBox.SelectedItem).Content.ToString(), StringComparison.OrdinalIgnoreCase) >= 0;
}
private void prodArea_Combo_SelectionChanged(object sender, RoutedEventArgs e)
{
CollectionView viewPA = (CollectionView)CollectionViewSource.GetDefaultView(lstView.ItemsSource);
viewPA.Filter = Filter;
CollectionViewSource.GetDefaultView(lstView.ItemsSource).Refresh();
//MessageBox.Show(((ComboBoxItem)prodAreaComboBox.SelectedItem).Content.ToString());
}
//private bool typeFilter(object item)
//{
// return (item as PCdata).Type.IndexOf(((ComboBoxItem)typeComboBox.SelectedItem).Content.ToString(), StringComparison.OrdinalIgnoreCase) >= 0;
//}
private void type_Combo_SelectionChanged(object sender, RoutedEventArgs e)
{
CollectionView viewType = (CollectionView)CollectionViewSource.GetDefaultView(lstView.ItemsSource);
viewType.Filter = Filter;
CollectionViewSource.GetDefaultView(lstView.ItemsSource).Refresh();
//MessageBox.Show(((ComboBoxItem)typeComboBox.SelectedItem).Content.ToString());
}
这个解决方案对我来说很有效,但是有必要添加逻辑来处理一个组合框或另一个组合框为空并相应地切换标准。
private void prodArea_Combo_SelectionChanged(object sender, RoutedEventArgs e)
{
if (string.IsNullOrEmpty(typeComboBox.Text)) //If other combo is empty, only use one criteria for box that will have value
{
CollectionView viewPA = (CollectionView)CollectionViewSource.GetDefaultView(lstView.ItemsSource);
viewPA.Filter = obj => ((PCdata)obj).ProductionArea.Contains(((ComboBoxItem)prodAreaComboBox.SelectedItem).Content.ToString());
}
else
{
CollectionView viewPA = (CollectionView)CollectionViewSource.GetDefaultView(lstView.ItemsSource);
viewPA.Filter = obj => ((PCdata)obj).ProductionArea.Contains(((ComboBoxItem)prodAreaComboBox.SelectedItem).Content.ToString()) &&
((PCdata)obj).Type.Contains(((ComboBoxItem)typeComboBox.SelectedItem).Content.ToString());
}
CollectionViewSource.GetDefaultView(lstView.ItemsSource).Refresh();
}
private void type_Combo_SelectionChanged(object sender, RoutedEventArgs e)
{
if (string.IsNullOrEmpty(prodAreaComboBox.Text))
{
CollectionView viewPA = (CollectionView)CollectionViewSource.GetDefaultView(lstView.ItemsSource);
viewPA.Filter = obj => ((PCdata)obj).Type.Contains(((ComboBoxItem)typeComboBox.SelectedItem).Content.ToString());
}
else
{
CollectionView viewPA = (CollectionView)CollectionViewSource.GetDefaultView(lstView.ItemsSource);
viewPA.Filter = obj => ((PCdata)obj).ProductionArea.Contains(((ComboBoxItem)prodAreaComboBox.SelectedItem).Content.ToString()) &&
((PCdata)obj).Type.Contains(((ComboBoxItem)typeComboBox.SelectedItem).Content.ToString());
}
CollectionViewSource.GetDefaultView(lstView.ItemsSource).Refresh();
}
一个过滤的例子。也许这对你有帮助。如果你有一个按"姓名"过滤的按钮;和"Age">
private void btnFilterName_Click(object sender, RoutedEventArgs e)
{
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(myCollectionView);
view.Filter = obj => ((MyDataModel)obj).Name.Contains(txtFilterName.Text);
}
private void btnFilterAge_Click(object sender, RoutedEventArgs e)
{
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(myCollectionView);
view.Filter = obj => ((MyDataModel)obj).Age == Convert.ToInt32(txtFilterAge.Text);
}
若要按多列进行筛选,可以使用"&&"在同一事件中组合多个过滤条件的操作符。
private void btnFilterNameAge_Click(object sender, RoutedEventArgs e)
{
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(myCollectionView);
view.Filter = obj => ((MyDataModel)obj).Name.Contains(txtFilterName.Text) && ((MyDataModel)obj).Age == Convert.ToInt32(txtFilterAge.Text);
}
要清除过滤器,请在按钮单击事件中将过滤器属性设置为空。
private void btnClearFilter_Click(object sender, RoutedEventArgs e)
{
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(myCollectionView);
view.Filter = null;
}
您可以使用if语句来应用筛选条件
private void ApplyFilter()
{
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(myCollectionView);
view.Filter = obj =>
{
if (ComboBoxName.SelectedItem != null && ComboBoxAge.SelectedItem != null)
{
return ((MyDataModel)obj).Name.Contains(ComboBoxName.SelectedItem.ToString()) && ((MyDataModel)obj).Age == Convert.ToInt32(ComboBoxAge.SelectedItem);
}
else if (ComboBoxName.SelectedItem != null)
{
return ((MyDataModel)obj).Name.Contains(ComboBoxName.SelectedItem.ToString());
}
else if (ComboBoxAge.SelectedItem != null)
{
return ((MyDataModel)obj).Age == Convert.ToInt32(ComboBoxAge.SelectedItem);
}
else
{
return true;
}
};
}
然后,您可以在组合框的SelectionChanged事件中调用ApplyFilter方法,以便在选择新项时应用过滤器。如果所选项不为空,则调用ApplyFilter方法,以防止组合框为空时出现错误。
private void ComboBoxName_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (ComboBoxName.SelectedItem != null)
{
ApplyFilter();
}
}
private void ComboBoxAge_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (ComboBoxAge.SelectedItem != null)
{
ApplyFilter();
}
}
如果您想要在两个组合框都为空时清除过滤器,您需要添加一个ClearFilter方法,并在两个组合框都为空时调用它。
private void ClearFilter()
{
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(myCollectionView);
view.Filter = null;
}
if (ComboBoxName.SelectedItem == null && ComboBoxAge.SelectedItem == null)
{
ClearFilter();
}
过滤器是应用于集合中的每个项目的谓词。
如果返回true,您将看到该项目。如果返回false,则不需要。
每次在集合上添加或删除项或强制refresh()时,都会运行该过滤逻辑。除非您让它使用实时过滤,在这种情况下,当集合中的任何更改时,将再次检查整个集合。
所有这些都意味着使用collectionview过滤是非常昂贵的。您应该仔细考虑您的基本集合中有多少项。如果有一百万行,并且你有一个用户插入或删除行,那么每次遍历整个集合并检查。
几年前我写过一篇关于过滤的文章:
https://social.technet.microsoft.com/wiki/contents/articles/26673.wpf-collectionview-tips.aspx
微软已经放弃了源代码所在的技术画廊。
如果您需要基于多个属性的灵活过滤,那么您可以使用演示谓词本身检查谓词列表的技术。想要两个列匹配,就向列表中添加2个谓词。想要3,就加3。下面,标准是一个列表。如果它们都为真,则返回真
private bool dynamic_Filter(object item)
{
Person p = item as Person;
bool isIn = true;
if (criteria.Count() == 0)
return isIn;
isIn = criteria.TrueForAll(x => x(p));
return isIn;
}
在该示例中,您可以从几个条件中的任何一个或所有条件中进行选择。将谓词添加到该列表的代码如下所示:
criteria.Clear();
if (yearsChosen > 0)
{
criteria.Add(new Predicate<Person>(x => yearsChosen < (
(_zeroDay + (_now - x.BirthDate)).Year - 1)
));
}
if (letterChosen != "Any")
{
criteria.Add(new Predicate<Person>(x => x.LastName.StartsWith(letterChosen)));
}
if (genderChosen != "Any")
{
criteria.Add(new Predicate<Person>(x => x.Gender.Equals(genderChosen.Substring(0, 1))));
}
PeopleView.Filter = dynamic_Filter;
RaisePropertyChanged("PeopleView");