WPF ListView: attacher un événement de double-clic (sur un élément)

85

J'ai ce qui suit ListView:

<ListView Name="TrackListView">
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Title" Width="100" 
                            HeaderTemplate="{StaticResource BlueHeader}" 
                            DisplayMemberBinding="{Binding Name}"/>

            <GridViewColumn Header="Artist" Width="100"  
                            HeaderTemplate="{StaticResource BlueHeader}"  
                            DisplayMemberBinding="{Binding Album.Artist.Name}" />
        </GridView>
    </ListView.View>
</ListView>

Comment puis-je attacher un événement à chaque élément lié qui se déclenchera en double-cliquant sur l'élément?

Andreas Grech
la source

Réponses:

101

J'ai trouvé la solution à partir d'ici: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/3d0eaa54-09a9-4c51-8677-8e90577e7bac/


XAML:

<UserControl.Resources>
    <Style x:Key="itemstyle" TargetType="{x:Type ListViewItem}">
        <EventSetter Event="MouseDoubleClick" Handler="HandleDoubleClick" />
    </Style>
</UserControl.Resources>

<ListView Name="TrackListView" ItemContainerStyle="{StaticResource itemstyle}">
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Title" Width="100" HeaderTemplate="{StaticResource BlueHeader}" DisplayMemberBinding="{Binding Name}"/>
            <GridViewColumn Header="Artist" Width="100" HeaderTemplate="{StaticResource BlueHeader}" DisplayMemberBinding="{Binding Album.Artist.Name}" />
        </GridView>
    </ListView.View>
</ListView>

C #:

protected void HandleDoubleClick(object sender, MouseButtonEventArgs e)
{
    var track = ((ListViewItem) sender).Content as Track; //Casting back to the binded Track
}
Andreas Grech
la source
13
Si vous n'avez pas besoin de réutiliser le style, vous pouvez le mettre directement dans la section <ListView.Resources /> et supprimer le x: Key.
David Schmitt
8
Cela a fonctionné pour moi aussi. Merci! BTW, vous voudrez probablement arrêter le bouillonnement de l'événement doubleClick dans votre gestionnaire en définissant: e.Handled = true;
Tom A
1
J'ai un problème avec ça. Autrement dit, j'utilise x: styles sans clé dans la fenêtre pour styliser tous les éléments de l'interface utilisateur, y compris les ListViews utilisés dans un contrôle personnalisé sur cette fenêtre. Mettre ce gestionnaire d'événements dans le xaml du contrôle personnalisé désactive le style appliqué dans la fenêtre.
Jeno Csupor
8
Juste par curiosité, existe-t-il une autre façon de faire cela qui ne viole pas MVVM?
Dave
13
En guise d'avertissement: l'utilisation d'un EventSetterpeut entraîner des fuites de mémoire si la cible de son gestionnaire dure plus longtemps que le ListViewItem. J'ai passé les derniers jours à déboguer une grave fuite de mémoire (20 Mo à la fois), pour découvrir que ListViewItems et leur mémoire associée étaient divulgués via un fichier EventSetter.
Zach Johnson
69

Aucune fuite de mémoire (pas besoin de désabonner chaque élément) , fonctionne bien:

XAML:

<ListView ItemsSource="{Binding TrackCollection}" MouseDoubleClick="ListView_MouseDoubleClick" />

C #:

    void ListView_MouseDoubleClick(object sender, MouseButtonEventArgs e)
    {
        var item = ((FrameworkElement) e.OriginalSource).DataContext as Track;
        if (item != null)
        {
            MessageBox.Show("Item's Double Click handled!");
        }
    }
épox
la source
1
Excellent, plus besoin de s'inquiéter des fuites de mémoire, et franchement, c'est vraiment beaucoup plus propre.
ean5533
3
Cela ne suffit pas si votre liste contient un objet complexe. Vous devez utiliser un assistant d'arborescence visuelle pour trouver le ListViewItem parent et à partir de là, vous pouvez prendre le datacontext
ravyoli
3
Propre et simple. Merci.
Eternal21
1
Très gentil et serviable. Dans mon cas, j'ai le bouton de sélection supplémentaire qui effectue l'action de sélection. J'ai donc utilisé le double clic comme suit: 'MouseDoubleClick = "SelectBtn_Click"' 'private void SelectBtn_Click (expéditeur de l'objet, RoutedEventArgs e) {}'
Kishore
3
C'est pourquoi vous faites toujours défiler la réponse acceptée. Juste au cas où ...
aggsol
7

Ma solution était basée sur la réponse de @ epox_sub que vous devriez regarder pour savoir où placer le gestionnaire d'événements dans le XAML. Le code-behind n'a pas fonctionné pour moi car ce ListViewItemssont des objets complexes. La réponse de @ sipwiz était un bon indice pour savoir où chercher ...

void ListView_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
    var item = ListView.SelectedItem as Track;
    if (item != null)
    {
      MessageBox.Show(item + " Double Click handled!");
    }
}

