WPF ListView: Attaching a double-click (on an item) event
C#WpfXamlC# Problem Overview
I have the following 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>
How can I attach an event to every bound item that will fire on double-clicking the item?
C# Solutions
Solution 1 - C#
Found the solution from here: 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
}
Solution 2 - C#
No memory leaks (no need to unsubscribe each item), works fine:
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!");
}
}
Solution 3 - C#
My solution was based on @epox_sub's answer which you should look at for where to put the Event Handler in the XAML. The code-behind didn't work for me because my ListViewItems
are complex objects. @sipwiz's answer was a great hint for where to look...
void ListView_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
var item = ListView.SelectedItem as Track;
if (item != null)
{
MessageBox.Show(item + " Double Click handled!");
}
}
The bonus with this is you get the SelectedItem
's DataContext binding (Track
in this case). Selected Item works because the first click of the double-click selects it.
Solution 4 - C#
Alternative that I used is 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>
Solution 5 - C#
For those interested in mostly maintaining the MVVM pattern, I used Andreas Grech's answer to make a work-around.
> Basic flow: > > User double-clicks item -> Event handler in code behind -> ICommand in > view model
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 can be found here.
In my instance, I have a collection of Project
objects that populate the ListView
. These objects contain more properties than are shown in the list, and I open a ProjectDetailView
(a WPF Window
) to display them.
The sender
object of the event handler is the selected ListViewItem
. Subsequently, the Project
that I want access to is contained within the Content
property.
Solution 6 - C#
In your example are you trying to catch when an item in your ListView is selected or when a column header is clicked on? If it's the former you would add a SelectionChanged handler.
<ListView Name="TrackListView" SelectionChanged="MySelectionChanged">
If it's the latter you would have to use some combination of MouseLeftButtonUp or MouseLeftButtonDown events on the GridViewColumn items to detect a double click and take appropriate action. Alternatively you could handle the events on the GridView and work out from there which column header was under the mouse.
Solution 7 - C#
Building on epox_spb's answer, I added in a check to avoid errors when double clicking in the GridViewColumn headers.
void ListView_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
var dataContext = ((FrameworkElement)e.OriginalSource).DataContext;
if (dataContext is Track)
{
MessageBox.Show("Item's Double Click handled!");
}
}