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

如何通过ViewModel从ItemsControl的ItemsSource获取自定义UserControl?

从ItemsControl中获取生成的UserControl集合

问题

我有一个ItemsControl,其ItemsSource为ObservableCollection<ViewModels>,这些ViewModel对应的DataContext是一个UserControl。现在需要从ItemsControl中获取生成的UI UserControl集合。

相关代码如下:

绑定到ObservableCollection的ItemsControl

<ScrollViewer HorizontalScrollBarVisibility="Disabled"
                VerticalScrollBarVisibility="Auto"
                ScrollChanged="ScrollViewer_ScrollChanged"
                x:Name="scroll">
    <StackPanel x:Name="stackPanel">
        <ItemsControl ItemsSource="{Binding Interlocutor.Messages}"
                        x:Name="itemsControl"
                        VirtualizingPanel.ScrollUnit="Item"
                        VirtualizingPanel.IsVirtualizing="True">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <VirtualizingStackPanel />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate DataType="{x:Type viewmodels:MessageViewModel}">
                    <controls:MessageControl />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </StackPanel>
</ScrollViewer>

UserControl(MessageControl)的DataContext绑定

<UserControl x:Class="Client.Controls.MessageControl"
             ...
             xmlns:viewmodels="clr-namespace:Client.ViewModels"
             d:DataContext="{d:DesignInstance Type=viewmodels:MessageViewModel}"
             mc:Ignorable="d">

我尝试了两种方法,但均未成功,返回的是包含ViewModel的ContentPresenter对象,而非目标MessageControl:

方法一

foreach (MessageViewModel item in itemsControl.Items)
{
    MessageControl control = (MessageControl)itemsControl.ItemContainerGenerator.ContainerFromItem(item);
}

方法二

for (int i = 0; i < itemsControl.Items.Count; i++)
{
    MessageControl element = (MessageControl)itemsControl.ItemContainerGenerator.ContainerFromIndex(i);
}

解决方案

ItemContainerGenerator.ContainerFromItem/ContainerFromIndex返回的是容器元素(对于ItemsControl来说就是ContentPresenter),而DataTemplate里的MessageControl是这个容器的子元素,需要额外提取。

方法1:直接获取ContentPresenter的Content

由于你的DataTemplate直接将MessageControl作为ContentPresenter的内容,可直接读取Content属性转换:

foreach (MessageViewModel item in itemsControl.Items)
{
    var container = itemsControl.ItemContainerGenerator.ContainerFromItem(item) as ContentPresenter;
    if (container != null)
    {
        var messageControl = container.Content as MessageControl;
        if (messageControl != null)
        {
            // 对MessageControl进行后续操作
        }
    }
}

方法2:视觉树遍历查找(通用场景)

如果DataTemplate结构更复杂,MessageControl不是直接作为容器内容,可通过视觉树递归查找目标控件:

// 通用视觉树查找方法
public static 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 typedChild)
        {
            return typedChild;
        }
        var foundChild = FindVisualChild<T>(child);
        if (foundChild != null)
        {
            return foundChild;
        }
    }
    return null;
}

// 使用示例
foreach (MessageViewModel item in itemsControl.Items)
{
    var container = itemsControl.ItemContainerGenerator.ContainerFromItem(item) as ContentPresenter;
    if (container != null)
    {
        var messageControl = FindVisualChild<MessageControl>(container);
        if (messageControl != null)
        {
            // 对MessageControl进行后续操作
        }
    }
}

注意虚拟化的影响

你的ItemsControl开启了虚拟化(VirtualizingPanel.IsVirtualizing="True"),这意味着只有当前可见的项才会生成对应的UI元素。对于不可见的项,ItemContainerGenerator会返回null。如果需要获取所有项的UI控件,可选择:

  • 关闭虚拟化(将VirtualizingPanel.IsVirtualizing设为False),但会增加内存占用
  • 滚动到对应项强制生成容器,但会影响列表加载性能

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 00:43:25