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

2番目のフィルター:

Filters:
Name: Jesus
Age: 15

Result:
Jesus, Age:15

私がやろうとしているのは、元のコレクションではなく、すでにフィルタリングされたコレクションにフィルターを適用することです。このアプローチは、適用されたフィルターを次のフィルターで上書きします。

4

1 に答える 1

1

OK、見てみましょう、私はこのあたりに似たようなものがありました...

[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();

次に、ビューモデルのtosomeメソッドの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()て変更をすぐに確認できます)。必要に応じて、イベントハンドラーで呼び出すことができます。

パフォーマンスは向上する可能性がありますが、これは非常に簡単です。数十のフィルターを使用して1メートルトンのデータをフィルター処理する場合を除いて、スニペットの調整と使用に関してそれほど多くの問題が発生することはありません。:)

于 2012-10-31T16:07:23.777 に答える