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

基于ListView选中状态动态控制控件可见性的实现

实现多选ListView选中项显示/隐藏TimePicker面板

我有一个支持多选的ListView,用于展示多名人员信息。需求如下:

  • 选中某人员时,显示其对应的包含TimePicker控件的StackPanel
  • 取消选中该人员时,隐藏该StackPanel

当前代码

XAML

<ContentDialog.Resources>
<DataTemplate x:Key="ContactListViewTemplate" x:DataType="model:Client">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Ellipse x:Name="Ellipse"
            Grid.RowSpan="2"
            Width ="32"
            Height="32"
            Margin="6"
            VerticalAlignment="Center"
            HorizontalAlignment="Center"
            Fill="{ThemeResource SystemControlBackgroundBaseMediumBrush}"/>
        <TextBlock Grid.Column="1"
            Text="{x:Bind FirstName}"
            x:Phase="1"
            Style="{ThemeResource BaseTextBlockStyle}"
            Margin="12,6,0,0"/>
        <TextBlock  Grid.Column="1"
            Grid.Row="1"
            Text="{x:Bind LastName}"
            x:Phase="2"
            Style="{ThemeResource BodyTextBlockStyle}"
            Margin="12,0,0,6"/>
        <StackPanel Orientation="Horizontal" x:Name="timePanel" Grid.Column="2" Grid.RowSpan="2" Visibility="Collapsed">
            <TimePicker Header="Start"></TimePicker>
            <TimePicker Header="End"></TimePicker>
        </StackPanel>
    </Grid>
</DataTemplate>
</ContentDialog.Resources>

<ListView x:Name="clientsList" SelectionMode="Multiple" ItemTemplate="{StaticResource ContactListViewTemplate}" SelectionChanged="ClientsList_SelectionChanged" />

Code Behind

private void ClientsList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    Debug.WriteLine("called mett " + e.AddedItems.Count);
    var listView= sender as ListView;
    if(e.RemovedItems.Count > 0)
    {
        
    }
    if(e.AddedItems.Count > 0)
    {
        foreach(var item in e.AddedItems)
        {
           ListViewItem myListViewItem = (ListViewItem)item;
           ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(myListViewItem);
            StackPanel stackPanel = myContentPresenter.ContentTemplate.FindDescendant("timePanel") as StackPanel;
        }
    }
}

解决方案

方法一:修正Code Behind实现

原代码核心错误是将AddedItems中的数据模型(Client对象)直接转换为ListViewItem,这是不正确的。需要通过ListView的ContainerFromItem方法获取对应项的容器,再查找目标面板。

首先补充视觉树查找方法:

private T FindVisualChild<T>(DependencyObject parent) where T : DependencyObject
{
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
    {
        var child = VisualTreeHelper.GetChild(parent, i);
        if (child is T tChild)
            return tChild;
        else
        {
            var result = FindVisualChild<T>(child);
            if (result != null)
                return result;
        }
    }
    return null;
}

private DependencyObject FindDescendant(this DataTemplate template, string name)
{
    var frameworkElement = template.LoadContent() as FrameworkElement;
    return frameworkElement?.FindName(name);
}

然后修改SelectionChanged事件处理逻辑:

private void ClientsList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    var listView = sender as ListView;
    if (listView == null) return;

    // 处理选中项,显示面板
    foreach (var client in e.AddedItems.OfType<model:Client>())
    {
        var listViewItem = listView.ContainerFromItem(client) as ListViewItem;
        if (listViewItem != null)
        {
            var contentPresenter = FindVisualChild<ContentPresenter>(listViewItem);
            if (contentPresenter != null && contentPresenter.ContentTemplate != null)
            {
                var timePanel = contentPresenter.ContentTemplate.FindDescendant("timePanel") as StackPanel;
                timePanel?.SetValue(VisibilityProperty, Visibility.Visible);
            }
        }
    }

    // 处理取消选中项,隐藏面板
    foreach (var client in e.RemovedItems.OfType<model:Client>())
    {
        var listViewItem = listView.ContainerFromItem(client) as ListViewItem;
        if (listViewItem != null)
        {
            var contentPresenter = FindVisualChild<ContentPresenter>(listViewItem);
            if (contentPresenter != null && contentPresenter.ContentTemplate != null)
            {
                var timePanel = contentPresenter.ContentTemplate.FindDescendant("timePanel") as StackPanel;
                timePanel?.SetValue(VisibilityProperty, Visibility.Collapsed);
            }
        }
    }
}

方法二:使用数据绑定(推荐)

这种方式符合MVVM设计模式,无需直接操作视觉元素,维护性更强。

  1. 给Client模型添加IsSelected属性(需实现INotifyPropertyChanged接口):
public class Client : INotifyPropertyChanged
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    private bool _isSelected;
    public bool IsSelected
    {
        get => _isSelected;
        set
        {
            if (_isSelected != value)
            {
                _isSelected = value;
                OnPropertyChanged(nameof(IsSelected));
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
  1. 在XAML中添加转换资源并绑定面板可见性:
<ContentDialog.Resources>
    <BooleanToVisibilityConverter x:Key="BoolToVis"/>
    <DataTemplate x:Key="ContactListViewTemplate" x:DataType="model:Client">
        <!-- 保留原Grid布局内容 -->
        <StackPanel Orientation="Horizontal" x:Name="timePanel" Grid.Column="2" Grid.RowSpan="2" 
                    Visibility="{x:Bind IsSelected, Converter={StaticResource BoolToVis}}">
            <TimePicker Header="Start"></TimePicker>
            <TimePicker Header="End"></TimePicker>
        </StackPanel>
    </DataTemplate>
</ContentDialog.Resources>
  1. 同步选中状态:
private void ClientsList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    foreach (var client in e.AddedItems.OfType<model:Client>())
    {
        client.IsSelected = true;
    }
    foreach (var client in e.RemovedItems.OfType<model:Client>())
    {
        client.IsSelected = false;
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 19:30:01