WPF ListView: dołączanie zdarzenia dwukrotnego kliknięcia (na elemencie)

85

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

Jak mogę dołączyć zdarzenie do każdego związanego przedmiotu, które zostanie uruchomione po dwukrotnym kliknięciu przedmiotu?

Andreas Grech
źródło

Odpowiedzi:

101

Rozwiązanie znalazłem tutaj: 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>

DO#:

protected void HandleDoubleClick(object sender, MouseButtonEventArgs e)
{
    var track = ((ListViewItem) sender).Content as Track; //Casting back to the binded Track
}
Andreas Grech
źródło
13
Jeśli nie musisz ponownie używać stylu, możesz umieścić go bezpośrednio w sekcji <ListView.Resources /> i usunąć x: Key.
David Schmitt,
8
Dla mnie to też zadziałało. Dzięki! Przy okazji, prawdopodobnie będziesz chciał zatrzymać propagację zdarzenia doubleClick w swoim module obsługi, ustawiając: e.Handled = true;
Tom A
1
Mam z tym problem. Oznacza to, że używam stylów x: Key -less w oknie do stylizacji wszystkich elementów interfejsu użytkownika, w tym ListViews używanych w niestandardowej kontrolce w tym oknie. Umieszczenie tej procedury obsługi zdarzeń w xaml kontrolki niestandardowej powoduje wyłączenie stylu zastosowanego w oknie.
Jeno Csupor
8
Czy z ciekawości można to zrobić w inny sposób, który nie narusza MVVM?
Dave,
13
Jako ostrzeżenie: użycie an EventSettermoże prowadzić do wycieków pamięci, jeśli cel jego obsługi żyje dłużej niż ListViewItem. Spędziłem kilka ostatnich dni na debugowaniu poważnego wycieku pamięci (20 MB na raz), tylko po to, aby dowiedzieć się, że ListViewItems i związana z nimi pamięć wyciekły przez plik EventSetter.
Zach Johnson,
69

Brak wycieków pamięci (nie ma potrzeby anulowania subskrypcji każdego elementu) , działa dobrze:

XAML:

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

DO#:

    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!");
        }
    }
epoks
źródło
1
Doskonale, nie musisz już martwić się wyciekami pamięci, a szczerze mówiąc, jest to po prostu cholernie dużo czystsze.
ean5533
3
To nie wystarczy, jeśli lista zawiera złożony obiekt. Aby znaleźć element nadrzędny ListViewItem, musisz użyć pomocnika drzewa wizualnego, a stamtąd możesz pobrać
kontekst danych
3
Czyste i proste. Dzięki.
Eternal 21
1
Bardzo miła i pomocna. W moim przypadku mam dodatkowy przycisk wyboru, który wykonuje akcję wyboru. Więc użyłem podwójnego kliknięcia w następujący sposób: 'MouseDoubleClick = "SelectBtn_Click"' 'private void SelectBtn_Click (nadawca obiektu, RoutedEventArgs e) {}'
Kishore
3
Dlatego zawsze przewijasz zaakceptowaną odpowiedź. Na wszelki wypadek ...
aggsol
7

Moje rozwiązanie opierało się na odpowiedzi @ epox_sub, na którą powinieneś zwrócić uwagę, gdzie umieścić Event Handler w XAML. Kod nie działał dla mnie, ponieważ moje ListViewItemssą złożone obiekty. Odpowiedź @ sipwiz była świetną wskazówką, gdzie szukać ...

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

Dodatkową korzyścią jest to, że otrzymujesz SelectedItempowiązanie DataContext ( Trackw tym przypadku). Wybrany element działa, ponieważ pierwsze kliknięcie dwukrotnego kliknięcia powoduje jego zaznaczenie.

Facet z CAD
źródło
4

Dla tych, którzy są zainteresowani głównie utrzymaniem wzorca MVVM, użyłem odpowiedzi Andreasa Grecha, aby dokonać obejścia.

Przepływ podstawowy:

Użytkownik dwukrotnie klika element -> Procedura obsługi zdarzeń w kodzie za -> ICommand w modelu widoku

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 można znaleźć tutaj .

W moim przypadku mam kolekcję Projectobiektów, które wypełniają ListView. Te obiekty zawierają więcej właściwości niż są pokazane na liście, a ja otwieram plik ProjectDetailView(WPF Window), aby je wyświetlić.

senderCelem obsługi zdarzenia jest wybrany ListViewItem. Następnie element, do Projectktórego chcę uzyskać dostęp, jest zawarty w Contentnieruchomości.

Micah Vertal
źródło
3

Czy w Twoim przykładzie próbujesz złapać, gdy element w Twoim ListView jest zaznaczony lub gdy kliknięto nagłówek kolumny? Jeśli jest to pierwszy, należy dodać procedurę obsługi SelectionChanged.

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

Jeśli to drugie, musisz użyć kombinacji zdarzeń MouseLeftButtonUp lub MouseLeftButtonDown na elementach GridViewColumn, aby wykryć dwukrotne kliknięcie i podjąć odpowiednią akcję. Alternatywnie możesz obsłużyć zdarzenia w widoku GridView i ustalić, który nagłówek kolumny znajdował się pod myszą.

Aaron Clauson
źródło
Chciałem zawody na ograniczonych pozycjach, a nie na nagłówkach
Andreas Grech
To dla mnie nowość. Dziękujemy za przesłanie odpowiedzi (i usunę oświadczenie o braku zdarzenia DoubleClick z mojego).
Aaron Clauson
3

Alternatywą, której użyłem, jest 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>
Imię Jack
źródło
1

Opierając się na odpowiedzi epox_spb , dodałem czek, aby uniknąć błędów po dwukrotnym kliknięciu nagłówków 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
źródło
bardzo fajne - działa z 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; } })---
Przesyłanie