Pregunta

Tengo un expansor en el elemento de planta de un cuadro de lista. Me hace bien. El problema con el que me he encontrado es que me gustaría que el evento ListBox_SelectionChanged se dispare cuando el expansor se expande y/o se seleccione. El evento Mousedown no parece burbujear hasta el cuadro de lista.

Lo que necesito es el ININDEX seleccionado del cuadro de lista. Debido a que el ListBox_SelectionChanged no se dispara, el índice es -1 y no puedo determinar qué elemento ha sido seleccionado.

El evento ListBox_SelectionChanged se dispara si un usuario hace clic en el contenido del expansor después de que se haya expandido. Si solo hacen clic en el expansor, el evento no se dispara. Esto es confuso para el usuario porque visualmente, creen que ya han hecho clic en ese elemento al hacer clic en el encabezado del expansor. Necesito el elemento de caja de lista seleccionado cuando el usuario expande el expansor porque en lo que respecta al usuario, el elemento ahora se selecciona cuando realmente no lo es.

¿Alguna sugiere cómo hacer que esto funcione o alternar formas de determinar el INDEX seleccionado del cuadro de lista con expansores en él?

Código simplificado para referencia:

<Window x:Class="WpfApplication3.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300"
    Loaded="Window_Loaded">
    <Grid Name="Root">
        <ScrollViewer>
            <ListBox SelectionChanged="ListBox_SelectionChanged" ItemsSource="{Binding}">
                <ItemsControl.ItemTemplate >
                    <DataTemplate>
                        <Border>
                            <Expander>
                                <Expander.Header>
                                    <TextBlock Text="{Binding Path=Name}"/>
                                </Expander.Header>
                                <Expander.Content>
                                    <StackPanel>
                                        <TextBlock Text="{Binding Path=Age}"/>
                                        <TextBlock Text="Line 2"/>
                                        <TextBlock Text="Line 3"/>
                                    </StackPanel>
                                </Expander.Content>
                            </Expander>
                        </Border>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ListBox>
        </ScrollViewer>
    </Grid>
</Window>

Clase simple para vinculación:

public class Person
{
    public string Name {
        get;
        set;
    }

    public int Age {
        get;
        set;
    }
}

Creación y población de datos para la vinculación:

private void Window_Loaded(object sender, RoutedEventArgs e) {

    data = new ObservableCollection<Person>();

    data.Add(new Person {
        Name = "One",
        Age=10
    });

    data.Add(new Person {
        Name = "Two",
        Age = 20
    });

    data.Add(new Person {
        Name = "Three",
        Age = 30
    });

    Root.DataContext = data;
}

Este es el evento que necesito (realmente solo el INDEX seleccionado que necesito)

private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) {
    ListBox box = (ListBox)sender;

    // This value is not set because events from Expander are not bubbled up to fire SelectionChanged Event
    int index = box.SelectedIndex;
}
¿Fue útil?

Solución

Una forma alternativa que no depende de la isleada, puede enganchar un evento ampliado/colapsado de expansor al código y usar el siguiente código para encontrar el índice de cuadro de lista en el que hizo clic.

DependencyObject dep = (DependencyObject)e.OriginalSource;

while ((dep != null) && !(dep is ListViewItem))
{
   dep = VisualTreeHelper.GetParent(dep);
}

if (dep == null)
     return;

int index = yourListBox.ItemContainerGenerator.IndexFromContainer(dep);

Otros consejos

Lo que quería es obtener el control de expansores controla la selección de ListBox. Puede archivarlo fácilmente estableciendo una vinculación de Twoway en la propiedad ISExpanded del expansor al Inmediate ListBoxItem en el que hizo clic.

 <Expander IsExpanded="{Binding IsSelected,Mode=TwoWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}">

Actualización: si necesita evitar el colapso automático cuando seleccione otro elemento, haga que ListBox Selection Mode sea múltiple.

<ListBox SelectionMode="Multiple"

Gracias Jobi. Eso es bastante inteligente. La madriguera del WPF sigue cada vez más profundo.

Esto es lo que hice en función de su sugerencia:

private void Expander_Expanded(object sender, RoutedEventArgs e) {
    DependencyObject dep = (DependencyObject)sender;

    while ((dep != null) && !(dep is ListBoxItem)) {
        dep = VisualTreeHelper.GetParent(dep);
    }

    if (dep == null)
        return;

    int index = PersonList.ItemContainerGenerator.IndexFromContainer(dep);

    PersonList.SelectedIndex = index;
}

private void Expander_Collapsed(object sender, RoutedEventArgs e) {
    DependencyObject dep = (DependencyObject)sender;

    while ((dep != null) && !(dep is ListBoxItem)) {
        dep = VisualTreeHelper.GetParent(dep);
    }

    if (dep == null)
        return;

    int index = PersonList.ItemContainerGenerator.IndexFromContainer(dep);

    if (PersonList.SelectedIndex == index)
        PersonList.SelectedIndex = -1;
}

Tuve que cambiar el ListViewItem a ListBoxItem (estaba usando un cuadro de lista).

Además, utilicé el índice para seleccionar o deseleccionar el ListBox.SelectedIndex. Esto me da la experiencia que estaba buscando.

  1. La primera vez que alguien expande a un expansor, selecciona el recientemente expandido ListBoxItem.

  2. Si alguien expande a otro expansor, el ListBoxItem anterior está deseleccionado, pero permanece expandido, se selecciona el recientemente expandido ListBoxItem.

  3. Si alguien colapsa un expansor seleccionado, el ListBoxItem no está seleccionado.

  4. Si hay varios expansores expandidos, alguien colapsa un expansor ListBoxItem no seleccionado, el ListBoxItem previamente seleccionado permanece seleccionado.

Gracias por la ayuda: creo que este es un pequeño fragmento de código muy útil para cualquiera que use expansores en un cuadro de lista.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top