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

C#中含交互元素的动态复杂网格高效渲染方案咨询

优化WPF生产调度日历渲染性能的实战建议

从你的描述和代码来看,核心问题出在手动通过后台代码创建并添加大量UserControl实例到Grid——这种方式完全违背了WPF数据驱动UI的设计原则,也是导致20行数据渲染耗时3秒的根本原因。下面是针对性的优化方案,结合WPF的虚拟化、数据绑定特性来彻底解决性能瓶颈:


1. 立刻抛弃"后台生成UI元素"的思路,切换为数据驱动模式

WPF的强项是通过ViewModel + 数据绑定渲染UI,而非手动创建控件。你需要把"列"(任务头列、日期列)抽象成数据模型(ViewModel),让WPF自动完成UI渲染:

步骤1:定义列的ViewModel基类与子类

// 基类实现INotifyPropertyChanged,支持数据绑定更新
public abstract class ColumnViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propName)
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}

// 任务头列的ViewModel
public class JobHeaderColumnVm : ColumnViewModel
{
    public Job Job { get; set; }
    // 把原来的事件转换为命令(符合MVVM),或者保留事件绑定
    public event Action JobUpdated;
    public event Action JobDeleted;
}

// 日期列的ViewModel
public class DateColumnVm : ColumnViewModel
{
    public DateTime Date { get; set; }
    public Job Job { get; set; }
    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }
    public event Action PhaseChanged;
}

步骤2:重构RenderFull为数据填充逻辑

不再创建UserControl,而是填充ViewModel集合:

// 在你的ViewModel中维护列集合(用ObservableCollection支持自动更新UI)
public ObservableCollection<ColumnViewModel> Columns { get; } = new();

private void RenderFull()
{
    Columns.Clear();
    
    // 添加任务头列
    var headerVm = new JobHeaderColumnVm { Job = job };
    headerVm.JobUpdated += ColumnJob_JobUpdated;
    headerVm.JobDeleted += ColumnJob_JobDeleted;
    Columns.Add(headerVm);
    
    // 添加日期列
    DateTime processedDate = startDate;
    while (processedDate <= endDate)
    {
        var dateVm = new DateColumnVm
        {
            Job = job,
            Date = processedDate,
            StartDate = startDate,
            EndDate = endDate
        };
        dateVm.PhaseChanged += Handler_PhaseChanged;
        Columns.Add(dateVm);
        processedDate = processedDate.AddDays(1);
    }
}

2. 用虚拟化控件渲染列,开启UI元素复用

用ItemsControl结合VirtualizingStackPanel替代手动操作Grid,WPF会自动只渲染可见区域的UI元素,并回收复用不可见元素,这能把渲染耗时降到毫秒级:

XAML实现(绑定ViewModel的Columns集合)

<!-- 外层容器:用ItemsControl渲染所有列,开启水平虚拟化 -->
<ItemsControl ItemsSource="{Binding Columns}"
              VirtualizingStackPanel.IsVirtualizing="True"
              VirtualizingStackPanel.VirtualizationMode="Recycling">
    <!-- 设置水平布局的虚拟化面板 -->
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel Orientation="Horizontal"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <!-- 用DataTemplate根据ViewModel类型自动渲染对应的UI -->
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <ContentControl Content="{Binding}">
                <ContentControl.Resources>
                    <!-- 任务头列的UI模板 -->
                    <DataTemplate DataType="{x:Type local:JobHeaderColumnVm}">
                        <Grid Width="200">
                            <!-- 这里直接写原来ColumnJobHeaderClassic的UI内容,绑定Vm的属性 -->
                            <TextBlock Text="{Binding Job.Name}"/>
                            <!-- 操作按钮绑定命令或事件 -->
                            <Button Content="修改" Click="JobHeader_UpdateClick"/>
                        </Grid>
                    </DataTemplate>
                    <!-- 日期列的UI模板 -->
                    <DataTemplate DataType="{x:Type local:DateColumnVm}">
                        <Grid>
                            <!-- 原来ColumnJobDay的UI内容,绑定Vm的Date、Job等属性 -->
                            <TextBlock Text="{Binding Date, StringFormat='yyyy-MM-dd'}"/>
                            <!-- 生产阶段拖放逻辑:用WPF原生DragDrop API绑定Vm -->
                            <Border AllowDrop="True" DragEnter="Phase_DragEnter" Drop="Phase_Drop">
                                <TextBlock Text="{Binding Job.CurrentPhase}"/>
                            </Border>
                        </Grid>
                    </DataTemplate>
                </ContentControl.Resources>
            </ContentControl>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

3. 针对行动态生成的补充优化

你的行数是根据项目数动态生成的,同样用数据绑定处理:把每个生产项目抽象成JobItemViewModel,放到ObservableCollection<JobItemViewModel>中,然后用虚拟化的ListView/DataGrid渲染每行——这样即使25行数据,也能瞬间完成渲染,因为DataGrid默认开启了虚拟化。

4. 额外性能优化点

  • 批量UI操作时,先将容器的Visibility设为Collapsed,操作完成后再设回Visible,减少WPF的布局计算次数。
  • 拖放逻辑尽量在ViewModel中实现(用命令或Attached Property),避免在UI控件中写大量事件处理代码。
  • 避免在RenderFull中频繁清空再重建集合,可以考虑增量更新(比如只添加/删除变化的列)。

这种设计不仅能彻底解决渲染性能问题,还能让你更轻松地绑定数据库实体(ViewModel可以直接映射EF实体,或用AutoMapper做转换),同时UI和业务逻辑分离,代码可维护性也会大幅提升。

内容的提问来源于stack exchange,提问作者Karel Křesťan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 05:37:42