miércoles, 8 de febrero de 2017

WPF SearchAll Control (básico)





SearchAll es un control de WPF que realiza de filtrados de manera extremadamente sencilla. SearchAll reduce el tiempo y el esfuerzo y posee una interfaz muy amigable y cuidada. Este control está basado en el control de búsqueda de la página de formación Pluralsight, con la diferencia de que SearchAll es válido para cualquier tipo de datos, ya que es un control completamente genérico.

Vamos a ver todas sus bondades.






Siempre he pretendido buscar una técnica que me librara de ese gasto tan tedioso de tiempo, recurso y espacio que conlleva el el espacio reservado al filtrado en nuestros formularios. SearchAll cubre una gran parte de las necesidades de filtrado, con un código mínimo  y con un gran resultado.





Muy importante:


SearchAll soporta la versión 4.5.2 .NET Framework  y posteriores


Si tu instalas el paquete de Nuget sobre una aplicación WPF con una versión de .NET Framework  inferior a la 4.5.2, Visual Studio instalará correctamente el complemento pero el assembly no aparecerá en references.

NO hemos testeado el control para UWA, pero la idea es si esta plataforma tiene salida, darle soporte en el futuro.

SearchAll es un control de código abierto que está disponible en GitHub y en descarga por Nuget.





El Control

SearchAll es un control de composición muy simple. Está formado por tres elementos principales:





  • Text to search (Texto a bucar) .- ·   Es la parte donde se introduce el texto a filtrar, cuando hacemos click en esta zona, aparece un popup de búsqueda que ocupa prácticamente la totalidad de la pantalla, y según vamos introduciendo caracteres, nos van apareciendo las coincidencias (en caso de haberlas claro está).
  • Cancel Filter Button (Botón de Cancelación de Filtro) .- ·  Este botón está disponible cuando hemos realizado un filtrado y nos proporciona la opción de cancelar el filtrado.
  • Filter Button (Botón de Filtrado).-  Su función es filtrar nuestra fuente de datos (ItemsSource), pero este botón solo tiene utilidad cuando se ha añadido el texto a buscar mediante código (MVVM-binding o CodeBihind), ya que normalmente este texto se rellena automáticamente al aceptar el filtrado (con la tecla Return) desde el popup de búsqueda. 




Un primer vistazo a una ejecución simple:





See you in HD





Instalación

La instalación del control SearchAll la realizaremos mediante un paquete de Nuget:

1.- Instalaremos el paquete.




También podemos hacerlo desde Package Manager Console




Install-Package MoralesLarios.CustomsControls































See you in HD


2.- En el WPF Toolbox haremos click-derecho y eligiremos ‘Choose Items’ >> Browse. Buscaremos la librería MoralesLarios.CustomControls.dll en la carpeta bin de nuestro proyecto. Recordar que es necesario haber compilado el Proyecto.

















































































































Click OK, y la tendremos disponible en la ventana de herramientas para utilizar.













In movimiento.
































See you in HD


Solo necesitamos arrastrar el control a nuestra window.





SearchAll es un control muy sencillo que para funcionar, solo necesita de la configuración de su propiedad ItemsSource. Normalmente esta fuente de datos, suele coincidir con la fuente de datos de los controles contenedores, tales como (DataGrid, ListBox, ListView, ComboBox, etc), los cuales serán filtrados. Esto lo podemos hacer mediante MVVM o CodeBehind.

Si no configuramos la propiedad ItemsSource del control, esté arrancará deshabilitado


XAML

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication17"
        xmlns:CustomsControls="clr-namespace:MoralesLarios.CustomsControls;assembly=MoralesLarios.CustomsControls" 
        x:Class="WpfApplication17.MainWindow"
        mc:Ignorable="d"
        Title="MainWindow" Height="261.112" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="46"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        
        <CustomsControls:SearchAll x:Name="searchAll"
                                    HorizontalAlignment="Left" 
                                   Margin="50,10,0,0" 
                                   VerticalAlignment="Top" 
                                   Height="29" 
                                   Width="270"
                                   />
 
        <DataGrid x:Name="dgData" Grid.Row="1" AutoGenerateColumns="True" />
    </Grid>
