You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WinUI 3中点击DataTemplate内控件时选中对应ListViewItem

WinUI 3 实现点击子控件时选中对应的 ListViewItem

在WinUI 3中,ToggleSwitch、Expander这类交互式控件会默认拦截点击事件,导致父级ListViewItem无法触发选中状态。可以通过以下两种方法解决这个问题:

方法一:直接绑定指针按下事件

1. XAML 代码

给需要触发选中的子控件添加PointerPressed事件:

<ListView x:Name="TargetListView">
    <ListView.ItemTemplate>
        <DataTemplate>
            <StackPanel Margin="8">
                <ToggleSwitch 
                    Header="状态开关" 
                    PointerPressed="InteractiveControl_PointerPressed"/>
                <Expander 
                    Header="详情展开" 
                    PointerPressed="InteractiveControl_PointerPressed">
                    <TextBlock Text="展开后的详细内容" Margin="16,8"/>
                </Expander>
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

2. 后台代码实现

在代码中统一处理事件,找到父级ListViewItem并设置选中:

private void InteractiveControl_PointerPressed(object sender, PointerRoutedEventArgs e)
{
    // 从触发控件向上遍历,找到对应的ListViewItem
    var currentElement = sender as UIElement;
    while (currentElement != null && currentElement is not ListViewItem)
    {
        currentElement = VisualTreeHelper.GetParent(currentElement) as UIElement;
    }

    if (currentElement is ListViewItem targetItem)
    {
        // 设置ListViewItem选中
        targetItem.IsSelected = true;
        // 同步ListView的SelectedItem,确保数据源选中状态一致
        TargetListView.SelectedItem = targetItem.DataContext;
    }
}

方法二:使用附加属性(复用性更强)

如果多个页面或控件都需要这个功能,推荐用附加属性封装逻辑,避免重复代码:

1. 创建附加属性类

using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;

namespace YourAppNamespace
{
    public static class ListViewItemSelector
    {
        public static readonly DependencyProperty SelectParentOnPointerPressedProperty =
            DependencyProperty.RegisterAttached(
                "SelectParentOnPointerPressed",
                typeof(bool),
                typeof(ListViewItemSelector),
                new PropertyMetadata(false, OnPropertyValueChanged));

        public static bool GetSelectParentOnPointerPressed(DependencyObject obj)
        {
            return (bool)obj.GetValue(SelectParentOnPointerPressedProperty);
        }

        public static void SetSelectParentOnPointerPressed(DependencyObject obj, bool value)
        {
            obj.SetValue(SelectParentOnPointerPressedProperty, value);
        }

        private static void OnPropertyValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is UIElement element)
            {
                if ((bool)e.NewValue)
                {
                    element.PointerPressed += OnElementPointerPressed;
                }
                else
                {
                    element.PointerPressed -= OnElementPointerPressed;
                }
            }
        }

        private static void OnElementPointerPressed(object sender, PointerRoutedEventArgs e)
        {
            var currentElement = sender as UIElement;
            // 找到父级ListViewItem
            while (currentElement != null && currentElement is not ListViewItem)
            {
                currentElement = VisualTreeHelper.GetParent(currentElement) as UIElement;
            }

            if (currentElement is ListViewItem targetItem)
            {
                targetItem.IsSelected = true;
                // 找到所属的ListView并同步选中项
                var parentListView = FindParent<ListView>(targetItem);
                parentListView?.SetValue(ListView.SelectedItemProperty, targetItem.DataContext);
            }
        }

        // 通用父级控件查找方法
        private static T FindParent<T>(DependencyObject child) where T : DependencyObject
        {
            var parent = VisualTreeHelper.GetParent(child);
            if (parent == null || parent is T)
            {
                return parent as T;
            }
            return FindParent<T>(parent);
        }
    }
}

2. XAML 中使用附加属性

先引入命名空间,再给子控件添加附加属性:

xmlns:local="using:YourAppNamespace"

<ListView x:Name="TargetListView">
    <ListView.ItemTemplate>
        <DataTemplate>
            <StackPanel Margin="8">
                <ToggleSwitch 
                    Header="状态开关" 
                    local:ListViewItemSelector.SelectParentOnPointerPressed="True"/>
                <Expander 
                    Header="详情展开" 
                    local:ListViewItemSelector.SelectParentOnPointerPressed="True">
                    <TextBlock Text="展开后的详细内容" Margin="16,8"/>
                </Expander>
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

说明:这两种方法都不会破坏ToggleSwitch和Expander本身的交互功能,点击控件时既会触发自身的逻辑(比如开关切换、展开折叠),同时也会选中对应的ListViewItem。

内容的提问来源于stack exchange,提问作者Effektor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 23:55:24