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

.NET WinForms DataGridView单业务对象对应多行的数据绑定实现问询

嘿,这个需求完全可行!核心就是把单个PlannedProductGroup对象拆分成多个对应不同财务指标的子对象,让每个子对象对应DataGridView的一行,这样就能用数据绑定自动生成表格了。下面我给你一步步拆解实现方案:

核心思路:拆分业务对象为行级模型

DataGridView的数据绑定逻辑是「一行对应一个对象」,所以我们需要把原来一个产品组对应的15个指标行,转换成15个独立的行级对象。每个行级对象包含:产品组ID、指标类型、行头文本、52周的数据,以及行的只读/可见性配置。


步骤1:定义行级数据模型

先创建一个适配DataGridView行的模型类,用来承载单条指标行的所有信息:

Public Enum Measure
    ProductGroupId = 0
    RetailPrice = 1
    PriceMultiple = 2
    ' 补充剩下的12个枚举值
End Enum

Public Class PlanningRowModel
    ' 产品组ID(对应表格第一列)
    Public Property ProductGroupId As Integer
    ' 指标类型(用来区分是哪类财务指标行)
    Public Property MeasureType As Measure
    ' 行头显示文本
    Public Property HeaderText As String
    ' 52周的数据(用Object类型适配不同数据格式:文本、数值、下拉框值)
    Public Property WeeklyValues As Object()
    ' 行是否只读
    Public Property IsReadOnly As Boolean
    ' 行是否可见
    Public Property IsVisible As Boolean
End Class

步骤2:转换业务数据为行级模型集合

把从业务层获取的PlannedProductGroup列表,转换成PlanningRowModel的集合。这里完全复用你原来的业务逻辑,只是把手动创建行的逻辑改成创建行级对象:

' 从业务层获取产品组规划数据
Dim plannedGroups As List(Of PlannedProductGroup) = Load_Planning_Details_As_Business_Objects()
Dim planningRows As New List(Of PlanningRowModel)()

For Each group In plannedGroups
    If Product_Group_Checked(group.ID) Then
        ' 1. 生成「产品组名称」对应的行(对应原代码第一个手动创建的行)
        Dim productGroupRow = New PlanningRowModel() With {
            .ProductGroupId = group.ID,
            .MeasureType = Measure.ProductGroupId,
            .HeaderText = group.Name,
            .WeeklyValues = New Object(51){},
            .IsReadOnly = True,
            .IsVisible = True
        }
        ' 填充周数据(复用原代码的组合文本逻辑)
        For weekCounter As Integer = 0 To 51
            Dim priceMultipleId = group.GetWeeklyValue(Measure.PriceMultiple, weekCounter)
            Dim priceMultipleName = _priceMultiples.Find(Function(p) p.ID = priceMultipleId).Name
            Dim complexOffer = If(group.GetWeeklyValue(Measure.Complex, weekCounter) = 1, " *", "")
            Dim retailPrice = Math.Abs(group.GetWeeklyValue(Measure.RetailPrice, weekCounter)).ToString("C2") & complexOffer
            productGroupRow.WeeklyValues(weekCounter) = $"{priceMultipleName} {retailPrice}"
        Next
        planningRows.Add(productGroupRow)

        ' 2. 生成「零售价」对应的行
        Dim retailPriceRow = New PlanningRowModel() With {
            .ProductGroupId = group.ID,
            .MeasureType = Measure.RetailPrice,
            .HeaderText = " Retail Price",
            .WeeklyValues = New Object(51){},
            .IsReadOnly = _isReadOnly,
            .IsVisible = ToolStripCheckBox_Retail.Checked
        }
        For weekCounter As Integer = 0 To 51
            retailPriceRow.WeeklyValues(weekCounter) = group.GetWeeklyValue(Measure.RetailPrice, weekCounter)
        Next
        planningRows.Add(retailPriceRow)

        ' 3. 生成「价格倍数」对应的行
        Dim priceMultipleRow = New PlanningRowModel() With {
            .ProductGroupId = group.ID,
            .MeasureType = Measure.PriceMultiple,
            .HeaderText = " Price Multiple",
            .WeeklyValues = New Object(51){},
            .IsReadOnly = _isReadOnly,
            .IsVisible = ToolStripCheckBox_Retail.Checked
        }
        For weekCounter As Integer = 0 To 51
            priceMultipleRow.WeeklyValues(weekCounter) = group.GetWeeklyValue(Measure.PriceMultiple, weekCounter)
        Next
        planningRows.Add(priceMultipleRow)

        ' ... 剩下的12个指标行,按照同样的逻辑生成即可
    End If