</Window>


Code Behind:

using System.Linq;
using System.Windows;
 
namespace WpfApplication17
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
 
            var data = typeof(Enumerable).GetMethods();
 
            dgData.ItemsSource = data;
 
            searchAll.ItemsSource = data;
        }
    }
}


En acción:



See you in HD






En el primer paso para filtrar hemos pulsado la  Tecla Enter. En el Segundo hemos pulsado un link de opción.











Como trabaja

Explicado de forma sencilla SearchAll trabaja buscando u carácter(es)/palabra(s) en todas las propiedades de los elementos de la secuencia que forman la fuente de datos:

Por defecto solo instanciamos la propiedad ItemsSource.





























En movimiento.


See you in HD



El control SearchAll para realizado su filtrado se aprovecha de la vista vinculado a la fuente de datos (ICollectionView) de la propiedad ItemsSource.





Propiedades más importantes

Para mostrar las propiedades más importantes del control, usaremos una aplicación test de WPF. Esta aplicación tiene un diseño cuidado y tiene bindeadas las propiedades más importantes del control, para poder realizar o ver los cambios en tiempo real.

La fuente de datos de la aplicación, consiste en la consulta de todos los servicios de Windows de la máquina en la que corre (services.msc). Para ello hacemos uso d ela clase System.ServiceProcess.ServiceController de .NET Framework.


 public class ServiceController
{
    public bool                    CanPauseAndContinue  { get; }
    public bool                    CanShutdown          { get; }
    public bool                    CanStop              { get; }
    public ServiceController[]     DependentServices    { get; }
    public string                  DisplayName          { get; set; }
    public string                  MachineName          { get; set; }
    public SafeHandle              ServiceHandle        { get; }
    public string                  ServiceName          { get; set; }
    public ServiceController[]     ServicesDependedOn   { get; }
    public ServiceType             ServiceType          { get; }
    public ServiceControllerStatus Status               { get; }
}


UI





ItemSource

public IEnumerable<object> ItemsSource { get; set; }

La propiedad de dependencia ItemsSource obtiene o establece la colección que será filtrada por el control. Esta es la única propiedad que es necesaria para que el control funcione.

Como hemos dicho anteriormente, si no asignas esta propiedad el control permanecerá deshabilitado.



Text

public string Text { get; set; }


La propiedad de dependencia Text, obtiene o estable el contenido a filtrar.

Cuando la propiedad Text, está vacía los botones Filter y Cancel, permanecen deshabilitados.



NumberSugerencyElements

public int NumberSugerencyElements { get; set; }

La propiedad de dependencia NumberSugerencyElements obtiene o establece el número máximo de sugerencias mostradas en el popup de búsqueda. Esta propiedad es importante a nivel de rendimiento, ya que si se aplica sobre una colección con un número elevado de elementos y con un texto de búsqueda poco probable, las tareas pueden ser pesadas, ya que se debe recorrer prácticamente toda la colección y todas las propiedades. A menor número, menor trabajo.




20 es el valor por defecto.





En movimiento






FilterClass


public FilterType FilterClass { get; set; }

La propiedad de dependencia FilterClass obtiene o estable el modo en que se realizará la búsqueda.

Su tipo de retorno es FilterType y posee estos 5 valores:

  • StartWith
  • EndWith
  • Contains
  • Equals
  • Custom





















Contains es el valor por defecto.

































En nuestra aplicación de pruebas, hemos implementado una forma amigable para poder cambiar esta propiedad en caliente, y poder ir modificando los modos de búsqueda sin tener que para la ejecución, cambiar la propiedad y volver a recompilar. Esto lo podemos distinguir en la parte inferior izquierda de la aplicación, justo debajo de la etiqueta FilterClass. Para cambiar haremos doble-click en los textblocks con la definición de cada una de las posibilidades, al iluminarse nos mostrará cual es el que está activo.






























