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

UWP:添加ListViewItem时切换DataTemplate遇空引用问题

解决ListViewItem获取为null的问题

兄弟,这个坑我之前踩过!你遇到的问题核心原因很明确:当你往ObservableCollection里添加新项后,WPF并不会立刻生成对应的ListViewItem——UI的更新是异步调度的,你紧接着调用ContainerFromIndex的时候,容器还没被创建出来,自然返回null了。

下面给你两种靠谱的解决办法,第二种是WPF推荐的最佳实践,优先考虑:

方法1:用Dispatcher延迟执行,等UI渲染完再操作

通过Dispatcher把切换模板的代码放到UI线程的渲染队列里,给UI一点时间生成新项的容器:

CurrentTicket.Add(new Item { itemID = selectedItem.itemID, price = price, name = selectedItem.name, taxID = selectedItem.taxID, modName = modNames });
if(modNames.Count() != 0)
{
    // 把操作放到渲染优先级的队列,等UI完成新项渲染后再执行
    Application.Current.Dispatcher.BeginInvoke(
        DispatcherPriority.Render,
        new Action(() =>
        {
            ListViewItem lvi = ticketListBox.ContainerFromIndex(ticketListBox.Items.Count - 1) as ListViewItem;
            if(lvi != null)
            {
                lvi.ContentTemplate = (DataTemplate)this.Resources["CurrentTicketModDataTemplate"];
            }
        })
    );
}

这个方法能快速解决问题,但属于“治标”的方式,毕竟手动操作UI容器不是WPF的设计初衷。

方法2:用DataTemplateSelector(推荐,更优雅)

WPF的核心是数据驱动UI,所以最好的方式是让模板根据数据自动切换,不用手动找容器。你可以自定义一个DataTemplateSelector,根据Item的modName是否有内容来自动选模板:

第一步:写个简单的Selector类

public class TicketItemTemplateSelector : DataTemplateSelector
{
    // 这两个属性在XAML里绑定对应的模板
    public DataTemplate DefaultTemplate { get; set; }
    public DataTemplate ModTemplate { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        var ticketItem = item as Item;
        // 如果modName有内容,用带mod的模板,否则用默认模板
        if(ticketItem != null && ticketItem.modName?.Count > 0)
        {
            return ModTemplate;
        }
        return DefaultTemplate;
    }
}

第二步:在XAML里配置Selector和模板

<!-- 先在Window/UserControl的资源里定义模板和Selector -->
<Window.Resources>
    <!-- 默认模板 -->
    <DataTemplate x:Key="CurrentTicketDefaultDataTemplate">
        <!-- 这里放你原来的默认模板内容 -->
    </DataTemplate>
    <!-- 带mod的模板 -->
    <DataTemplate x:Key="CurrentTicketModDataTemplate">
        <!-- 这里放你原来的带mod的模板内容 -->
    </DataTemplate>
    <!-- 实例化我们的Selector,绑定两个模板 -->
    <local:TicketItemTemplateSelector 
        x:Key="TicketItemTemplateSelector"
        DefaultTemplate="{StaticResource CurrentTicketDefaultDataTemplate}"
        ModTemplate="{StaticResource CurrentTicketModDataTemplate}" />
</Window.Resources>

<!-- 给ListView指定这个Selector -->
<ListView x:Name="ticketListBox" 
          ItemsSource="{Binding CurrentTicket}"
          ItemTemplateSelector="{StaticResource TicketItemTemplateSelector}" />

第三步:简化后台代码

现在你只需要安心加数据就行,完全不用管UI模板的事了:

CurrentTicket.Add(new Item { itemID = selectedItem.itemID, price = price, name = selectedItem.name, taxID = selectedItem.taxID, modName = modNames });

这种方式完全符合WPF的MVVM思想,代码更简洁,也不会再出现UI同步的问题,长期维护起来更省心。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:47:42