2009-07-25 19 views
5

Tengo un expansor en la ItemTemplate de un ListBox. Renders bien. El problema que me he encontrado es que me gustaría que el evento ListBox_SelectionChanged se dispare cuando se expande y/o selecciona el expansor. El evento MouseDown no parece burbujear hasta el ListBox.WPF Listbox + Expander events

Lo que necesito es el SelectedIndex del ListBox. Como ListBox_SelectionChanged no se activa, el índice es -1 y no puedo determinar qué elemento se ha seleccionado.

El evento ListBox_SelectionChanged se activa si un usuario hace clic en el contenido del expansor una vez que se ha expandido. Si solo hacen clic en el expansor, el evento no se dispara. Esto es confuso para el usuario porque, a simple vista, piensan que ya han hecho clic en ese elemento al hacer clic en el encabezado del expansor. Necesito el elemento ListBox seleccionado cuando el usuario expande el expansor porque, en lo que respecta al usuario, ahora el elemento está seleccionado cuando realmente no lo está.

¿Alguna sugerencia sobre cómo hacer que esto funcione o formas alternativas de determinar el SelectedIndex 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 Encuadernación:

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

    public int Age { 
     get; 
     set; 
    } 
} 

Crear y llenar los datos para la unió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 SelectedIndex 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; 
} 

Respuesta

1

Una forma alternativa, que duerma depende de la IsSelected, puede enlazar un evento desplegado/contraído de expansión para el código subyacente y utilizar el siguiente código para averiguar el índice ListBox en el que ha hecho 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); 
4

Lo que quería es que los controles de control del expansor sean ListBox Selection. Puede archivar esto fácilmente configurando un enlace de doble vía en la propiedad IsExpanded del expansor al elemento ListBox inmediato que hizo clic.

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

ACTUALIZACIÓN: Si es necesario para evitar el colapso automática al seleccionar otro objeto de prueba, el modo de selección de cuadro de lista a múltiples.

<ListBox SelectionMode="Multiple" 
+0

Ahh sí - ¡eso tiene sentido y funciona! Gracias. – IUnknown

+0

Funciona ahora, pero con un efecto secundario no deseado. Cuando expando un segundo Expansor, el primero se colapsa automáticamente. ¿Hay alguna manera de hacerlo sin colapsar automáticamente el elemento expandido anterior? – IUnknown

+0

Simplemente haga SelectionMode = "Multiple" en el ListBox –

0

Gracias Jobi. Eso es bastante inteligente. El agujero de conejo de WPF se hace cada vez más profundo.

Esto es lo que hice basado en 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; 
} 

que tenía que cambiar el ListViewItem a ListBoxItem (yo estaba usando un ListBox).

Además, utilicé el índice para seleccionar o anular la selección de ListBox.SelectedIndex. Esto me da la experiencia que estaba buscando.

  1. La primera vez que alguien expande un expansor, selecciona el recién expandido ListBoxItem.

  2. Si alguien expande otro expansor, el ListBoxItem anterior no está seleccionado, pero permanece expandido, se selecciona el recién expandido ListBoxItem.

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

  4. Si hay varios Expansores expandidos, alguien colapsa un expansor ListBoxItem no seleccionado, el ListBoxItem seleccionado previamente 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 ListBox.

+0

genial ... Sí, no solo expansor en una plantilla de datos de Listbox ... Cualquier control como Botón/casilla de verificación o cualquier cosa que realmente necesite este tipo de truco. –

+0

Creo que su palabra "hackear" es más apropiada que mi trabajo "fragmento de código". Esta solución de arriba no es muy bonita, funciona, pero no es bonita. Debería haber una solución más elegante que caminar el gráfico visual. – IUnknown