В WPF вы можете фильтровать CollectionViewSource без кода?

на самом деле в теме все сказано.

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

дело не в том, что я не могу иметь код позади. Это меня просто бесит.

3 ответов


вы можете сделать почти все в XAML, если вы "достаточно стараетесь",до написания целых программ в нем.

вы никогда не обойдете код позади (ну, если вы используете библиотеки, вам не нужно писать, но приложение все еще полагается на него, конечно), вот пример того, что вы можете сделать в этом конкретном случае:

<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-элемент-нотацию)


на самом деле вам даже не нужен доступ к CollectionViewSource экземпляр, вы можете фильтровать исходную коллекцию непосредственно в ViewModel:

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

(заметим, что ICollectionView.Filter не является событием, как CollectionViewSource.Filter, это свойство типа Predicate<object>)


WPF автоматически создает CollectionView--или производный тип, такой как ListCollectionView, BindingListCollectionView, etc. (это зависит от возможностей, обнаруженных в вашей исходной коллекции)--для любого ItemsSource привязка, если вы не предоставляете один, когда вы связываете свой IEnumerable - производный источник непосредственно к ItemsControl.ItemsSource собственность.

это автоматически поставляемые CollectionView экземпляр создается и поддерживается системой в сборнике основы (Примечание: не per-UI control или binding цель). Другими словами, для каждого источник что вы привязываетесь, и это уникально CollectionView экземпляр можно получить (или создать по требованию) в любое время, передав IEnumerable к статическому методу CollectionViewSource.GetDefaultView().

иногда, даже если вы пытаетесь явно связать свой собственный конкретный CollectionView-производный тип к ItemsSource, в привязки данных в WPF двигатель может обернуть его (используя внутренний тип CollectionViewProxy).

в любом случае, каждый ItemsControl С привязкой к данным ItemsSource свойство всегда будет в конечном итоге с сортировкой и фильтрацией возможностей, любезно некоторых преобладающих CollectionView. Вы можете легко выполнить фильтрацию / сортировку для любого заданного IEnumerable путем захвата и манипулирования его "по умолчанию"CollectionView, но обратите внимание, что все цели с привязкой к данным в пользовательском интерфейсе, которые в конечном итоге используют это представление-либо потому, что вы явно привязаны к CollectionViewSource.GetDefaultView(), или потому, что вы не предоставил никакого представления вообще-все будут иметь одинаковые эффекты сортировки/фильтрации.

что не часто упоминается на эту тему, в дополнение к обязательной коллекции источник to the ItemsSource свойства ItemsControl (как обязательный цель), вы также можете "одновременно"доступ к эффективной коллекции результатов примененного фильтра / сортировки--exposed как CollectionView-производный экземпляр System.Windows.Controls.ItemCollection--путем связывания С управления Items свойство (как привязка источник).

это включает многочисленные упрощенные сценарии XAML:

  1. если у вас есть одна, глобально разделяемая возможность фильтрации/сортировки для данного IEnumerable источник достаточно для вашего приложения, а затем просто привязать непосредственно к ItemsSource. Все еще в XAML только, вы можете фильтровать / сортировать элементы by лечения Items свойство на том же элементе управления, что и ItemCollection обязательные источник. Он имеет много полезных связываемых свойств для управления фильтром / сортировкой. Как уже отмечалось, фильтрация / сортировка будет совместно использоваться всеми элементами пользовательского интерфейса, привязанными к одному источнику IEnumerable таким образом.   --или...

  2. создать и применить один или несколько различных (не"по умолчанию") CollectionView экземпляров себя. Это позволяет каждая цель с привязкой к данным должна иметь независимые настройки фильтра/сортировки. Это также можно сделать в XAML, и/или вы можете создать свой собственный (List)CollectionView-производные классы. Этот тип подхода хорошо освещен в другом месте, но я хотел бы отметить, что во многих случаях XAML можно упростить, используя ту же технику привязка данных к ItemsControl.Items свойства (в качестве привязки источник) для доступа к эффективное CollectionView.

резюме: С XAML в одиночку, вы можете привязать данные к коллекции, представляющие эффективные результаты любой ток CollectionView фильтрация / сортировка на WPF ItemsControl на лечение Items свойство как привязка только для чтения источник. Это будет System.Windows.Controls.ItemCollection который предоставляет свойства bindable / mutable для управления активный фильтр и критерии сортировки.



[править -- дальнейшие мысли:]
обратите внимание, что в простом случае привязки IEnumerable на ItemsSource на ItemCollection вы можете привязать к at ItemsControl.Items будет оберткой на оригинальной коллекции CollectionViewSource.GetDefaultView(). Как обсуждалось выше, в XAML использование это без проблем для привязки к этой оболочке пользовательского интерфейса (через ItemsControl.Items), в отличие от привязки к основному представлению, которое он обертывает (через CollectionViewSource.GetDefaultView), поскольку первый подход избавляет вас от необходимости упоминать a CollectionView на всех.

но дальше, потому что ItemCollection обертывания по умолчанию CollectionView, Мне кажется, что даже в код (где выбор менее очевиден) возможно, более утилитарно привязываться к представлению, обнародованному пользовательским интерфейсом, так как это, вероятно, лучше настроено на возможности обоих источников данных и его пользовательский интерфейс управления цель.