CollectionViewSource 过滤逻辑
我提出的过滤设计往好里说是很笨拙,往坏了说就是有问题。这个想法是有一个基类来支持选择列表,并让子类根据需要添加额外的过滤逻辑。
让我特别困惑的是如何在各种过滤条件发生变化时触发视图进行过滤(请参阅下面的 _ApplyFiler())。这样设置过滤器合适吗?过滤后我应该在哪里取消订阅/将其设置为空?
干杯, Berryl
丑陋的代码:
public class SubjectPickerBase<T> : ViewModelBase, ISubjectPicker<T>
where T : class, IAvailableItem, INotifyPropertyChanged, IActivitySubject
{
public CollectionViewSource Subjects { get; private set; }
protected SubjectPickerBase() { }
protected void _Initialize(IEnumerable<T> subjects, string subjectName) {
...
Subjects = new CollectionViewSource { Source = subjects };
_ApplyFilter();
}
protected void _ApplyFilter() {
Subjects.View.Filter += Filter;
}
private bool Filter(object obj)
{
var subject = obj as T;
if (ReferenceEquals(subject, null)) return false;
NotifyPropertyChanged(() => Status);
var isIncludedBySubclass = OnFilter(subject);
var isIncludedByBase = subject.IsAvailable;
return isIncludedByBase & isIncludedBySubclass;
}
/// <summary>Hook to allow implementing subclass to provide it's own filter logic</summary>
protected virtual bool OnFilter(T subject) { return true; }
}
public class ProjectSelectionViewModel : SubjectPickerBase<ProjectViewModel>
{
public ProjectSelectionViewModel(IEnumerable<ProjectViewModel> projects)
{
...
_Initialize(projects, Strings.ActivitySubject__Project);
}
public string DescriptionMatchText {
get { return _descriptionMatchText; }
set {
ApplyPropertyChange<ProjectSelectionViewModel, string>(ref _descriptionMatchText, x => x.DescriptionMatchText, value);
_ApplyFilter();
}
}
private string _descriptionMatchText;
protected override bool OnFilter(ProjectViewModel subject)
{
...
var isDescriptionMatch = subject.IsMatch_Description(DescriptionMatchText);
return isPrefixMatch && isMidfixMatch && isSequenceNumberMatch && isDescriptionMatch;
}
}
The design I've come up with for filtering is awkward at best, and buggy at worst. The idea is to have a base class to support a pick list, and let subclasses add on additional filtering logic as needed.
What is particularly confusing to me is how to trigger the view to filter as various filtering criteria change (see _ApplyFiler(), below). Is setting the filter that way appropriate? Where should I unsubscribe / set it to null after it filters?
Cheers,
Berryl
ugly code:
public class SubjectPickerBase<T> : ViewModelBase, ISubjectPicker<T>
where T : class, IAvailableItem, INotifyPropertyChanged, IActivitySubject
{
public CollectionViewSource Subjects { get; private set; }
protected SubjectPickerBase() { }
protected void _Initialize(IEnumerable<T> subjects, string subjectName) {
...
Subjects = new CollectionViewSource { Source = subjects };
_ApplyFilter();
}
protected void _ApplyFilter() {
Subjects.View.Filter += Filter;
}
private bool Filter(object obj)
{
var subject = obj as T;
if (ReferenceEquals(subject, null)) return false;
NotifyPropertyChanged(() => Status);
var isIncludedBySubclass = OnFilter(subject);
var isIncludedByBase = subject.IsAvailable;
return isIncludedByBase & isIncludedBySubclass;
}
/// <summary>Hook to allow implementing subclass to provide it's own filter logic</summary>
protected virtual bool OnFilter(T subject) { return true; }
}
public class ProjectSelectionViewModel : SubjectPickerBase<ProjectViewModel>
{
public ProjectSelectionViewModel(IEnumerable<ProjectViewModel> projects)
{
...
_Initialize(projects, Strings.ActivitySubject__Project);
}
public string DescriptionMatchText {
get { return _descriptionMatchText; }
set {
ApplyPropertyChange<ProjectSelectionViewModel, string>(ref _descriptionMatchText, x => x.DescriptionMatchText, value);
_ApplyFilter();
}
}
private string _descriptionMatchText;
protected override bool OnFilter(ProjectViewModel subject)
{
...
var isDescriptionMatch = subject.IsMatch_Description(DescriptionMatchText);
return isPrefixMatch && isMidfixMatch && isSequenceNumberMatch && isDescriptionMatch;
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我缺少对视图进行重要操作的几个部分,所有这些都与刷新作为 CollectionViewSource 属性的 CollectionView 有关:
我问题的第一部分是何时设置过滤器。对于我的用例,到目前为止效果最好的方法是注册 CollectionViewSource.Filter 事件,然后在每次更改过滤器时使用 View.Refresh 方法。过滤器事件的初始注册也会触发事件处理程序,您看到的许多 msdn 示例都将其显示为过滤视图的一种方式,仅此而已。但如果你的场景并不简单&用户可以改变一些过滤条件,需要使用上面的一种或多种刷新相关的方法&特性。
我的问题的第二部分与您是否需要取消订阅过滤器事件有关,如果需要,何时取消。好吧,事实证明您不需要取消订阅,但如果您这样做,它会有效地清除视图的任何过滤。许多 msdn 的简单示例正是这样做来清除过滤器的,如果您想完全清除任何过滤,这当然是可行的方法,但对于我的用例来说,这并不是我真正想要的。我想要的是清除一些标准,但不清除其他标准,因此再次使用“刷新”(在正确的时间)给了我所需的行为。
HTH,
贝里尔
There are several pieces to a non-trivial manipulation of the view that I was missing, all having to do with refreshing the CollectionView that is a property of the CollectionViewSource:
The first part of my question was when to set the filter. For my use case, what worked best so far turned out to be registering for the CollectionViewSource.Filter event and then using the View.Refresh method each time a filter is changed. The initial registration of the filter event also triggers the event handler, and many of the msdn samples you see show this as a way of filtering a view, and nothing else. But if your scenario is not trivial & the user can change some filter criteria, you need to use one or more of the above refresh related methods & properties.
The second part of my question had to do with whether you needed to unsubscribe to the filter event, and if so, when. Well, it turns out that you don't need to unsubscribe, but if you do so it effectively clears any filtering of the view. And many of the msdn trivial samples do exactly that to clear the filter, which is certainly the way to go if you want to completely clear any filtering, but for my use case was not what I really wanted. What I wanted was to clear some criteria but not others, and so again using Refresh (at the right time) gave me the desired behavior.
HTH,
Berryl