En WPF, ¿puede filtrar un CollectionViewSource sin código?

Realmente el sujeto lo dice todo.

 

No es que no pueda tener código detrás. Simplemente me irrita.

Puedes hacer casi cualquier cosa en XAML si “te esfuerzas lo suficiente”, hasta escribir progtwigs completos en él .

Nunca se olvidará el código (bueno, si utiliza bibliotecas, no tiene que escribir ninguna, pero la aplicación todavía depende de ella, por supuesto), aquí hay un ejemplo de lo que puede hacer en este caso específico:

        
 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 _filters = new Collection(); public ICollection 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; } } } } } 

Las extensiones de marcado son su amigo si desea hacer algo en XAML.

(Es posible que desee deletrear el nombre de la extensión, es decir, me:FilterExtension ya que la comprobación sobre la marcha en Visual Studio puede quejarse sin razón, todavía se comstack y se ejecuta, por supuesto, pero las advertencias pueden ser molestas.
Además, no espere que CollectionViewSource.Filter muestre en IntelliSense, no espera que configure ese controlador a través de la notación de elementos XML)

En realidad, ni siquiera necesita acceder a la instancia de CollectionViewSource , puede filtrar la colección de origen directamente en ViewModel:

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

(Tenga en cuenta que ICollectionView.Filter no es un evento como CollectionViewSource.Filter , es una propiedad de tipo Predicate )

WPF crea automáticamente un CollectionView o tipo derivado como ListCollectionView , BindingListCollectionView , etc. (depende de las capacidades detectadas en su colección de origen) – para cualquier enlace de ItemsSource , si no proporciona uno cuando vincula su IEnumerable – origen derivado directamente a una propiedad ItemsControl.ItemsSource .

Esta instancia de CollectionView suministrada automáticamente es creada y mantenida por el sistema por colección (nota: no por control UI u objective vinculante). En otras palabras, habrá exactamente una vista “predeterminada” compartida globalmente para cada fuente a la que se enlace, y esta instancia única de CollectionView se puede recuperar (o crear a pedido) en cualquier momento pasando el IEnumerable al método estático CollectionViewSource.GetDefaultView() .

A veces, incluso si intenta vincular explícitamente su propio tipo específico de CollectionView a un ItemsSource , el motor de enlace de datos de WPF puede envolverlo (utilizando el tipo interno CollectionViewProxy ).

En cualquier caso, cada ItemsControl con una propiedad ItemsSource enlazada a ItemsSource siempre terminará con capacidades de clasificación y filtrado, cortesía de algunos de los que prevalecen CollectionView . Puede realizar fácilmente el filtrado / clasificación de cualquier IEnumerable dado agarrando y manipulando su CollectionView “predeterminado”, pero tenga en cuenta que todos los destinos de datos en la interfaz de usuario que terminan utilizando esa vista, ya sea porque está explícitamente obligado a CollectionViewSource.GetDefaultView() , o porque no proporcionó ninguna vista en absoluto, todos compartirán los mismos efectos de clasificación / filtrado.

Lo que no se menciona a menudo sobre este tema es que, además de vincular la colección de origen a la propiedad ItemsSource de un ItemsControl (como un objective vinculante), también puede acceder “simultáneamente” a la colección efectiva de filtros aplicados / resultados de clasificación, expuestos como una instancia derivada de CollectionView de System.Windows.Controls.ItemCollection –by vinculando desde la propiedad de los Items del control (como fuente de enlace).

Esto permite numerosos escenarios XAML simplificados:

  1. Si tener una única capacidad de filtro / ordenación global compartida para la fuente de IEnumerable dada es suficiente para su aplicación, entonces simplemente ItemsSource directamente a ItemsSource . Aún en XAML solamente, puede luego filtrar / ordenar los elementos tratando la propiedad Items en el mismo control como un ItemCollection enlace de ItemCollection . Tiene muchas propiedades enlazables útiles para controlar el filtro / clasificación. Como se señaló, el filtrado / clasificación se compartirá entre todos los elementos de la interfaz de usuario que están vinculados a la misma fuente IEnumerable de esta manera. –o–

  2. Cree y aplique usted mismo una o más instancias de CollectionView distintas (no predeterminadas). Esto permite que cada destino de datos tenga una configuración de filtrado / clasificación independiente. Esto también se puede hacer en XAML , y / o puede crear sus propias (List)CollectionView clases derivadas de (List)CollectionView . Este tipo de enfoque está bien cubierto en otros lugares, pero lo que quería señalar aquí es que en muchos casos el XAML se puede simplificar utilizando la misma técnica de enlace de datos a la propiedad ItemsControl.Items (como fuente de enlace) en para acceder al CollectionView efectivo .

Resumen: con XAML solo, puede vincular datos a una colección que represente los resultados efectivos de cualquier filtro / clasificación de CollectionView actual en un WPF ItemsControl tratando su propiedad Items como un origen de enlace de solo lectura. Este será un System.Windows.Controls.ItemCollection que expone las propiedades enlazables / mutables para controlar el filtro activo y los criterios de clasificación.


[editar – más pensamientos:]
Tenga en cuenta que en el caso simple de vincular su IEnumerable directamente a ItemsSource , la ItemCollection que puede vincularse en ItemsControl.Items será un contenedor en CollectionViewSource.GetDefaultView() la colección original. Como se discutió anteriormente, en el uso de XAML no es necesario enlazar a este contenedor de interfaz de usuario (a través de ItemsControl.Items ), en lugar de vincularlo a la vista subyacente que envuelve (a través de CollectionViewSource.GetDefaultView ), ya que el enfoque anterior le ahorra el problema de lo contrario, mencionar una CollectionView en absoluto.

Pero además, dado que ItemCollection envuelve el CollectionView predeterminado, me parece que, incluso en código subyacente (donde la elección es menos obvia), es quizás más útil vincularse a la vista promulgada por la UI, ya que es probable que esté mejor sintonizada. a las capacidades de la fuente de datos y su objective de control de la interfaz de usuario.