En movimiento.































Como se puede observar falta por comentar el último tipo, el tipo Custom, este lo dejaremos para un próximo artículo en el que mostraremos un funcionamiento más avanzado del control.



FieldsSugerenciesSearch


public IEnumerable<string> FieldsSugerenciesSearch { get; set; }

La propiedad de dependencia FieldsSugerenciesSearch obtiene o establece el nombre y el orden de las propiedades del tipo de la fuente de datos en el que se buscará el texto de búsqueda. En caso de coincidir será mostrado en las sugerencias.

El valor por defecto es Null. En caso de contener el valor por defecto, la búsqueda se realizará en todas y cada de las propiedades del tipo, por orden de aparición en la clase.




Los ejemplos mostrados en las imágenes, son ejemplos muy simples, ya que solo hemos utilizado una propiedad a buscar. Este modo me parecía más didáctico y considero que hace entender mejor su utilidad. Al ser una colección de cadenas, podremos añadir todas las que queramos.
Para el ejemplo de la izquierda, hemos utilizado el campo DisplayName y para el de la derecha el campo ServiceName.
En nuestra aplicación de pruebas, como en el caso anterior, hemos desarrollado un cambio dinámico de propiedades, para poder realizar también el cambio en caliente, con el mismo objetivo que con la propiedad FilterClass. La forma de cambiarlo es haciendo click en la etiqueta  ‘FieldSearch / SugrenceSearch’.

Así que vamos a verlo en movimiento.





Otro ejemplo con más campos, esta vez en codebehind:





































FieldsSearch

public IEnumerable<string> FieldsSearch { get; set; }


La propiedad de dependencia FieldSearch, es prácticamente igual a FieldsSugerenciesSearch, con la salvedad de que la definición de campos de FieldSearch se utiliza en el momento de realizar el filtrado de los datos.
Normalmente las dos propiedades están configuradas con los mismos valores, pero existen por si por alguna razón se quisieran diferenciar.

Un ejemplo completo con varias propiedades:

































Query Filtered Data

Si queremos consultar los datos filtrados, deberemos hacer lo siguiente:

En CodeBehind:


public void QueryFilterData()
{
    /// Use de ReadOnly Dependency Property FilteredItemsSource
 
    /// --> ServiceController is a data of mi DataSource (ObservableCollection in ItemsSource)
 
    var filteredData = searchAll.FilteredItemdSource.OfType<ServiceController>().ToList();
}


MVVM en el ViewModel:


public void QueryFilterData()
{
    /// Use the ICollectionView of our DataSource
 
    /// --> ServiceController is a data of mi DataSource (ObservableCollection in ItemsSource)
 
    var filteredData = CollectionViewSource.GetDefaultView(Services).OfType<ServiceController>().ToList();
}



Limitaciones

El Sistema de filtrado del control SearchAll, está basado en Reflection, por lo que hay determinadas propiedades, que al acceder a ellas por este método, elevan una excepción. Etas propiedades suelen ser propiedades de apoyo, generación de proxies dinámicos, etc, que tienen poco valor a nivel informativo.
La forma de subsanar estos errores es configurando las propiedades FieldSearch y FieldsSugerenciesSearch , obviando estás propiedades problemáticas, o eliminándolas de su configuración en caso de ya estar utilizando estás dependency properties.
Cuando el control encuentra una de estas propiedades, muestra un mensaje de error, indicando su nombre y la forma de remediar el error.

En movimiento



































Para el próximo artículo

Para el próximo artículo del control SearchAll dejaremos la parte más avanzada. En ella hablaremos de Events, Custom Type FilterClass y algo de su implementación.





AQUÍ ESTÁ EL CÓDIGO DISPONIBLE PARA SU DESCARGA LINK