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メートルトンのデータをフィルター処理する場合を除いて、スニペットの調整と使用に関してそれほど多くの問題が発生することはありません。:)