2010-09-15 4 views
5

Ich habe folgende Datenvorlage auf eine ListBox angewendet:Wie wird ListBoxItem beim Klicken auf die Schaltfläche in der Vorlage ausgewählt?

<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> 

Wenn ich auf die Schaltfläche klicken Sie auf den Befehl wird ausgelöst, aber die ListBoxItem ausgewählt nicht erhalten. Wie erzwinge ich die Auswahl, damit ich das ausgewählte Element in meiner "ausgeführten" Methode bekommen kann?

Dank

+0

Ich könnte den Knopf durch den Befehl übergeben, und finde den Knopf auf der anderen Seite, aber das ist eine Art Hack, ich fühle ... –

+0

Nun, ich löschte meine Antwort (dh einen Trigger hinzufügen, der das ListBoxItem auswählt wenn der Tastaturfokus innerhalb ist), denn auf den zweiten Blick ist es möglicherweise nicht der beste Weg, dies in den meisten Fällen zu handhaben. – ASanch

+0

Das gleiche hier. Meine ursprüngliche Lösung war, "Self" an CommandParameter zu binden, und auf "executed" -Methode bekomme ich dann das LisBoxItem wie folgt: ListBoxItem selectedListBoxItem = ((ListBoxItem) MyListBox.ContainerFromElement ((DependencyObject) e.Parameter)); Aber ich glaube nicht, dass das sehr sauber ist ... –

Antwort

8

Eine bessere Art und Weise, da Sie bei der Auswahl der Artikel nicht wirklich interessiert sind (weil es schnell sowieso bekommt gelöscht wird) wäre das Element selbst als Commandparameter an den Befehl zu übergeben.

Alternativ können Sie auf Umwegen entweder mit Code-Behind oder mit Triggern umgehen, aber ich denke nicht, dass es in Bezug auf den Punkt wäre. Zum Beispiel:

Sie die ButtonBase.Click Veranstaltung auf Ihrer listbox umgehen konnten, wie

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

dann in Ihrem Code hinter, dies zu tun:

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; 
} 

dass das geklickt gebunden Element wird, Da der Datenkontext der Schaltfläche von dem Vorlagenobjekt übernommen wird, wird das tatsächlich automatisch generierte ListBoxItem aus dem ItemContainerGenerator der ListBox abgeleitet und die IsSelected-Eigenschaft auf true festgelegt. Ich denke, das ist einer der schnellsten und einfachsten Wege. Funktioniert auch mit mehreren von ButtonBase abgeleiteten Objekten in der Vorlage.

Natürlich kann man auch schön kapseln all dies (mehr oder weniger genau das gleiche) als wiederverwendbarer 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; 
    } 
} 

Sie es wie folgt verwenden können:

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

Fehler hinzufügen Code wie mindestens Null-Checks natürlich zu behandeln - würde nicht eine einfache Sache wie diese Bombardierung Ihrer App wollen.

Um das Problem zu verstehen, setzt die Schaltfläche die Handled-Eigenschaft für alle Mausereignisse, die darauf einwirken (MouseDown/Click), so wahr, dass sie vom ListBoxItem nicht berücksichtigt werden. Sie können auch das MouseDown-Ereignis an die ListBox anhängen und den visuellen Baum nach oben führen, bis Sie das übergeordnete ListBoxItem erreichen, aber das ist viel komplizierter ... Wenn Sie neugierig sind, können Sie this article lesen, um zu wissen, warum, im Grunde genommen auch auf FrameworkContentElements stoßen (die auch auf MouseDown reagieren), so dass der Code komplizierter wird, mit dem Vorteil, dass alles, was in die Datamapte geklickt wird, das ListBoxItem auslöst, unabhängig davon, ob es das Ereignis als behandelt markiert hat.

Heh, ich habe auch versucht, es ausschließlich mit Stilen und Triggern zu tun, aber es wurde schnell hässlich und ich verlor das Interesse (und verlor den Überblick über all die ... irren Dinger). Grundsätzlich könnte es gelöst werden, denke ich, aber ich glaube nicht, dass es sich lohnt. Vielleicht habe ich etwas offensichtlich übersehen, weiß es nicht.

+0

Hallo Alex, danke für deine Antwort. In diesem Beispiel muss ich das Element löschen, also könnte ich das Element einfach an den Befehl übergeben. Aber ich möchte die einfachste Art lernen, den Gegenstand zu wählen. –

+0

Gute Antwort. In SL kommt ButtonBase von einer anderen Basis und hat kein ButtonBase.ClickEvent. –

1

Lassen Sie das zugrunde liegende Objekt eine RemoveCommand-Eigenschaft verfügbar machen und binden Sie die Eigenschaft Command der Schaltfläche an die Eigenschaft. Dies vereinfacht die Datenvorlage. Es vereinfacht auch den Fall, in dem Anwendungslogik diktieren kann, dass ein bestimmter Artikel nicht entfernt werden kann.

+1

An diesem Punkt müsste das zugrundeliegende Objekt die Sammlung no kennen? das scheint schlecht zu sein. – jr3

0

Alex, danke für die Antwort.Ihre Lösung mit Behaviour ist großartig. Die erste Lösung ist nicht so gut, denn das funktioniert nur, wenn Sie auf einen bestimmten Button klicken. Hier ist eine weitere Lösung, die auf Klick auf beliebige Kontrollformular ListBoxItem Vorlage arbeiten:

<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> 

dass XAML ist nur Ansatz. Ich habe auch die BasedOn-Eigenschaft festgelegt, um sicherzustellen, dass der aktuelle ListBoxItem-Stil nicht überschrieben wird.

+0

Dadurch wird die Auswahl aufgehoben, wenn der Fokus auf ein anderes Element gesetzt ist. Ich kann diesen Ansatz nicht verwenden, wenn ich möchte, dass er ausgewählt bleibt. – Lumo

Verwandte Themen