在WPF中,您可以在没有代码的情况下过滤CollectionViewSource吗?

时间:2011-06-23 23:31:34

标签: c# wpf xaml filter collectionviewsource

真的是这个主题说明了一切。

<CollectionViewSource x:Key="MyData"
    Source="{Binding}" Filter="{ SomethingMagicInXaml? }" />

并不是说我不能拥有代码。它只是唠叨我。

3 个答案:

答案 0 :(得分:18)

如果你“努力”,up to writing whole programs in it,你可以在XAML中做很多事情。

你永远不会忘记代码(好吧,如果你使用库,你不必编写任何代码,但应用程序当然仍然依赖它),这里有一个例子,说明在这个特定情况下你可以做些什么: / p>

<CollectionViewSource x:Key="Filtered" Source="{Binding DpData}"
                      xmlns:me="clr-namespace:Test.MarkupExtensions">
    <CollectionViewSource.Filter>
        <me:Filter>
            <me:PropertyFilter PropertyName="Name" Value="Skeet" />
        </me:Filter>
    </CollectionViewSource.Filter>
</CollectionViewSource>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Markup;
using System.Windows.Data;
using System.Collections.ObjectModel;
using System.Windows;
using System.Text.RegularExpressions;

namespace Test.MarkupExtensions
{
    [ContentProperty("Filters")]
    class FilterExtension : MarkupExtension
    {
        private readonly Collection<IFilter> _filters = new Collection<IFilter>();
        public ICollection<IFilter> Filters { get { return _filters; } }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return new FilterEventHandler((s, e) =>
                {
                    foreach (var filter in Filters)
                    {
                        var res = filter.Filter(e.Item);
                        if (!res)
                        {
                            e.Accepted = false;
                            return;
                        }
                    }
                    e.Accepted = true;
                });
        }
    }

    public interface IFilter
    {
        bool Filter(object item);
    }
    // Sketchy Example Filter
    public class PropertyFilter : DependencyObject, IFilter
    {
        public static readonly DependencyProperty PropertyNameProperty =
            DependencyProperty.Register("PropertyName", typeof(string), typeof(PropertyFilter), new UIPropertyMetadata(null));
        public string PropertyName
        {
            get { return (string)GetValue(PropertyNameProperty); }
            set { SetValue(PropertyNameProperty, value); }
        }
        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(object), typeof(PropertyFilter), new UIPropertyMetadata(null));
        public object Value
        {
            get { return (object)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }
        public static readonly DependencyProperty RegexPatternProperty =
            DependencyProperty.Register("RegexPattern", typeof(string), typeof(PropertyFilter), new UIPropertyMetadata(null));
        public string RegexPattern
        {
            get { return (string)GetValue(RegexPatternProperty); }
            set { SetValue(RegexPatternProperty, value); }
        }

        public bool Filter(object item)
        {
            var type = item.GetType();
            var itemValue = type.GetProperty(PropertyName).GetValue(item, null);
            if (RegexPattern == null)
            {
                return (object.Equals(itemValue, Value));
            }
            else
            {
                if (itemValue is string == false)
                {
                    throw new Exception("Cannot match non-string with regex.");
                }
                else
                {
                    return Regex.Match((string)itemValue, RegexPattern).Success;
                }
            }
        }
    }
}

如果您想在XAML中执行某些操作,则标记扩展名是您的朋友。

(您可能想要拼出扩展名,即me:FilterExtension,因为Visual Studio中的动态检查可能会无缘无故地抱怨,它仍然会编译并运行当然但是警告可能很烦人 另外,不要指望CollectionViewSource.Filter出现在IntelliSense中,它不希望您通过XML-element-notation设置该处理程序)

答案 1 :(得分:16)

实际上您甚至不需要访问CollectionViewSource实例,您可以直接在ViewModel中过滤源集合:

ICollectionView view = CollectionViewSource.GetDefaultView(collection);
view.Filter = predicate;

