如何让ListView ItemTemplate内按钮点击触发SelectionChanged事件
Button本身是可交互控件,会吞掉点击的冒泡路由事件(将事件的Handled标记设为true),点击动作不会向上传递到外层的ListViewItem,因此ListView无法收到选中触发信号。只有点击Item内的非交互区域时,事件才能正常冒泡到列表项层级,触发SelectionChanged事件。
可根据自身代码场景选择以下任意一种实现,都能达到「点击按钮先选中对应列表项、再执行按钮自身逻辑」的预期效果:
方案1:按钮点击时手动赋值选中项(适配当前代码结构,改动最小)
你已经实现了按钮的后台Click事件,只需要在执行按钮自身业务逻辑前,先把当前按钮绑定的数据项赋值给ListView的SelectedItem即可,赋值完成后会自动触发SelectionChanged事件,执行顺序完全符合需求:
// 正面PDF预览按钮点击逻辑 private void btn_preview_front_pdf(object sender, RoutedEventArgs e) { // 取当前按钮绑定的对应数据实体,替换成你自己的PDF实体类名即可 var currentPdfItem = (sender as FrameworkElement)?.DataContext as PdfModel; // 手动触发选中,这一步执行完会立刻执行SelectionChanged对应的处理逻辑 lv_pdfs.SelectedItem = currentPdfItem; // 下方保留原有的正面PDF预览业务代码即可 // ... } // 背面PDF预览按钮点击逻辑 private void btn_preview_back_pdf(object sender, RoutedEventArgs e) { var currentPdfItem = (sender as FrameworkElement)?.DataContext as PdfModel; lv_pdfs.SelectedItem = currentPdfItem; // 下方保留原有的背面PDF预览业务代码即可 // ... }
小提示:如果点击的是已经处于选中状态的项,重复给
SelectedItem赋相同值不会重复触发SelectionChanged,和原生点击空白区域选中的交互逻辑一致。
方案2:全局样式统一处理(适合Item内存在大量可交互控件的场景,无需逐个写逻辑)
如果ListView内除了这两个按钮,还有复选框、超链接等其他会拦截点击事件的控件,不想给每个控件单独写选中逻辑,可以给ListViewItem加统一的点击选中规则:只要点击区域属于该Item,不管点到内部什么控件都先自动选中。
首先在项目中添加简单的附加属性实现:
public static class ListViewItemEx { public static readonly DependencyProperty AutoSelectWhenClickProperty = DependencyProperty.RegisterAttached("AutoSelectWhenClick", typeof(bool), typeof(ListViewItemEx), new PropertyMetadata(false, (d, e) => { if (d is not ListViewItem item) return; if ((bool)e.NewValue) { // 监听鼠标按下事件,即使事件被内部控件标记为已处理也能正常捕获 item.AddHandler(UIElement.PointerPressedEvent, new PointerEventHandler((s, args) => item.IsSelected = true), handledEventsToo: true); } })); public static void SetAutoSelectWhenClick(DependencyObject element, bool value) => element.SetValue(AutoSelectWhenClickProperty, value); public static bool GetAutoSelectWhenClick(DependencyObject element) => (bool)element.GetValue(AutoSelectWhenClickProperty); }
之后只需要在ListView的ItemContainerStyle中开启这个属性即可,原有的ItemTemplate和按钮逻辑完全不需要修改:
<ListView x:Name="lv_pdfs" ItemsSource="{x:Bind ViewModel.PDFsOC, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedItem="{x:Bind ViewModel.SelectedPDF, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectionChanged="{x:Bind ViewModel.lv_pdfs_SelectionChanged}"> <ListView.ItemContainerStyle> <Style TargetType="ListViewItem"> <Setter Property="local:ListViewItemEx.AutoSelectWhenClick" Value="True"/> </Style> </ListView.ItemContainerStyle> <ListView.ItemTemplate> <DataTemplate> <StackPanel > <Button Click="btn_preview_front_pdf" Content="Preview Front PDF" /> <Button Margin="10 0 0 0" Click="btn_preview_back_pdf" Content="Preview Back PDF" IsEnabled="{Binding HasBack, UpdateSourceTrigger=PropertyChanged}" /> </StackPanel> </DataTemplate> </ListView.ItemTemplate> </ListView>
配置完成后,点击Item内的任何可交互控件,都会先自动选中当前项,再执行控件自身的逻辑,不需要给每个控件单独写选中赋值代码。
内容的提问来源于stack exchange,提问作者tinmac

