Quiero mostrar algo de contenido personalizado (usando la plantilla de datos) al hacer clic en el botón:

<ContentControl x:Name="content" />
<Button Content="Test" Click="button_Click" />

El botón muestra / oculta contenido como este

VM _vm = new VM();
void button_Click(object sender, RoutedEventArgs e) =>
     content.Content = content.Content == null ? _vm : null;

Aquí está la plantilla de datos:

<DataTemplate DataType="{x:Type local:VM}">
    <ListBox ItemsSource="{Binding Items}" SelectionChanged="listBox_SelectionChanged">
        <ListBox.ItemContainerStyle>
            <Style TargetType="ListBoxItem">
                <Setter Property="IsSelected" Value="{Binding IsSelected}" />
            </Style>
        </ListBox.ItemContainerStyle>
    </ListBox>
</DataTemplate>

Controlador de eventos:

void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e) =>
    Title = "Items: " + ((ListBox)sender).Items.Count;

Viewmodel:

public class VM
{
    public List<Item> Items { get; } = new List<Item> { new Item(), new Item(), new Item() };
}
public class Item
{
    public bool IsSelected { get; set; }
}

El problema : cuando la plantilla de datos se descarga, SelectionChanged para el evento ListBox se eleva sin elementos.

No quiero este evento. No quiero ver "Elementos: 0" después de seleccionar algo y descargar la plantilla de datos.

Pregunta : ¿qué está sucediendo y cómo puedo evitar que esto suceda?


Nota: esto es MCVE muy corto y simplificado, es decir, no todo es bonito, aunque hay puntos clave: plantilla de datos con ListBox dentro, que usa el enlace IsSelected y necesito deshacerme de eso {{X2 }} evento en la descarga.

Pila de llamadas:

0
Sinatr 23 ene. 2018 a las 18:52

3 respuestas

La mejor respuesta

Esto funciona exactamente como se diseñó. Hizo una selección haciendo clic en un elemento en el cuadro de lista. Cuando la plantilla se descarga, el enlace ItemsSource se desconecta y el origen de los elementos se vacía. En ese momento, la selección actual ya no es válida (el elemento no existe en el origen de los elementos), por lo que la selección se borra. Eso es un cambio de selección: la selección pasó de algo a nada. El evento se espera se generará en estas circunstancias.

Rara vez es necesario suscribirse a SelectionChanged. Por lo general, es mejor vincular el SelectedItem a una propiedad en su modelo de vista. Cada vez que cambie la selección, esa propiedad se actualizará. En lugar de responder al evento SelectionChanged, puede responder a ese cambio de propiedad.

Este enfoque evita muy bien el problema que está viendo. Una vez que se descargue la plantilla, el enlace SelectedItem se desconectará, por lo que su modelo de vista ya no se actualizará. En consecuencia, no verá ese cambio final cuando se borre la selección.


Solución alternativa para múltiples selecciones.

Si su ListBox admite múltiples selecciones, puede continuar suscribiéndose a SelectionChanged. Sin embargo, no consulte listBoxItems; en su lugar, escanee a través de _vm.Items y vea qué elementos tienen IsSelected configurados en true. Eso debería indicarle la selección real, y los resultados no deberían verse afectados por la descarga de la plantilla.

También puede determinar que la plantilla se descargó verificando si (sender as ListBox)?.ItemsSource es nulo en su controlador. Sin embargo, esto no debería ser necesario.

2
Mike Strobel 23 ene. 2018 a las 16:30

Creo que esto está ocurriendo porque siempre está sobrescribiendo el contenido:

VM _vm = new VM();
void button_Click(object sender, RoutedEventArgs e) =>
     content.Content = content.Content == null ? _vm : null;

Cámbielo a esto para que la lista solo se asigne una vez, de modo que solo se asigne una vez.

void button_Click(object sender, RoutedEventArgs e)
{
    if (content.Content == null )
    {
        content.Content = _vm; 

        // I also recommend you add the event handler for the ListBox here so it's not fired until you have content.
    }
}
0
Ctznkane525 23 ene. 2018 a las 16:02

Creo que listBox_SelectionChanged cuando descargas la lista este evento se dispara, porque la sección realmente cambió, verifica el recuento de elementos allí, y si es 0 establece el título como predeterminado.

void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e) =>
Title = (((ListBox)sender).Items.Count > 0)? "Items: " + ((ListBox)sender).Items.Count: "Your title";
0
Peter 23 ene. 2018 a las 16:08