(请注意,ICollectionView.Filter不是CollectionViewSource.Filter之类的事件,它是Predicate<object>类型的属性

答案 2 :(得分:1)

WPF 会自动创建CollectionView或派生类型,例如ListCollectionViewBindingListCollectionView等(具体取决于您在源代码中检测到的功能)集合) - 对于任何ItemsSource绑定,如果在将IEnumerable派生的源直接绑定到ItemsControl.ItemsSource属性时未提供一个绑定。

系统会在 每个集合 的基础上创建和维护此自动提供的CollectionView实例(注意:不是每个UI控件或绑定目标)。换句话说,您绑定的每个将只有一个全局共享的“默认”视图,并且可以在以下检索(或按需创建)此唯一CollectionView实例。任何时候将IEnumerable传递给静态方法CollectionViewSource.GetDefaultView()

有时即使您尝试将自己的特定CollectionView派生类型显式绑定到ItemsSource,WPF数据绑定引擎也可以将其包装(使用内部类型CollectionViewProxy)。

在任何情况下,每个具有数据绑定ItemsControl属性的ItemsSource将始终以排序和过滤功能结束,这得益于一些流行的CollectionView。您可以通过抓取并操纵其“默认”IEnumerable轻松地对任何给定的CollectionView执行过滤/排序,但请注意UI中最终使用该视图的所有数据绑定目标 - 要么因为您明确绑定到CollectionViewSource.GetDefaultView(),或者因为您根本没有提供任何视图 - 所有视图都会共享相同的排序/过滤效果。

  

除了将源集合 绑定到 ItemsSource的{​​{1}}属性之外,此主题上没有经常提及的内容(如绑定目标),您还可以“同时” 访问已应用过滤器/排序结果的有效集合 - 公开为{{1 - System.Windows.Controls.ItemCollection的派生实例 - 通过绑定 来自 控件的ItemsControl属性(作为绑定 )。

这可以实现许多简化的XAML场景:

  1. 如果为您的应用提供针对给定CollectionView来源的单一全局共享过滤器/排序功能,则只需直接绑定到Items即可。仍然只在 XAML 中,您可以通过 将同一控件上的IEnumerable属性视为ItemCollection 来对项目进行过滤/排序绑定来源。它有许多有用的可绑定属性来控制过滤器/排序。如上所述,过滤/排序将在以这种方式绑定到同一源ItemsSource的所有UI元素之间共享。 --or -

  2. 自己创建并应用一个或多个不同的(非“默认”)Items实例。这允许每个数据绑定目标具有独立的过滤器/排序设置。这也可以在 XAML 中完成,和/或您可以创建自己的IEnumerable派生类。这种方法在其他地方有很好的涵盖,但我想在此指出的是,在许多情况下,通过使用 数据绑定到CollectionView的相同技术可以简化XAML属性 (作为绑定),以便访问 有效(List)CollectionView

  3. <强>要点: 仅使用 XAML ,您就可以数据绑定到表示WPF ItemsControl.Items上任何当前CollectionView过滤/排序的有效结果的集合。将其CollectionView属性视为只读绑定 。这将是ItemsControl,它公开可绑定/可变属性以控制活动过滤器和排序条件。


    <小时/> [编辑 - 进一步的想法:]注意,在将Items直接绑定到System.Windows.Controls.ItemCollection的简单情况下,ItemCollection可以绑定到IEnumerable将成为原始集合ItemsSource的包装器。如上所述,在 XAML 用法中,绑定到此UI包装器(通过ItemsControl.Items)是明智的,而不是绑定到它包装的底层视图(通过{{1} }),因为前一种方法可以省去你提到CollectionViewSource.GetDefaultView()的麻烦。

    但更进一步,因为ItemsControl.Items 包装默认CollectionViewSource.GetDefaultView,在我看来,即使在 代码隐藏中也是如此(选择不那么明显)它可能更加实用,可以绑定到UI发布的视图,因为这可能更好地适应数据源的UI的功能控制目标。