WPF ListView: Anexando um evento de clique duplo (em um item)

85

Eu tenho o seguinte 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>

Como posso anexar um evento a cada item vinculado que será disparado ao clicar duas vezes no item?

Andreas Grech
fonte

Respostas:

102

Encontrei a solução aqui: 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
fonte
14
Se você não precisar reutilizar o estilo, pode colocá-lo diretamente na seção <ListView.Resources /> e remover o x: Key.
David Schmitt
8
Isso funcionou para mim também. Obrigado! BTW, você provavelmente desejará parar o borbulhar do evento doubleClick em seu manipulador definindo: e.Handled = true;
Tom A
1
Eu tenho um problema com isto. Ou seja, eu uso estilos sem chave x: na janela para definir o estilo de todos os elementos da interface do usuário, incluindo ListViews usados ​​em um controle personalizado nessa janela. Colocar este manipulador de eventos no xaml do controle personalizado desativa o estilo aplicado na janela.
Jeno Csupor
8
Só por curiosidade, existe outra maneira de fazer isso que não viole o MVVM?
Dave
13
Como um aviso: o uso de um EventSetterpode causar vazamentos de memória se o destino de seu manipulador viver mais do que o ListViewItem. Passei os últimos dias depurando um sério vazamento de memória (20 MB por vez), apenas para descobrir que se ListViewItema memória associada estava vazando por meio de um EventSetter.
Zach Johnson
73

Sem vazamentos de memória (não há necessidade de cancelar a assinatura de cada item) , funciona bem:

XAML:

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

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!");
        }
    }
epóxi
fonte
1
Excelente, não há mais necessidade de se preocupar com vazamentos de memória e, francamente, é muito mais limpo.
ean5533
3
Isso não é suficiente se sua lista contiver um objeto complexo. Você precisa usar um auxiliar de árvore visual para encontrar o ListViewItem pai e de lá você pode pegar o datacontext
ravyoli
3
Limpo e simples. Obrigado.
Eternal21
1
Muito bom e útil. No meu caso, tenho o botão de seleção adicional que executa a ação de seleção. Então, usei o duplo clique da seguinte forma: 'MouseDoubleClick = "SelectBtn_Click"' 'private void SelectBtn_Click (object sender, RoutedEventArgs e) {}'
Kishore
3
É por isso que você sempre passa pela resposta aceita. Apenas no caso ...
aggsol
7

Minha solução foi baseada na resposta de @epox_sub, que você deve examinar para saber onde colocar o Event Handler no XAML. O code-behind não funcionou para mim porque ListViewItemssão objetos complexos. A resposta de @sipwiz foi uma ótima dica de onde procurar ...

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

O bônus com isso é que você obtém a SelectedItemvinculação DataContext do ( Trackneste caso). Item selecionado funciona porque o primeiro clique do clique duplo o seleciona.

Cara CAD
fonte
4

Para aqueles interessados ​​principalmente em manter o padrão MVVM, usei a resposta de Andreas Grech para fazer uma solução alternativa .

Fluxo básico:

O usuário clica duas vezes no item -> manipulador de eventos no code behind -> ICommand no modelo de visualização

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 pode ser encontrado aqui .

No meu caso, tenho uma coleção de Projectobjetos que povoam o ListView. Esses objetos contêm mais propriedades do que as mostradas na lista e abro um ProjectDetailView(um WPF Window) para exibi-los.

O senderobjeto do manipulador de eventos é o selecionado ListViewItem. Posteriormente, o Projectque desejo acessar está contido na Contentpropriedade.

Micah Vertal
fonte
3

Em seu exemplo, você está tentando capturar quando um item em seu ListView é selecionado ou quando um cabeçalho de coluna é clicado? Se for o primeiro, você adicionaria um manipulador SelectionChanged.

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

Se for o último, você terá que usar alguma combinação de eventos MouseLeftButtonUp ou MouseLeftButtonDown nos itens GridViewColumn para detectar um clique duplo e executar a ação apropriada. Alternativamente, você pode manipular os eventos no GridView e descobrir a partir daí qual cabeçalho da coluna está sob o mouse.

Aaron Clauson
fonte
Eu queria um evento sobre os itens delimitados, não os cabeçalhos
Andreas Grech,
Isso é novo para mim. Obrigado por colocar sua resposta (e vou remover a declaração de nenhum evento da DoubleClick da minha).
Aaron Clauson,
3

A alternativa que usei é 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>
Codinome Jack
fonte
1

Com base na resposta de epox_spb , adicionei uma verificação para evitar erros ao clicar duas vezes nos cabeçalhos 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
fonte
muito legal - funciona com 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; } })--- A fundição não é necessária, mas ajuda no ISE a obter a conclusão
BananaAcid