Le bonus avec ceci est que vous obtenez la SelectedItemliaison DataContext de ( Trackdans ce cas). L'élément sélectionné fonctionne car le premier clic du double-clic le sélectionne.

Type CAD
la source
4

Pour ceux qui s'intéressent principalement à la maintenance du modèle MVVM, j'ai utilisé la réponse d'Andreas Grech pour contourner le problème.

Flux de base:

L'utilisateur double-clique sur l'élément -> Gestionnaire d'événements dans le code derrière -> ICommand dans le modèle de vue

ProjectView.xaml:

<UserControl.Resources>
    <Style TargetType="ListViewItem" x:Key="listViewDoubleClick">
        <EventSetter Event="MouseDoubleClick" Handler="ListViewItem_MouseDoubleClick"/>
    </Style>
</UserControl.Resources>

...

<ListView ItemsSource="{Binding Projects}" 
          ItemContainerStyle="{StaticResource listViewDoubleClick}"/>

ProjectView.xaml.cs:

public partial class ProjectView : UserControl
{
    public ProjectView()
    {
        InitializeComponent();
    }

    private void ListViewItem_MouseDoubleClick(object sender, MouseButtonEventArgs e)
    {
        ((ProjectViewModel)DataContext)
            .ProjectClick.Execute(((ListViewItem)sender).Content);
    }
}

ProjectViewModel.cs:

public class ProjectViewModel
{
    public ObservableCollection<Project> Projects { get; set; } = 
               new ObservableCollection<Project>();

    public ProjectViewModel()
    {
        //Add items to Projects
    }

    public ICommand ProjectClick
    {
        get { return new DelegateCommand(new Action<object>(OpenProjectInfo)); }
    }

    private void OpenProjectInfo(object _project)
    {
        ProjectDetailView project = new ProjectDetailView((Project)_project);
        project.ShowDialog();
    }
}

DelegateCommand.cs peut être trouvé ici .

Dans mon cas, j'ai une collection d' Projectobjets qui peuplent le ListView. Ces objets contiennent plus de propriétés que celles affichées dans la liste, et j'ouvre un ProjectDetailView(un WPF Window) pour les afficher.

L' senderobjet du gestionnaire d'événements est le selected ListViewItem. Par la suite, le Projectauquel je veux accéder est contenu dans la Contentpropriété.

Micah Vertal
la source
3

Dans votre exemple, essayez-vous de détecter lorsqu'un élément de votre ListView est sélectionné ou lorsqu'un en-tête de colonne est cliqué? Si c'est le premier, vous ajouteriez un gestionnaire SelectionChanged.

<ListView Name="TrackListView" SelectionChanged="MySelectionChanged">

Si c'est le dernier, vous devrez utiliser une combinaison d'événements MouseLeftButtonUp ou MouseLeftButtonDown sur les éléments GridViewColumn pour détecter un double-clic et prendre les mesures appropriées. Vous pouvez également gérer les événements sur le GridView et déterminer à partir de là quel en-tête de colonne se trouvait sous la souris.

Aaron Clauson
la source
Je voulais un événement sur les éléments délimités, pas sur les en
Andreas Grech
C'est un nouveau pour moi. Merci d'avoir mis en place votre réponse (et je supprimerai la déclaration de non-événement DoubleClick de la mienne).
Aaron Clauson
3

L'alternative que j'ai utilisée est Event To Command,

<ListView ItemsSource="{Binding SelectedTrack}" SelectedItem="{Binding SelectedTrack}" >
    <i:Interaction.Triggers>
         <i:EventTrigger EventName="MouseDoubleClick">
              <i:InvokeCommandAction Command="{Binding SelectTrackCommand}"/>
         </i:EventTrigger>
    </i:Interaction.Triggers>
    ...........
    ...........
</ListView>
Nom de code Jack
la source
1

En me basant sur la réponse d'epox_spb , j'ai ajouté une vérification pour éviter les erreurs lors d'un double-clic dans les en-têtes GridViewColumn.

void ListView_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
    var dataContext = ((FrameworkElement)e.OriginalSource).DataContext;
    if (dataContext is Track)
    {
        MessageBox.Show("Item's Double Click handled!");
    }
}
Kramer
la source
très cool - fonctionne avec PowerShell- $myListView.Add_MouseDoubleClick({ Param($sender, $ev); $e = [System.Windows.Input.MouseButtonEventArgs]$ev; $itemData = ([System.Windows.FrameworkElement]$e.OriginalSource).DataContext }); if ($item -ne $null) { Write-Host $itemData; } })--- La diffusion n'est pas requise mais aide à l'ISE à terminer
BananaAcid