2010-09-15 6 views
5

J'ai le modèle de données suivantes appliquées à un ListBox:Comment sélectionner ListBoxItem en cliquant sur le bouton dans Template?

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

Lorsque je clique sur le bouton de la commande, mais le feu se ListBoxItem ne soit pas sélectionné. Comment puis-je le forcer à être sélectionné, afin que je puisse obtenir l'élément sélectionné dans ma méthode "exécutée"?

Merci

+0

Je pourrais passer le bouton à travers la commande, et trouver le bouton de l'autre côté, mais c'est une sorte de piratage, je me sens ... –

+0

Eh bien, j'ai supprimé ma réponse (à savoir ajouter un déclencheur qui sélectionne le ListBoxItem lorsque le focus du clavier est à l'intérieur) car à la réflexion, ce n'est peut-être pas la meilleure façon de gérer cela dans la plupart des cas. – ASanch

+0

Même ici. Ma solution initiale était de lier "Self" à CommandParameter, et sur la méthode "execute" I puis obtenir le LisBoxItem comme suit: ListBoxItem selectedListBoxItem = ((ListBoxItem) MyListBox.ContainerFromElement ((DependencyObject) e.Parameter)); Mais je ne pense pas que ce soit très propre ... –

Répondre

8

Une meilleure façon, puisque vous n'êtes pas vraiment intéressé à choisir l'élément (car il se rapidement supprimé de toute façon) serait de passer l'élément lui-même à la commande en tant CommandParameter. Sinon, vous pouvez contourner le problème avec le code-behind ou avec des déclencheurs, mais je ne pense pas que ce soit le cas. Par exemple:

vous pouvez gérer l'événement ButtonBase.Click sur votre zone de liste, comme

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

puis dans votre code derrière, faites ceci:

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

qui obtient l'élément lié cliqué, parce que le datacontext du bouton est hérité de son élément basé sur un modèle, puis l'objet ListBoxItem généré automatiquement à partir de ItemContainerGenerator de ListBox et définit la propriété IsSelected sur true. Je pense que c'est l'un des moyens les plus rapides et les plus faciles. Fonctionne également avec plusieurs objets dérivés de ButtonBase dans le modèle.

Bien sûr, vous pouvez aussi plus résumer bien tout cela (plus ou moins la même exactement) comme Behavior réutilisable:

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

Vous pouvez l'utiliser comme ceci:

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

Ajouter erreur Manipulation du code comme au moins des vérifications nuls, bien sûr - ne voudrait pas une chose simple comme ceci bombarder votre application.

Pour que le problème soit pris en compte, le bouton définit la propriété Handled sur true pour tous les événements de souris qui agissent sur lui (MouseDown/Click) afin qu'ils ne soient pas pris en compte par ListBoxItem. Vous pouvez également attacher l'événement MouseDown à la ListBox et parcourir l'arborescence visuelle jusqu'à ce que vous atteigniez le ListBoxItem parent mais c'est beaucoup plus compliqué ... eh si vous êtes curieux, vous pouvez lire this article pour savoir pourquoi, en gros vous aurez rencontre également FrameworkContentElements (qui répond également à MouseDown) donc le code devient plus compliqué, avec l'avantage que tout ce qui est cliqué dans le datatemplate va déclencher la sélection de ListBoxItem, qu'il ait ou non marqué l'événement comme manipulé. Heh, j'ai aussi essayé de le faire exclusivement avec des styles et des triggers mais ça s'est vite laid et j'ai perdu tout intérêt (et perdu la trace de tous les trucs d'égarement). Fondamentalement, il pourrait être résolu, je pense, mais je ne pense vraiment pas que cela vaut la peine. Peut-être que j'ai oublié quelque chose d'évident, je ne sais pas.

+0

Salut Alex, merci pour votre réponse. Dans cet exemple, j'ai besoin de supprimer l'élément, donc oui, je pourrais simplement passer l'élément à la commande. Mais je voudrais apprendre la manière la plus simple de choisir l'article. –

+0

Bonne réponse. Dans SL ButtonBase vient de base différente et il n'a aucun ButtonBase.ClickEvent. –

1

Faites en sorte que l'objet sous-jacent expose une propriété RemoveCommand et liez la propriété Command du bouton. Cela simplifie le modèle de données; cela simplifie également grandement le cas où la logique de l'application peut imposer qu'un élément spécifique ne puisse pas être supprimé.

+1

À ce stade, l'objet sous-jacent devrait être conscient de la collection non? cela semble mauvais. – jr3

0

Alex, merci pour la réponse.Votre solution avec Behavior est géniale. La première solution n'est pas très bonne parce que cela ne fonctionnera que si vous cliquez sur un bouton spécifique. Voici une autre solution qui fonctionnera sur le modèle de clic ListBoxItem formulaire de commande arbitraire:

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

C'est XAML seule approche. J'ai également défini la propriété BasedOn juste pour être sûr de ne pas remplacer le style ListBoxItem en cours.

+0

Cela entraînera la désélection de l'élément si le focus est défini sur un autre élément. Je ne peux pas utiliser cette approche si je veux qu'elle reste sélectionnée. – Lumo

Questions connexes