WPF ListView: adjuntar un evento de doble clic (en un elemento)

85

Tengo lo siguiente 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>

¿Cómo puedo adjuntar un evento a cada elemento vinculado que se activará al hacer doble clic en el elemento?

Andreas Grech
fuente

Respuestas:

101

Encontré la solución desde aquí: 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
fuente
13
Si no necesita reutilizar el estilo, puede ponerlo directamente en la sección <ListView.Resources /> y eliminar la clave x :.
David Schmitt
8
Esto también funcionó para mí. ¡Gracias! Por cierto, probablemente querrá detener la propagación del evento doubleClick dentro de su controlador estableciendo: e.Handled = true;
Tom A
1
Tengo un problema con esto. Es decir, utilizo x: estilos sin clave en la ventana para diseñar todos los elementos de la interfaz de usuario, incluidos los ListViews utilizados en un control personalizado en esa ventana. Poner este controlador de eventos en el xaml del control personalizado deshabilita el estilo aplicado en la ventana.
Jeno Csupor
8
Solo por curiosidad, ¿hay otra forma de hacer esto que no viole MVVM?
Dave
13
Como advertencia: el uso de un EventSetterpuede provocar pérdidas de memoria si el objetivo de su controlador vive más tiempo que el ListViewItem. Pasé los últimos días depurando una fuga de memoria grave (20 MB a la vez), solo para descubrir que ListViewItemse estaban filtrando s y su memoria asociada a través de un archivo EventSetter.
Zach Johnson
69

Sin fugas de memoria (no es necesario cancelar la suscripción de cada elemento) , funciona 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!");
        }
    }
epoxi
fuente
1
Excelente, ya no es necesario preocuparse por las fugas de memoria y, francamente, es mucho más limpio.
ean5533
3
Esto no es suficiente si su lista contiene un objeto complejo. Necesita usar un ayudante de árbol visual para encontrar el ListViewItem principal y desde allí puede tomar el contexto de datos
ravyoli
3
Limpio y sencillo. Gracias.
Eternal21
1
Muy agradable y servicial. En mi caso, tengo el botón de selección adicional que realiza la acción de selección. Así que usé el doble clic de la siguiente manera: 'MouseDoubleClick = "SelectBtn_Click"' 'private void SelectBtn_Click (remitente del objeto, RoutedEventArgs e) {}'
Kishore
3
Por eso siempre pasa de la respuesta aceptada. Por si acaso ...
aggsol
7

Mi solución se basó en la respuesta de @ epox_sub, que debe buscar dónde colocar el controlador de eventos en el XAML. El código subyacente no funcionó para mí porque ListViewItemsson objetos complejos. La respuesta de @ sipwiz fue una gran pista sobre dónde buscar ...

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

La ventaja de esto es que obtiene el SelectedItemenlace DataContext ( Tracken este caso). El elemento seleccionado funciona porque el primer clic del doble clic lo selecciona.

Tipo CAD
fuente
4

Para aquellos interesados ​​en mantener principalmente el patrón MVVM, utilicé la respuesta de Andreas Grech para hacer una solución .

Flujo básico:

El usuario hace doble clic en el elemento -> Controlador de eventos en el código subyacente -> ICommand en el modelo de vista

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 se puede encontrar aquí .

En mi caso, tengo una colección de Projectobjetos que pueblan el ListView. Estos objetos contienen más propiedades de las que se muestran en la lista y abro un ProjectDetailView(un WPF Window) para mostrarlas.

El senderobjeto del controlador de eventos es el seleccionado ListViewItem. Posteriormente, el objeto al Projectque quiero acceder está contenido dentro de la Contentpropiedad.

Miqueas Vertal
fuente
3

En su ejemplo, ¿está tratando de detectar cuándo se selecciona un elemento en su ListView o cuando se hace clic en el encabezado de una columna? Si es lo primero, agregaría un controlador SelectionChanged.

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

Si es el último, tendría que usar alguna combinación de eventos MouseLeftButtonUp o MouseLeftButtonDown en los elementos GridViewColumn para detectar un doble clic y tomar la acción apropiada. Alternativamente, puede manejar los eventos en GridView y determinar desde allí qué encabezado de columna estaba debajo del mouse.

Aaron Clauson
fuente
Quería un evento en los elementos delimitados, no en los encabezados
Andreas Grech
Eso es nuevo para mí. Gracias por enviar su respuesta (y eliminaré la declaración de no evento de DoubleClick de la mía).
Aaron Clauson
3

La alternativa que utilicé es 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>
Nombre de código Jack
fuente
1

Sobre la base de la respuesta de epox_spb , agregué una verificación para evitar errores al hacer doble clic en los encabezados 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
fuente
muy bueno - funciona con 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; } })--- No se requiere
conversión,