WinForms DataGridView同一列如何实现货币与百分比双格式?
WinForms DataGridView同一列根据字段值设置不同显示格式
我在WinForms项目的DataGridView中有一个名为Discount Value的列,需要根据Discount Type字段的值,分别以印尼货币(Rp格式)和百分比格式显示数据。请问能否在DataGridView的同一列中设置不同的显示格式?
我尝试了以下代码,但输出未达到预期效果,问题出在CtechGroupedDataGridView_CellFormatting事件处理方法中:
namespace WinFormsApp1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); List<RegulerDiscountModel> regulerDiscountModelList = new List<RegulerDiscountModel>(); RegulerDiscountModel regulerDiscountModel1 = new RegulerDiscountModel(); regulerDiscountModel1.RegulerDiscountID = 1; regulerDiscountModel1.RegulerDiscountProductId = 1; regulerDiscountModel1.RegulerDiscountProductName = "Product 1"; regulerDiscountModel1.RegulerDiscountName = "Disc1"; regulerDiscountModel1.RegulerDiscountValue = 1000; regulerDiscountModel1.RegulerDiscountStrTypeId = "Rp"; regulerDiscountModelList.Add(regulerDiscountModel1); RegulerDiscountModel regulerDiscountModel2 = new RegulerDiscountModel(); regulerDiscountModel2.RegulerDiscountID = 1; regulerDiscountModel2.RegulerDiscountProductId = 1; regulerDiscountModel2.RegulerDiscountProductName = "Product 1"; regulerDiscountModel2.RegulerDiscountName = "Disc2"; regulerDiscountModel2.RegulerDiscountValue = 2000; regulerDiscountModel2.RegulerDiscountStrTypeId = "Rp"; regulerDiscountModelList.Add(regulerDiscountModel2); RegulerDiscountModel regulerDiscountModel3 = new RegulerDiscountModel(); regulerDiscountModel3.RegulerDiscountID = 1; regulerDiscountModel3.RegulerDiscountProductId = 1; regulerDiscountModel3.RegulerDiscountProductName = "Product 1"; regulerDiscountModel3.RegulerDiscountName = "Disc3"; regulerDiscountModel3.RegulerDiscountValue = 10.5; regulerDiscountModel3.RegulerDiscountStrTypeId = "%"; regulerDiscountModelList.Add(regulerDiscountModel3); RegulerDiscountModel regulerDiscountModel4 = new RegulerDiscountModel(); regulerDiscountModel4.RegulerDiscountID = 1; regulerDiscountModel4.RegulerDiscountProductId = 1; regulerDiscountModel4.RegulerDiscountProductName = "Product 1"; regulerDiscountModel4.RegulerDiscountName = "Disc4"; regulerDiscountModel4.RegulerDiscountValue = 20.1; regulerDiscountModel4.RegulerDiscountStrTypeId = "%"; regulerDiscountModelList.Add(regulerDiscountModel4); CtechGroupedDataGridView.DataSource = regulerDiscountModelList; CtechGroupedDataGridView.CellFormatting += CtechGroupedDataGridView_CellFormatting; } private void CtechGroupedDataGridView_CellFormatting(object? sender, DataGridViewCellFormattingEventArgs e) { try { if (e.RowIndex >= 0 && e.RowIndex <= CtechGroupedDataGridView.Rows.Count - 1) { DataGridViewRow row = CtechGroupedDataGridView.CurrentRow; if (row != null) { // ID ProductId ProductName DiscountName DiscountValue DiscountType // 0 1 2 3 4 5 if (row.Cells[4].Value != null && row.Cells[5].Value != null) { String? strDiscountValue = row.Cells[4].Value.ToString(); String? strDiscountType = row.Cells[5].Value.ToString(); if (!String.IsNullOrEmpty(strDiscountValue) && !String.IsNullOrEmpty(strDiscountType)) { switch (strDiscountType) { case "Rp": CtechGroupedDataGridView[4, e.RowIndex].Style.Format = "N0"; break; case "%": CtechGroupedDataGridView[4, e.RowIndex].Style.Format = "P1"; break; } } } } } } catch (Exception ex) { MessageBox.Show(ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } }
效果说明
- 当前输出效果:所有Discount Value列显示原始数字,未应用任何格式(如1000、2000、10.5、20.1)
- 点击DataGridView后效果:部分单元格格式错乱,百分比类型数值未正确处理
- 期望输出效果:
- Discount Type为Rp的行,Discount Value显示为
Rp 1,000、Rp 2,000格式 - Discount Type为%的行,Discount Value显示为
10.5%、20.1%格式
- Discount Type为Rp的行,Discount Value显示为
自定义CtechGroupedDataGridView代码
namespace WinFormsApp1 { public partial class CtechGroupedDataGridView : DataGridView { public CtechGroupedDataGridView() { InitializeComponent(); DoubleBuffered = true; GridColor = Color.LightGray; BackgroundColor = SystemColors.Control; BorderStyle = BorderStyle.None; SelectionMode = DataGridViewSelectionMode.FullRowSelect; ColumnGroupEnabled = new int[] { 1, 2}; ClearSelection(); } protected override void OnDataBindingComplete(DataGridViewBindingCompleteEventArgs e) { base.OnDataBindingComplete(e); // Fit columns int columnsWidth = 0; foreach (DataGridViewColumn column in this.Columns) { if (column.Visible) { columnsWidth += column.Width; } } if (columnsWidth < this.Width) { for (int i = 0; i < this.Columns.Count; i++) { this.Columns[i].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells; } this.Columns[this.Columns.Count - 1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; } else { for (int i = 0; i < this.Columns.Count; i++) { this.Columns[i].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells; } } for (int i = 0; i < this.Columns.Count; i++) { int column = this.Columns[i].Width; this.Columns[i].AutoSizeMode = DataGridViewAutoSizeColumnMode.None; this.Columns[i].Width = column; this.Columns[i].HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter; } ClearSelection(); } protected override void OnCellFormatting(DataGridViewCellFormattingEventArgs args) { // Call home to base base.OnCellFormatting(args); // First row always displays if (args.RowIndex == 0) { return; } if (IsRepeatedCellValue(args.RowIndex, args.ColumnIndex)) { args.Value = string.Empty; args.FormattingApplied = true; } } private bool IsRepeatedCellValue(int rowIndex, int colIndex) { if (ColumnGroupEnabled.Contains(colIndex)) { DataGridViewCell currCell = Rows[rowIndex].Cells[colIndex]; DataGridViewCell prevCell = Rows[rowIndex - 1].Cells[colIndex]; if (currCell.Value != null && prevCell.Value != null) { if ((currCell.Value == prevCell.Value) || (currCell.Value.ToString() == prevCell.Value.ToString())) { return true; } } } return false; } public int[] ColumnGroupEnabled { get; set; } protected override void OnCellPainting(DataGridViewCellPaintingEventArgs args) { base.OnCellPainting(args); // Keep first and last row to visible if (args.RowIndex >= 0 && args.RowIndex < RowCount - 1) { args.AdvancedBorderStyle.Bottom = DataGridViewAdvancedCellBorderStyle.None; } // Ignore column and row headers and first row if (args.RowIndex < 1 || args.ColumnIndex < 0) { return; } if (IsRepeatedCellValue(args.RowIndex, args.ColumnIndex)) { args.AdvancedBorderStyle.Top = DataGridViewAdvancedCellBorderStyle.None; } else { args.AdvancedBorderStyle.Top = AdvancedCellBorderStyle.Top; } } } }
RegulerDiscountModel代码
public class RegulerDiscountModel { public RegulerDiscountModel() { } [DisplayName("ID")] public int RegulerDiscountID { get; set; } [DisplayName("Product Id")] public int RegulerDiscountProductId { get; set; } [DisplayName("Product Name")] public String? RegulerDiscountProductName { get; set; } [DisplayName("Discount Name")] public String? RegulerDiscountName { get; set; } [DisplayName("Discount Value")] public double RegulerDiscountValue { get; set; } [DisplayName("Discount Type")] public String? RegulerDiscountStrTypeId { get; set; } }
问题分析与修复方案
你的代码核心问题在于CellFormatting事件中的错误逻辑:
- 使用
CurrentRow获取行,导致始终处理选中行而非正在格式化的行 - 直接设置单元格Style.Format的时机错误,未利用事件参数完成格式化
修复后的CtechGroupedDataGridView_CellFormatting方法:
private void CtechGroupedDataGridView_CellFormatting(object? sender, DataGridViewCellFormattingEventArgs e) { try { // 仅处理Discount Value列(索引4) if (e.ColumnIndex == 4 && e.RowIndex >= 0 && e.RowIndex < CtechGroupedDataGridView.Rows.Count) { DataGridViewRow row = CtechGroupedDataGridView.Rows[e.RowIndex]; var discountType = row.Cells[5].Value?.ToString(); if (discountType == null) return; if (double.TryParse(e.Value?.ToString(), out double discountValue)) { switch (discountType) { case "Rp": // 格式化为带Rp前缀的千分位数字 e.Value = $"Rp {discountValue:N0}"; e.FormattingApplied = true; break; case "%": // 格式化为保留1位小数的百分比 e.Value = $"{discountValue:P1}"; e.FormattingApplied = true; break; } } } } catch (Exception ex) { MessageBox.Show(ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } }
关键修复点
- 通过
e.ColumnIndex精准定位目标列,避免无意义的处理 - 使用
Rows[e.RowIndex]获取当前正在格式化的行,而非选中行 - 直接设置
e.Value为格式化后的字符串,并标记FormattingApplied = true,告知DataGridView已完成格式化 - 增加数值解析容错逻辑,避免类型转换异常
内容的提问来源于stack exchange,提问作者Ihdina
相关产品推荐
相关产品推荐