Next

注:这里假设PlannedProductGroup有GetWeeklyValue(measure As Measure, week As Integer)方法来获取对应指标的周数据,你可以根据实际业务层结构调整这个逻辑。


步骤3:配置DataGridView数据绑定

用BindingSource包装行级模型集合,然后绑定到DataGridView:

Dim bindingSource As New BindingSource()
bindingSource.DataSource = planningRows
_dgvPlanningDetails.DataSource = bindingSource

' 手动添加列:第一列是产品组ID,然后添加52周的列
_dgvPlanningDetails.Columns.Clear()
' 添加产品组ID列
Dim productGroupIdCol As New DataGridViewTextBoxColumn()
productGroupIdCol.DataPropertyName = "ProductGroupId"
productGroupIdCol.HeaderText = "Product Group ID"
_dgvPlanningDetails.Columns.Add(productGroupIdCol)
' 添加52周的列
For week As Integer = 1 To 52
    Dim weekCol As New DataGridViewTextBoxColumn()
    weekCol.HeaderText = $"Week {week}"
    weekCol.DataPropertyName = $"WeeklyValues[{week-1}]" ' 绑定到数组的对应索引
    _dgvPlanningDetails.Columns.Add(weekCol)
Next

步骤4:处理UI细节(行头、样式、单元格类型)

原来手动设置的行头、单元格样式、下拉框类型,现在可以通过DataGridView的事件来处理:

1. 设置行头文本

Private Sub _dgvPlanningDetails_RowPostPaint(sender As Object, e As DataGridViewRowPostPaintEventArgs) Handles _dgvPlanningDetails.RowPostPaint
    Dim rowModel = TryCast(_dgvPlanningDetails.Rows(e.RowIndex).DataBoundItem, PlanningRowModel)
    If rowModel IsNot Nothing Then
        _dgvPlanningDetails.Rows(e.RowIndex).HeaderCell.Value = rowModel.HeaderText
        ' 同步行的可见性和只读属性
        _dgvPlanningDetails.Rows(e.RowIndex).Visible = rowModel.IsVisible
        _dgvPlanningDetails.Rows(e.RowIndex).ReadOnly = rowModel.IsReadOnly
    End If
End Sub

2. 设置单元格样式(黑周背景、格式)

Private Sub _dgvPlanningDetails_CellFormatting(sender As Object, e As DataGridViewCellFormattingEventArgs) Handles _dgvPlanningDetails.CellFormatting
    ' 只处理周列(索引1到52)
    If e.ColumnIndex >= 1 And e.ColumnIndex <= 52 Then
        Dim weekNumber = e.ColumnIndex ' 对应原代码的weekCounter+1
        ' 设置黑周背景色
        If _blackoutCollection.Contains(weekNumber.ToString()) Then
            e.CellStyle.BackColor = Color.LightSlateGray
        Else
            e.CellStyle.BackColor = Color.LightGray
        End If

        Dim rowModel = TryCast(_dgvPlanningDetails.Rows(e.RowIndex).DataBoundItem, PlanningRowModel)
        If rowModel Is Nothing Then Return

        ' 零售价行设置货币格式
        If rowModel.MeasureType = Measure.RetailPrice Then
            e.CellStyle.Format = "C2"
        End If

        ' 价格倍数行替换为下拉框单元格
        If rowModel.MeasureType = Measure.PriceMultiple Then
            Dim comboCell = New DataGridViewComboBoxCell()
            comboCell.DataSource = _priceMultiples.AsEnumerable()
            comboCell.ValueMember = "ID"
            comboCell.DisplayMember = "Name"
            comboCell.Value = e.Value
            _dgvPlanningDetails.Rows(e.RowIndex).Cells(e.ColumnIndex) = comboCell
        End If
    End If
End Sub

对比原代码的优势
  • 代码更清晰:业务数据转换和UI渲染分离,后续修改指标或周数只需调整模型和转换逻辑,不用动UI代码
  • 可维护性更高:避免手动循环创建行的重复代码,减少出错概率
  • 原生数据绑定支持:可以利用BindingSource的排序、过滤功能,后续扩展更方便

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 03:59:46