Cambiar el Template del item seleccionado en una lista de WPF

Otra de las preguntas recurrentes de la gente es en la listas, combox, items control como cambiar el aspecto de la información que se muestra cuando el usuario selecciona una fila. Esto es muy sencillo de realizar en WPF. Para hacer esto lo primero que intenta la gente es subscribirse al evento SelectionChanged de la lista y cambiar un diferente DataTemplate, pero esto no funciona ya que cambia a toda la lista.

Para realizar esto debemos basarnos en la clase System.Windows.Controls.DataTemplateSelector, debemos de crear una clase que herede de esta y sobrescribir el método SelectTemplate. Basándonos en la aplicación del post DataTemplates en WPF, creamos la clase PersonDataTemplateSelector y heredamos de DataTemplateSelector

public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
           

 

En este método como vemos tenemos el parámetro item, que nos va a pasar el objeto seleccionado, la propiedad container que es ContentPresenter de la lista.  En este método lo que tenemos que implementar es cambiar el DataTemplate dependiendo si el item esta seleccionado o no, yo he creado en recursos un nuevo DataTemplate para cuando este seleccionado, mostrando la imagen y el texto mas grande

<DataTemplate x:Key="TemplatePersonSelect">
        <DataTemplate.Resources>
            <LinearGradientBrush x:Key="backBrush" StartPoint="0,0.5" EndPoint="1,0.5">
                <GradientStop Color="#1100CC22" Offset="0" />
                <GradientStop Color="#8800CC22" Offset="0.97" />
                <GradientStop Color="#AA10FF18" Offset="0.999" />
                <GradientStop Color="#44FFFFFF" Offset="1" />
            </LinearGradientBrush>
            <local:PersonImageConverter x:Key="imageConverter" />
        </DataTemplate.Resources>

        <Border x:Name="personsBorder" Style="{StaticResource PersonBorderStyle}" BorderThickness="8" CornerRadius="5" BorderBrush="#FF201F1F" Width="450">
            <Grid>
                <StackPanel Orientation="Horizontal">

                    <Image Width="60" Height="60" 
                           Source="{Binding Path=ImageRef, 
                        Converter={StaticResource imageConverter}}">
                        <Image.BitmapEffect>
                            <DropShadowBitmapEffect />
                        </Image.BitmapEffect>

                    </Image>

                    <TextBlock x:Name="personName" 
                               Text="{Binding Name}"  
                               Padding="15,15"
                               Foreground="Black" FontSize="20" />

                </StackPanel>
            </Grid>
        </Border>
    </DataTemplate>

Una vez que tenemos el DataTemplate implementamos el método SelectTemplate

 

public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
        {
            Person person = item as Person;
            DataTemplate dataTemplate = null;
            if (person != null)
            {
                Window window = System.Windows.Application.Current.MainWindow;
                ListBox list = window.FindName("personItems") as ListBox;

                Person selectedPerson = list.SelectedItem as Person;
                ContentPresenter pres = container as ContentPresenter;

                if (selectedPerson != null && selectedPerson.Name == person.Name)
                    dataTemplate = pres.FindResource("TemplatePersonSelect") as DataTemplate;
                else
                    dataTemplate = pres.FindResource("TemplatePerson") as DataTemplate;
            }
            return dataTemplate;
        }

 

 

Como podéis observar si la persona seleccionada es la misma que el item enviado por parámetro devolvemos el DataTemplate que queremos para los objetos seleccionado, sino el de por defecto, esto no solo sirve para los seleccionados, sino por ejemplo si la comprobación hubiera sido que el nombre fuese Oscar o lo que queramos, este método nos permite cambiar el DataTemplate dinámicamente.

Ahora lo que tenemos que hacer es implementar el using de XAML para poder referenciar la clase PersonDataTemplateSelector , en nuestra aplicacion

 

xmlns:local="clr-namespace:WPFDataTemplates"

Crear el recurso para el DataTemplate Selector

 <Window.Resources>
        <local:PersonDataTemplateSelector x:Key="PersonTemplateSelector" />
    </Window.Resources>

 

Implementarlo en la lista

<ListBox x:Name="personItems" Grid.Row="1" 
            ItemTemplateSelector="{StaticResource PersonTemplateSelector}"
            SelectionChanged="personItems_SelectionChanged"
            HorizontalAlignment="Stretch"   
            Margin="10" 
            VerticalAlignment="Center"
        />

Nos tenemos que fijar en dos cosas, primero debemos de  tener en cuenta es que no podemos tener asignada la propiedad ItemTemplate que especificabamos en el post anterior junto con ItemTemplateSelector. Segundo debemos de implementar el evento SelectionChanged que en nuestro caso seria

private void personItems_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            personItems.ItemTemplateSelector = new PersonDataTemplateSelector();
        }

 

La aplicación nos quedaría:

 

image

7 comentarios sobre “Cambiar el Template del item seleccionado en una lista de WPF”

  1. Como ejemplo está muy bien, pero personalmente preferiría tener la propiedad «IsSelected» en la clase Persona, «bindeada» a «IsSelected» de su correspondiente ListBoxItem, y en el DataTemplate reaccionar a los cambios en esa propiedad de Persona con un DataTrigger. Así me ahorro suscribirme a un evento (SelectionChanged), que suele dar problemas en WPF, además de que no siempre puedes hacerlo en XAML (por ejemplo, si el elemento está en un diccionario de recursos).

  2. Hola Sergi, en efecto era el siguiente post y la causa de que tenga ese efecto raro con las teclas, has sido muy rapido en darte cuenta que es mejor realizarlo con un DataTrigger

    Enhorabuena !!!

  3. cuando doy cursos suelo meter algun error de temas anteriores para ver si la gente esta «despierta» ya vero que si y la verdad es que me alegra

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *