如何从各种文本框(多标准)中过滤ObservableCollection?

时间:2012-10-31 15:26:50

标签: c# wpf listview filter observablecollection

我有ListView我正在填充ObservableCollection

我正在根据TextBox

中输入的文字过滤列表

以下是我正在使用的代码的一部分:

    private void Filter_TextChanged(object sender, TextChangedEventArgs e)
    {
           view = CollectionViewSource.GetDefaultView(Elementos_Lista.ItemsSource);
           view.Filter = null;
           view.Filter = new Predicate<object>(FilterList);
    }

如果我想通过一个条件过滤列表,这没关系,但每当我想混合多个文本框进行过滤时,它总是根据ItemsSource进行过滤,而不是当前的结果集,这意味着有没有积累标准。

这是我的FilterList方法。

 ItemDetail item = obj as ItemDetail;  
 if (item == null) return false;
 string  textFilter = txtFilter.Text; 
 if (textFilter.Trim().Length == 0) return true; //this returns the unfiltered results.
 if ((item.problema.ToLower().Contains(textFilter.ToLower()))) return true;  
 return false;  

有没有办法按多个条件过滤ObservableCollection(view),这些条件并不总是同时提供?。

我尝试更改FilterList方法以评估各种文本框,但我必须制作一个IF语句来检查匹配条件的所有可能性。

(filter1=value , filter2=value) OR 
(filter1=value , filter2=empty) OR 
(filter1=empty , filter2=value)

由于我打算使用至少5种不同的控件进行过滤,这根本不会很有趣。

示例:

List:
Maria, Age:26
Jesus, Age:15
Angela, Age:15

第一个过滤器

Filters:
Name: //Empty
Age: 15

Result:
Jesus, Age:15
Angela, Age:15

第二次过滤:

Filters:
Name: Jesus
Age: 15

Result:
Jesus, Age:15

我要做的是将过滤器应用于已经过滤的集合,而不是原始过滤集合,这种方法会用下一个过滤器覆盖应用的过滤器。

1 个答案:

答案 0 :(得分:1)

好的,让我们看看,我周围有类似的东西......

[ContractClass(typeof(CollectionViewFilterContracts<>))]
public interface ICollectionViewFilter<in T> where T : class
{
    bool FilterObject(T obj);
}

合同当然是可选的(CodeContracts

    [ContractClassFor(typeof(ICollectionViewFilter<>))]
    public abstract class CollectionViewFilterContracts<T> : ICollectionViewFilter<T> where T : class
    {
        public bool FilterObject(T obj)
        {
            Contract.Requires<ArgumentNullException>(obj != null, "Filtered object can't be null");

            return default(bool);
        }
    }

然后是基本实现,据我所知,你只使用字符串进行比较,所以这里只是字符串版本:

public abstract class CollectionFilterBase<T> : ICollectionViewFilter<T> where T : class
    {

        private readonly Dictionary<string, string> filters = new Dictionary<string, string>(10);
        private readonly PropertyInfo[] properties;

        protected CollectionFilterBase()
        {
             properties = typeof(T).GetProperties();
        }        

        protected void AddFilter(string memberName, string value)
        {
            if (string.IsNullOrEmpty(value))
            {
                filters.Remove(memberName);
                return;
            }

            filters[memberName] = value;
        }

        public virtual bool FilterObject(T objectToFilter)
        {
             foreach (var filterValue in filters)
             {
                 var property = properties.SingleOrDefault(x => x.Name == filterValue.Key);
                 if(property == null)
                     return false;

                 var propertyValue = property.GetValue(objectToFilter, null);
                 var stringValue = propertyValue == null ? null : propertyValue.ToString(); // or use string.Empty instead of null, depends what you're going to do with it.
                 // Now you have the property value and you have your 'filter' value in filterValue.Value, do the check, return false if it's not what you're looking for.
                 //The filter will run through all selected (non-empty) filters and if all of them check out, it will return true.
             }

            return true;
        }
  }

现在一些有意义的实现,让我们说这是你的类,为简单起见:

public class Person
{
    public string Name {get;set;}
    public int Age {get;set;}
}

过滤器实现 - 同时 - 包含所有“过滤”控件的视图后面的视图模型,因此您可以相应地将文本框值绑定到属性。

public class PersonFilter : CollectionFilterBase<Person>
{
    private string name;
    public string Name
    {
        get
        {
            return name;
        }

        set
        {
            name = value;
            //NotifyPropertyChanged somehow, I'm using Caliburn.Micro most of the times, so:
            NotifyOfPropertyChange(() => Name);
            AddFilter("Name", Name);            
        }
    }
    private int age;
    public int Age
    {
        get
        {
            return age;
        }
        set
        {
            age = value;
            //Same as above, notify
            AddFilter("Age", Age.ToString()) // only a string filter...
        }
    }
}

然后在视图模型中有一个PersonFilter对象实例。

ICollectionViewFilter<Person> personFilter = new PersonFilter();

然后你只需要在Filter上使用CollectionView事件来查看视图模型中的某些方法,例如:

CollectionView.Filter += FilterPeople

private void FilterPeople(object obj)
{
    var person = obj as Person;
    if(person == null)
        return false;
    return personFilter.FilterObject(person);
}

过滤器名称必须与过滤对象上的属性名称相同。 :)

当然你必须在某个地方调用CollectionView.Refresh();,你可以将其移动到过滤器(例如,当属性发生变化时,你可以调用CollectionView.Refresh()立即查看更改),你可以在事件处理程序中调用它,无论你想要什么。

这很简单,虽然性能可能会更好。除非您使用几十个过滤器过滤一公吨数据,否则调整和使用片段不会有太多问题。 :)