В WPF вы можете фильтровать CollectionViewSource без кода позади? - PullRequest
11 голосов
/ 24 июня 2011

Действительно субъект говорит все это.

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

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

Ответы [ 3 ]

19 голосов
/ 24 июня 2011

Вы можете делать почти все в 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-элемента)

16 голосов
/ 24 июня 2011

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

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

(обратите внимание, что ICollectionView.Filter - это не событие, подобное CollectionViewSource.Filter, это свойство типа Predicate<object>)

1 голос
/ 05 июня 2018

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

Этот автоматически предоставленный экземпляр CollectionViewсоздается и поддерживается системой на основе для коллекции (примечание: не контроль или привязка для каждого пользовательского интерфейса target ).Другими словами, для каждого s̲o̲u̲r̲c̲e̲ , который вы привязываете, будет точно один глобально общий вид «Default», и этот уникальный экземпляр CollectionView может быть извлечен (или создан по требованию) в любое времяпередача IEnumerable статическому методу CollectionViewSource.GetDefaultView().

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

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

В дополнение к обязательному указанию по этому вопросу не часто упоминаетсяисходную коллекцию до свойство ItemsSource ItemsControl (в качестве привязки target ) вы также можете "одновременно" получить доступ к эффективному набору примененных фильтров / результатов сортировки - представлен как CollectionView -обработанный экземпляр System.Windows.Controls.ItemCollection - путем привязки из свойство элемента управления Items (в качестве привязки source ).

Это позволяет использовать многочисленные упрощенные сценарии XAML:

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

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

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



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

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

...