2010-09-15 11 views
5

que tienen la siguiente plantilla de datos aplicada a un cuadro de lista:¿Cómo seleccionar ListBoxItem al hacer clic en el botón en la Plantilla?

<DataTemplate x:Key="MyTemplate" DataType="{x:Type DAL:Person}"> 
    <StackPanel Orientation="Horizontal"> 
     <Button Content="X" Command="{x:Static cmd:MyCommands.Remove}"/> 
     <TextBlock Text="{Binding Person.FullName}" /> 
    </StackPanel> 
</DataTemplate> 

Cuando hago clic en el botón de comando es despedido, pero el ListBoxItem no quede seleccionado. ¿Cómo fuerzo que se seleccione, para que pueda obtener el elemento seleccionado en mi método "ejecutado"?

Gracias

+0

Pude pasar el botón a través del comando, y encontrar el botón en el otro lado, pero esto es como un hack, siento ... –

+0

Bueno, eliminé mi respuesta (es decir, agregué un disparador que seleccionaba el ListBoxItem cuando el foco del teclado está dentro) porque pensándolo bien, puede que no sea la mejor manera de manejar esto en la mayoría de los casos. – ASanch

+0

Lo mismo aquí. Mi solución inicial era vincular "Self" a CommandParameter, y en el método "ejecutado", obtengo el LisBoxItem de la siguiente manera: ListBoxItem selectedListBoxItem = ((ListBoxItem) MyListBox.ContainerFromElement ((DependencyObject) e.Parameter)); Pero no creo que esto esté muy limpio ... –

Respuesta

8

una mejor manera, ya que no está realmente interesado en la selección del material (ya que rápidamente se se eliminan todos modos) serían para pasar el elemento en sí al Comando como CommandParameter.

De forma alternativa, puede continuar de forma indirecta con código subyacente o desencadenadores, pero no creo que sea así. Por ejemplo:

se podía controlar el evento ButtonBase.Click en el cuadro de lista, como

<ListBox ButtonBase.Click="lb_Click" 
... 

entonces en su código detrás, hacer esto:

private void lb_Click(object sender, RoutedEventArgs e) 
{ 
    object clicked = (e.OriginalSource as FrameworkElement).DataContext; 
    var lbi = lb.ItemContainerGenerator.ContainerFromItem(clicked) as ListBoxItem; 
    lbi.IsSelected = true; 
} 

que recibe el elemento de atado se hace clic, porque el contexto de datos del botón se hereda de su elemento con plantilla, entonces el elemento ListBoxItem autogenerado de ItemContainerGenerator de ListBox y establece la propiedad IsSelected en true. Creo que esa es una de las maneras más rápidas y fáciles. También funciona con múltiples objetos derivados de ButtonBase en la plantilla.

Por supuesto también se puede más bien encapsular todo esto (más o menos exactamente lo mismo) como un reutilizable Behavior:

public class SelectItemOnButtonClick : Behavior<ListBox> 
{ 
    protected override void OnAttached() 
    { 
     base.OnAttached(); 
     this.AssociatedObject.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(handler), true); 
    } 
    protected override void OnDetaching() 
    { 
     this.AssociatedObject.RemoveHandler(ButtonBase.ClickEvent, new RoutedEventHandler(handler)); 
     base.OnDetaching(); 
    } 
    private void handler(object s, RoutedEventArgs e) 
    { 
     object clicked = (e.OriginalSource as FrameworkElement).DataContext; 
     var lbi = AssociatedObject.ItemContainerGenerator.ContainerFromItem(clicked) as ListBoxItem; 
     lbi.IsSelected = true; 
    } 
} 

Usted puede utilizar de esta manera:

<ListBox xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" ...> 
    <i:Interaction.Behaviors> 
     <local:SelectItemOnButtonClick /> 
    </i:Interaction.Behaviors> 
</ListBox> 

Añadir error código de manejo como al menos controles nulos, por supuesto, no querría algo tan simple como bombardear su aplicación.

Para comprender el problema, el botón establece la propiedad Controlado como verdadero para todos los eventos del mouse que actúan sobre él (MouseDown/Click) para que no sean considerados por el ListBoxItem. También podría adjuntar el evento MouseDown al ListBox y recorrer el árbol visual hacia arriba hasta que llegue al elemento ListBoxItem principal, pero eso es mucho más complicado ... eh, si tiene curiosidad, puede leer this article para saber por qué, básicamente, lo hará también se encuentran FrameworkContentElements (que también responden a MouseDown) por lo que el código se volverá más complicado, con la ventaja de que todo lo que se haga clic dentro de la plantilla de datos activará ListBoxItem para ser seleccionado, independientemente de si marcó el evento como manejado.

Heh, también traté de hacerlo exclusivamente con estilos y desencadenadores, pero se puso feo rápido y perdí interés (y perdí la pista de todas las ... cositas err). Básicamente podría resolverse, creo, pero realmente no creo que valga la pena. Tal vez pasé por alto algo obvio, sin embargo, no lo sé.

+0

Hola Alex, gracias por tu respuesta. En este ejemplo, necesito eliminar el artículo, así que sí, podría pasar el artículo al comando. Pero me gustaría aprender la forma más sencilla de seleccionar el artículo. –

+0

Buena respuesta. En SL ButtonBase proviene de una base diferente y no tiene ButtonBase.ClickEvent. –

1

Haga que el objeto subyacente exponga una propiedad RemoveCommand y vincule la propiedad del botón Command. Esto simplifica la plantilla de datos; también simplifica en gran medida el caso donde la lógica de la aplicación puede dictar que un elemento específico no se puede eliminar.

+1

En este punto, el objeto subyacente debería conocer la colección no? eso parece malo – jr3

0

Alex, gracias por su respuesta.Su solución con Comportamiento es genial. La primera solución no es tan buena porque funcionará solo si haces clic en un botón específico. Aquí es una solución más que funcionará en haga clic en plantilla de formulario de control ListBoxItem arbitraria:

<ListBox.ItemContainerStyle> 
    <Style TargetType="ListBoxItem" 
      BasedOn="{StaticResource {x:Type ListBoxItem}}"> 
     <Style.Triggers> 
      <Trigger Property="IsKeyboardFocusWithin" Value="True"> 
       <Setter Property="IsSelected" Value="True"/> 
      </Trigger> 
     </Style.Triggers> 
    </Style> 
</ListBox.ItemContainerStyle> 

Eso es XAML único enfoque. También configuré la propiedad BasedOn solo para asegurarme de no anular el estilo actual de ListBoxItem.

+0

Esto hará que el elemento se deseleccione si el foco se establece en otro elemento. No puedo usar este enfoque si quiero que permanezca seleccionado. – Lumo

Cuestiones relacionadas