WPF ListView-在单击所选项目时检测
我使用的是WPF ListView控件,它显示数据绑定项的列表WPF ListView-在单击所选项目时检测,wpf,listview,Wpf,Listview,我使用的是WPF ListView控件,它显示数据绑定项的列表 <ListView ItemsSource={Binding MyItems}> <ListView.View> <GridView> <!-- declare a GridViewColumn for each property --> </GridView> </ListView.View&g
<ListView ItemsSource={Binding MyItems}>
<ListView.View>
<GridView>
<!-- declare a GridViewColumn for each property -->
</GridView>
</ListView.View>
</ListView>
我正在尝试获取类似于ListView.SelectionChanged
事件的行为,只是我还想检测是否单击了当前选定的项目。如果再次单击同一项,则不会触发SelectionChanged
事件(显然)
最好(最干净)的方法是什么?使用ListView.ItemContainerStyle属性为ListViewItems提供一个事件设置器,该设置器将处理PreviewMouseLeftButtonDown事件。然后,在处理程序中,检查是否选中了单击的项目 XAML:
您可以处理ListView的PreviewMouseLeftButtonUp事件。 不处理PreviewMouseLeftButtonDown事件的原因是,在处理该事件时,ListView的SelectedItem可能仍然为空 XAML:
<ListView ... PreviewMouseLeftButtonUp="listView_Click"> ...
您可以按如下方式处理单击列表视图项:
<ListView.ItemTemplate>
<DataTemplate>
<Button BorderBrush="Transparent" Background="Transparent" Focusable="False">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding DataContext.MyCommand, ElementName=ListViewName}" CommandParameter="{Binding}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Button.Template>
<ControlTemplate>
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
...
...
这些都是很好的建议,但如果我是你,我会在你的视图模型中这样做。在视图模型中,可以创建中继命令,然后将其绑定到项目模板中的单击事件。若要确定是否选择了相同的项目,可以在视图模型中存储对所选项目的引用。我喜欢使用MVVM Light来处理绑定。这使您的项目在将来更容易修改,并允许您在Blend中设置绑定
当所有这些都说了又做了之后,您的XAML将像Sergey建议的那样。在你看来,我会避免使用隐藏的代码。我将避免在这个答案中编写代码,因为有大量的例子
这里有一个:
如果你需要一个例子,请评论,我会添加一个
~z~干杯
我说过我不会做一个例子,但我会的。给你
1) 在项目中,仅添加MVVM灯光库
2) 为视图创建一个类。一般来说,每个视图都有一个视图模型(视图:MainWindow.xaml&&viewModel:MainWindowViewModel.cs)
3) 下面是非常非常基本的视图模型的代码:
所有包含的名称空间(如果它们出现在这里,我假设您已经添加了对它们的引用。MVVM Light在Nuget中)
现在添加一个基本的公共类:
/// <summary>
/// Very basic model for example
/// </summary>
public class BasicModel
{
public string Id { get; set; }
public string Text { get; set; }
/// <summary>
/// Constructor
/// </summary>
/// <param name="text"></param>
public BasicModel(string text)
{
this.Id = Guid.NewGuid().ToString();
this.Text = text;
}
}
在XAML中,需要在代码顶部添加一些名称空间
<Window x:Class="Basic_Binding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:Custom="clr-namespace:GalaSoft.MvvmLight;assembly=GalaSoft.MvvmLight"
Title="MainWindow" Height="350" Width="525">
我加了“我”和“习惯”
以下是列表视图:
<ListView
Grid.Row="0"
Grid.Column="0"
HorizontalContentAlignment="Stretch"
ItemsSource="{Binding ModelsCollection}"
ItemTemplate="{DynamicResource BasicModelDataTemplate}">
</ListView>
<DataTemplate x:Key="BasicModelDataTemplate">
<Grid>
<TextBlock Text="{Binding Text}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonUp">
<i:InvokeCommandAction
Command="{Binding DataContext.SelectItemRelayCommand,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ItemsControl}}}"
CommandParameter="{Binding Id}">
</i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBlock>
</Grid>
</DataTemplate>
以下是ListView的ItemTemplate:
<ListView
Grid.Row="0"
Grid.Column="0"
HorizontalContentAlignment="Stretch"
ItemsSource="{Binding ModelsCollection}"
ItemTemplate="{DynamicResource BasicModelDataTemplate}">
</ListView>
<DataTemplate x:Key="BasicModelDataTemplate">
<Grid>
<TextBlock Text="{Binding Text}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonUp">
<i:InvokeCommandAction
Command="{Binding DataContext.SelectItemRelayCommand,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ItemsControl}}}"
CommandParameter="{Binding Id}">
</i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBlock>
</Grid>
</DataTemplate>
运行应用程序,并检查输出窗口。可以使用转换器处理选定项目的样式
这可能看起来很复杂,但当您需要将视图与ViewModel分离时(例如,为多个平台开发一个ViewModel),这会使以后的工作更加轻松。此外,它还使Blend 10x的工作更加轻松。开发ViewModel后,可以将其交给设计师,让其看起来非常艺术:)。MVVM灯光添加了一些功能,使Blend能够识别您的ViewModel。在大多数情况下,您可以在ViewModel中执行任何想要影响视图的操作
如果有人读到这篇文章,我希望你会觉得这篇文章很有帮助。如果你有问题,请告诉我。在本例中,我使用了MVVM灯光,但您可以在没有MVVM灯光的情况下完成此操作
~Cheers我还建议在单击某个项目后取消选择该项目,并使用MouseDoubleClick事件
private void listBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
try {
//Do your stuff here
listBox.SelectedItem = null;
listBox.SelectedIndex = -1;
} catch (Exception ex) {
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
我无法得到我想要的答案(见Farrukh的评论) 我提出了一个稍有不同的解决方案,这也让人感觉更自然,因为它在鼠标按钮按下时选择项目,然后当鼠标按钮松开时,您可以对其做出反应: XAML: 这对我有用 单击一行将触发代码隐藏 XAML:
实际上,您可以直接在
列表视图上设置处理程序,不需要EventSetter.item.IsSelected在第一次单击时无效,因此我必须双击。如果(item!=null){}工作正常。@Gábor这样设置处理程序在选择item时不会触发。@FarrukhWaheed我添加了另一个解决您的问题的答案。您的答案是所有答案中最好的,但我将扩展您绑定到的视图模型,因为前面的答案提到了使用隐藏代码+1从技术上讲,这是一个正确的答案,但我不会这么做。我建议不要使用代码隐藏。我没有否决投票,但我建议在您的视图模型中设置此选项。这不是正确的答案,因为用户可以单击ListView中的空白区域,如果当前有选定的项目,则将触发该操作,就像用户单击选定的项目一样。@Rogala代码隐藏本身没有问题。您的业务逻辑不应依赖于您的应用程序代码,您的应用程序代码也不应依赖于您的UI代码。如果您的业务逻辑与任何应用程序代码一起工作,并且您的应用程序代码与任何UI一起工作,那么您已经实现了MVVM。在不考虑上下文的情况下应用绝对的、人为的限制必然会限制代码质量。如果没有C#代码,某些UI行为是不可能的,调用ViewModel.SuperNeatCommand.Execute(param)与绑定到它完全相同。附加属性在这里工作得很好,但它并不总是最好的,因为YAGNI。@Daniel实际上,代码背后固有的错误是倾向于向其添加业务逻辑,而不是编写业务逻辑
public partial class MainWindow : Window
{
public MainWindowViewModel MyViewModel { get; set; }
public MainWindow()
{
InitializeComponent();
MyViewModel = new MainWindowViewModel();
this.DataContext = MyViewModel;
}
}
<Window x:Class="Basic_Binding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:Custom="clr-namespace:GalaSoft.MvvmLight;assembly=GalaSoft.MvvmLight"
Title="MainWindow" Height="350" Width="525">
<ListView
Grid.Row="0"
Grid.Column="0"
HorizontalContentAlignment="Stretch"
ItemsSource="{Binding ModelsCollection}"
ItemTemplate="{DynamicResource BasicModelDataTemplate}">
</ListView>
<DataTemplate x:Key="BasicModelDataTemplate">
<Grid>
<TextBlock Text="{Binding Text}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonUp">
<i:InvokeCommandAction
Command="{Binding DataContext.SelectItemRelayCommand,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ItemsControl}}}"
CommandParameter="{Binding Id}">
</i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBlock>
</Grid>
</DataTemplate>
private void listBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
try {
//Do your stuff here
listBox.SelectedItem = null;
listBox.SelectedIndex = -1;
} catch (Exception ex) {
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
<ListView Name="MyListView" ItemsSource={Binding MyItems}>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="ListViewItem_PreviewMouseLeftButtonDown" />
<EventSetter Event="PreviewMouseLeftButtonUp" Handler="ListViewItem_PreviewMouseLeftButtonUp" />
</Style>
</ListView.ItemContainerStyle>
private void ListViewItem_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
MyListView.SelectedItems.Clear();
ListViewItem item = sender as ListViewItem;
if (item != null)
{
item.IsSelected = true;
MyListView.SelectedItem = item;
}
}
private void ListViewItem_PreviewMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
ListViewItem item = sender as ListViewItem;
if (item != null && item.IsSelected)
{
// do stuff
}
}
<ListView x:Name="MyListView" MouseLeftButtonUp="MyListView_MouseLeftButtonUp">
<GridView>
<!-- Declare GridViewColumns. -->
</GridView>
</ListView.View>
private void MyListView_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
System.Windows.Controls.ListView list = (System.Windows.Controls.ListView)sender;
MyClass selectedObject = (MyClass)list.SelectedItem;
// Do stuff with the selectedObject.
}