WPF ListView: 附加双击(对项目)事件

我有以下 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>

如何将事件附加到双击该项时将触发的每个绑定项?

104031 次浏览

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.

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
}

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!");
}
}

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.

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!");
}
}

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.

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>

if you are populating your Listview through a
ObservableCollection<ItemClass> Items class ,
and non of the answer above works for you "as what happen to me" , then use :

private void ListView_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
var item = ((FrameworkElement)e.OriginalSource).DataContext as ItemClass; //<< your class name here


if (item != null)
{
MessageBox.Show(item.UserName + " : item Double Click handled!");
}
}

of course ItemClass would be your setter/getter class name