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

WPF+MVVM实现DataGrid相同内容单元格合并方法

嘿,针对你的职业培训规划项目需求——在MVVM模式下实现DataGrid相同内容单元格合并(比如学员连续几周修读同一课程时合并课程单元格),同时支持DIN A3/A2规格打印,我给你整理了一套落地性强的方案:

实现MVVM模式下DataGrid单元格合并+打印的完整方案

1. 先搞定ViewModel层的合并逻辑(核心)

MVVM模式要求业务逻辑尽量放在ViewModel层,所以我们需要先处理原始的每周课程数据,生成带有合并判断属性的集合,避免在View层写业务代码:

1.1 定义基础模型与合并用模型

// 原始每周课程模型
public class WeeklyCourse : ObservableObject
{
    public int WeekNumber { get; set; }
    public string CourseName { get; set; }
    // 其他业务属性:讲师、课时、教室等
}

// 用于单元格合并的包装模型
public class CourseMergeItem : ObservableObject
{
    public int WeekNumber { get; set; }
    public string CourseName { get; set; }
    public int RowSpan { get; set; } // 单元格需要跨多少行合并
    public bool IsCourseVisible { get; set; } // 标记当前行是否显示课程名(仅合并组的第一行显示)
}

1.2 在ViewModel中处理数据合并

public class TrainingPlanViewModel : ObservableObject
{
    public ObservableCollection<CourseMergeItem> MergedCourseList { get; set; }

    public TrainingPlanViewModel()
    {
        // 假设从数据库/接口加载原始每周课程数据
        var originalWeeklyCourses = LoadWeeklyTrainingData();
        MergedCourseList = GenerateMergedCourseItems(originalWeeklyCourses);
    }

    private ObservableCollection<CourseMergeItem> GenerateMergedCourseItems(List<WeeklyCourse> originalData)
    {
        var mergedItems = new ObservableCollection<CourseMergeItem>();
        if (originalData == null || !originalData.Any()) return mergedItems;

        // 遍历原始数据,分组连续相同的课程
        var currentCourse = originalData[0];
        int currentRowSpan = 1;

        for (int i = 1; i < originalData.Count; i++)
        {
            var nextCourse = originalData[i];
            if (nextCourse.CourseName == currentCourse.CourseName)
            {
                currentRowSpan++;
            }
            else
            {
                // 添加合并组的第一行
                mergedItems.Add(new CourseMergeItem
                {
                    WeekNumber = currentCourse.WeekNumber,
                    CourseName = currentCourse.CourseName,
                    RowSpan = currentRowSpan,
                    IsCourseVisible = true
                });
                // 添加合并组的后续行(隐藏课程名,RowSpan为1)
                for (int j = 1; j < currentRowSpan; j++)
                {
                    mergedItems.Add(new CourseMergeItem
                    {
                        WeekNumber = currentCourse.WeekNumber + j,
                        CourseName = currentCourse.CourseName,
                        RowSpan = 1,
                        IsCourseVisible = false
                    });
                }
                // 重置当前课程和跨度
                currentCourse = nextCourse;
                currentRowSpan = 1;
            }
        }

        // 添加最后一组数据
        mergedItems.Add(new CourseMergeItem
        {
            WeekNumber = currentCourse.WeekNumber,
            CourseName = currentCourse.CourseName,
            RowSpan = currentRowSpan,
            IsCourseVisible = true
        });
        for (int j = 1; j < currentRowSpan; j++)
        {
            mergedItems.Add(new CourseMergeItem
            {
                WeekNumber = currentCourse.WeekNumber + j,
                CourseName = currentCourse.CourseName,
                RowSpan = 1,
                IsCourseVisible = false
            });
        }

        return mergedItems;
    }
}

2. View层实现单元格合并渲染

在XAML中,通过绑定ViewModel的RowSpan和IsCourseVisible属性,结合DataGrid的样式实现单元格合并:

<Window.Resources>
    <BooleanToVisibilityConverter x:Key="BoolToVis" />
</Window.Resources>

<DataGrid ItemsSource="{Binding MergedCourseList}"
          AutoGenerateColumns="False"
          CanUserAddRows="False"
          CanUserDeleteRows="False"
          RowHeaderWidth="0">
    <!-- 周次列 -->
    <DataGridTextColumn Header="周次" Binding="{Binding WeekNumber}" Width="*" />
    <!-- 课程列(核心合并逻辑) -->
    <DataGridTemplateColumn Header="课程名称" Width="2*">
        <DataGridTemplateColumn.CellTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding CourseName}"
                           Visibility="{Binding IsCourseVisible, Converter={StaticResource BoolToVis}}"
                           VerticalAlignment="Center" />
            </DataTemplate>
        </DataGridTemplateColumn.CellTemplate>
        <DataGridTemplateColumn.CellStyle>
            <Style TargetType="DataGridCell">
                <!-- 设置单元格跨行数 -->
                <Setter Property="Grid.RowSpan" Value="{Binding RowSpan}" />
                <!-- 隐藏重复行的单元格边框,避免视觉割裂 -->
                <Style.Triggers>
                    <DataTrigger Binding="{Binding IsCourseVisible}" Value="False">
                        <Setter Property="BorderThickness" Value="0" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </DataGridTemplateColumn.CellStyle>
    </DataGridTemplateColumn>
    <!-- 其他业务列:讲师、课时等 -->
</DataGrid>

3. 适配DIN A3/A2规格的打印

你已经实现了DataGrid打印功能,只需要调整打印参数适配大尺寸纸张:

3.1 配置打印纸张尺寸

private void PrintTrainingPlan(DataGrid targetGrid)
{
    var printDialog = new PrintDialog();
    // 设置为DIN A3(毫米转百分之一英寸:297mm≈1169,420mm≈1654)
    var a3Size = new PageMediaSize(PageMediaSizeName.ISOC, 1169, 1654);
    // 如果需要A2,替换为:PageMediaSizeName.ISOB,尺寸594mm≈2339,420mm≈1654
    printDialog.PrintTicket.PageMediaSize = a3Size;

    if (printDialog.ShowDialog() == true)
    {
        // 打印前强制更新布局,确保合并单元格渲染正确
        targetGrid.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
        targetGrid.Arrange(new Rect(0, 0, targetGrid.DesiredSize.Width, targetGrid.DesiredSize.Height));
        // 执行打印
        printDialog.PrintVisual(targetGrid, "职业培训规划表");
    }
}

3.2 打印优化建议

  • 如果DataGrid内容过长,可以添加分页逻辑,或者设置打印缩放比例适配大纸张
  • 测试不同打印机的大尺寸纸张支持情况,调整边距参数

内容的提问来源于stack exchange,提问作者Björn Steinbrink

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:02:55