1

我有一个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);
    }

如果我只想按一个标准过滤列表,这可以正常工作,但每当我想混合超过 1 个文本框进行过滤时,它总是根据 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 (视图),这些条件并不总是同时提供?

我试图更改 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

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

4

1 回答 1

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()以立即查看更改),你可以在事件处理程序中调用它,但你想要。

这很简单,尽管性能可能会更好。除非您使用几十个过滤器过滤大量数据,否则在调整和使用代码段时应该不会有太多问题。:)

于 2012-10-31T16:07:23.777 回答