电子发票解析工具-c#桌面应用开发-DataGridView表格控件使用详解

电子发票解析工具-c#桌面应用开发-DataGridView表格控件使用详解

1. 概述

DataGridView 是 C# WinForms 中功能强大的表格控件,用于在 Windows 应用程序中显示和编辑数据。本文档基于电子发票解析工具项目中的实际应用,详细介绍 DataGridView 的配置、样式设置、数据绑定、事件处理、自定义绘制等核心功能。

2. DataGridView 控件的初始化与基本配置

2.1 控件的创建与基本属性设置

在设计器中创建 DataGridView 控件后,需要进行基本的属性配置:

csharp 复制代码
// 设置列标题高度
this.dataGridView_Data.ColumnHeadersHeight = 29;
// 设置控件停靠方式
this.dataGridView_Data.Dock = System.Windows.Forms.DockStyle.Fill;
// 设置行标题宽度
this.dataGridView_Data.RowHeadersWidth = 82;
// 设置行高
this.dataGridView_Data.RowTemplate.Height = 23;
// 设置控件大小
this.dataGridView_Data.Size = new System.Drawing.Size(1644, 510);

2.2 禁用和启用用户交互功能

根据实际需求,可以控制用户对表格的交互权限:

csharp 复制代码
// 禁用用户添加新行
this.dataGridView_Data.AllowUserToAddRows = false;
// 允许用户调整列宽
this.dataGridView_Data.AllowUserToResizeColumns = true;
// 允许用户调整行高
this.dataGridView_Data.AllowUserToResizeRows = true;

3. 数据源绑定与数据管理

3.1 数据源绑定

项目中使用 BindingList 作为数据源,实现数据的双向绑定和动态更新:

csharp 复制代码
// 定义并初始化 BindingList 数据源
private static BindingList<ExportData> eds;

// 绑定数据源到 DataGridView
dataGridView_Data.DataSource = eds;
ExportData数据结构

ExportData是用于存储发票数据的核心模型类,包含以下字段:

csharp 复制代码
public class ExportData
{
    public string 检测标识 { get; set; }
    public string 发票名称 { get; set; }
    public string 发票代码 { get; set; }
    public string 发票号码 { get; set; }
    public string 开票日期 { get; set; }
    public string 机器编号 { get; set; }
    public string 校验码 { get; set; }
    public string 购买方名称 { get; set; }
    public string 购买方税号 { get; set; }
    public string 购买方地址电话 { get; set; }
    public string 购买方开户行及账号 { get; set; }
    public string 销售方名称 { get; set; }
    public string 销售方税号 { get; set; }
    public string 销售方地址电话 { get; set; }
    public string 销售方开户行及账号 { get; set; }
    public string 发票明细ID { get; set; }
    public string 税收类别 { get; set; }
    public string 项目名称 { get; set; }
    public string 未税金额 { get; set; }
    public string 税率 { get; set; }
    public string 税额 { get; set; }
    public string 价税合计 { get; set; }
    public string 金额大写 { get; set; }
    public string 备注 { get; set; }
    public string 收款人 { get; set; }
    public string 复核人 { get; set; }
    public string 开票人 { get; set; }
    public string 文件名称 { get; set; }
    public string 图片名称 { get; set; }
    public string 文件路径 { get; set; }
}

### 3.2 数据刷新与重绘

当数据发生变化时,需要刷新表格显示:

```csharp
// 刷新 DataGridView
dataGridView_Data.Refresh();

3.3 清空表格数据

实现清空表格数据但保留列结构的功能:

csharp 复制代码
// 清空DataGridView,只保留列名
private void ClearDataGridView()
{
    // 创建一个空的 DataTable,保留原列结构
    DataTable emptyTable = new DataTable();
    foreach (DataGridViewColumn column in dataGridView_Data.Columns)
    {
        DataColumn newColumn = new DataColumn(column.Name, column.ValueType);
        newColumn.Caption = column.HeaderText;
        emptyTable.Columns.Add(newColumn);
    }
    
    // 将 DataGridView 的数据源设置为新的空 DataTable
    dataGridView_Data.DataSource = emptyTable;
}

4. 表格样式与外观定制

4.1 单元格字体和颜色设置

csharp 复制代码
// 设置单元格默认字体和颜色
dataGridView_Data.DefaultCellStyle.Font = new System.Drawing.Font("微软雅黑", 10, FontStyle.Regular);
dataGridView_Data.DefaultCellStyle.ForeColor = System.Drawing.Color.Black;

4.2 表头样式设置

项目中实现了自定义的表头样式设置方法:

csharp 复制代码
// 设置表头样式
SetColumnHeaderStyle(System.Drawing.Color.SaddleBrown, System.Drawing.Color.White, 12);

// 表头样式设置方法
private void SetColumnHeaderStyle(Color backColor, Color foreColor, float fontSize)
{
    dataGridView_Data.ColumnHeadersDefaultCellStyle.BackColor = backColor; // 设置背景色
    dataGridView_Data.ColumnHeadersDefaultCellStyle.ForeColor = foreColor; // 设置字体颜色
    dataGridView_Data.ColumnHeadersDefaultCellStyle.Font = new System.Drawing.Font(
        dataGridView_Data.ColumnHeadersDefaultCellStyle.Font.FontFamily, fontSize, FontStyle.Regular); // 设置字体大小
}

4.3 列宽设置与自动调整

实现了列宽的自动调整和手动设置:

csharp 复制代码
// 自动调整列宽
dataGridView_Data.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;

// 1. 先保留当前列宽
var columnWidths = new Dictionary<int, int>();
for (int i = 0; i < dataGridView_Data.Columns.Count; i++)
{
    columnWidths[i] = dataGridView_Data.Columns[i].Width;
}

// 2. 关闭自动调整列宽
dataGridView_Data.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None;

// 3. 设置每一列到刚刚保留的宽度
for (int i = 0; i < dataGridView_Data.Columns.Count; i++)
{
    dataGridView_Data.Columns[i].Width = columnWidths[i];
}

// 4. 针对特定列设置固定宽度
dataGridView_Data.Columns[getColumnIndexByName("购买方地址电话")].Width = 200;
dataGridView_Data.Columns[getColumnIndexByName("购买方开户行及账号")].Width = 200;
dataGridView_Data.Columns[getColumnIndexByName("销售方地址电话")].Width = 200;
dataGridView_Data.Columns[getColumnIndexByName("销售方开户行及账号")].Width = 200;

4.4 列锁定功能

实现了重要列的锁定功能,防止用户水平滚动时看不到关键信息:

csharp 复制代码
// 锁定列(水平滚动时固定显示)
dataGridView_Data.Columns[0].Frozen = true; // 锁定第一列
dataGridView_Data.Columns[1].Frozen = true; // 锁定第二列
dataGridView_Data.Columns[2].Frozen = true; // 锁定第三列
dataGridView_Data.Columns[3].Frozen = true; // 锁定第四列
dataGridView_Data.Columns[4].Frozen = true; // 锁定第五列

5. 自定义绘制与行号显示

5.1 行号显示功能

通过重写 RowPostPaint 事件,实现了自定义行号显示:

csharp 复制代码
private void dataGridView_Data_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
{
    // 获取表格中每行的第一个矩型
    Rectangle rect = new Rectangle(e.RowBounds.Location.X,
                           e.RowBounds.Location.Y,
                           dataGridView_Data.RowHeadersWidth - 4,
                           e.RowBounds.Height);
    //重新在矩型中添加文本
    TextRenderer.DrawText(e.Graphics,
                          (e.RowIndex + 1).ToString(),
                          dataGridView_Data.RowHeadersDefaultCellStyle.Font,
                          rect,
                          dataGridView_Data.RowHeadersDefaultCellStyle.ForeColor,
                          TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter);
}

5.2 单元格自定义绘制

通过 CellPainting 事件,实现了表头和左上角单元格的自定义绘制:

csharp 复制代码
private void dataGridView_Data_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
    // 检查是否是左上角单元格
    if (e.RowIndex == -1) // 左上角单元格的行列索引都是 -1
    {
        if (e.ColumnIndex == -1)
        {
            e.Handled = true; // 告诉系统我们会手动绘制此单元格

            // 绘制单元格背景
            e.Graphics.FillRectangle(Brushes.SaddleBrown, e.CellBounds);

            // 绘制自定义文本
            string text = "序号"; // 你想放置的内容

            // 创建对齐格式
            StringFormat stringFormat = new StringFormat
            {
                Alignment = StringAlignment.Center, // 水平居中
                LineAlignment = StringAlignment.Center, // 垂直居中
            };

            using (System.Drawing.Brush textBrush = new SolidBrush(System.Drawing.Color.White))
            {
                // 计算文本的位置
                RectangleF textBounds = new RectangleF(e.CellBounds.X, e.CellBounds.Y, e.CellBounds.Width, e.CellBounds.Height);
                // 创建一个非加粗的字体
                System.Drawing.Font font = new System.Drawing.Font("微软雅黑", e.CellStyle.Font.Size, FontStyle.Regular);
                e.Graphics.DrawString(text, font, textBrush, textBounds, stringFormat);
            }

            // 绘制单元格边框
            e.Graphics.DrawRectangle(Pens.Chocolate, e.CellBounds);

            // 继续绘制其他的单元格内容(如标题)
            e.PaintContent(e.CellBounds);
        }
        else
        {
            e.Handled = true;

            // 绘制单元格背景
            e.Graphics.FillRectangle(Brushes.SaddleBrown, e.CellBounds);

            // 绘制标题文本,水平居中
            using (System.Drawing.Brush textBrush = new SolidBrush(System.Drawing.Color.White))
            {
                // 设置文本对齐为居中
                StringFormat format = new StringFormat
                {
                    Alignment = StringAlignment.Center,
                    LineAlignment = StringAlignment.Center // 避免换行
                };
                // 计算文本的位置
                RectangleF textBounds = new RectangleF(e.CellBounds.X, e.CellBounds.Y, e.CellBounds.Width, e.CellBounds.Height);
                // 绘制文本
                System.Drawing.Font font = new System.Drawing.Font("微软雅黑", e.CellStyle.Font.Size, FontStyle.Regular);
                e.Graphics.DrawString(e.FormattedValue.ToString(), font, textBrush, textBounds, format);
            }

            // 绘制单元格边框
            e.Graphics.DrawRectangle(Pens.Chocolate, e.CellBounds);
        }
    }
}

6. 右键菜单功能实现

为 DataGridView 添加右键菜单,提供单元格和行操作功能:

csharp 复制代码
// 初始化右键菜单
private void InitializeContextMenuStrip()
{
    // 创建上下文菜单
    contextMenuStrip = new ContextMenuStrip();
    
    // 添加菜单项
    ToolStripMenuItem selectItem = new ToolStripMenuItem("查看单元格内容");
    ToolStripMenuItem selectRow = new ToolStripMenuItem("查看整行内容");
    ToolStripMenuItem showRowJson = new ToolStripMenuItem("显示行JSON数据");
    
    // 添加到菜单
    contextMenuStrip.Items.AddRange(new ToolStripItem[] { selectItem, selectRow, showRowJson });
    
    // 绑定右键菜单到 DataGridView
    dataGridView_Data.ContextMenuStrip = contextMenuStrip;
    
    // 添加菜单项点击事件
    selectItem.Click += SelectItem_Click;
    selectRow.Click += SelectRow_Click;
    showRowJson.Click += ShowRowJson_Click;
}

// 查看单元格内容
private void SelectItem_Click(object sender, EventArgs e)
{
    // 获取选定单元格
    if (dataGridView_Data.SelectedCells.Count > 0)
    {
        int rowIndex = dataGridView_Data.SelectedCells[0].RowIndex;
        int columnIndex = dataGridView_Data.SelectedCells[0].ColumnIndex;
        
        // 获取选定单元格的值
        var cellValue = dataGridView_Data.Rows[rowIndex].Cells[columnIndex].Value;
        string str_CellValue = "";
        if (cellValue != null)
        {
            str_CellValue = cellValue.ToString();
        }
        CustomPopBox messageBox = new CustomPopBox($"提示信息->《选定整行》", "内容如下:", str_CellValue);
        messageBox.ShowDialog(this); // 模态显示
    }
}

// 查看整行内容
private void SelectRow_Click(object sender, EventArgs e)
{
    // 获取选定单元格
    if (dataGridView_Data.SelectedCells.Count > 0)
    {
        int rowIndex = dataGridView_Data.SelectedCells[0].RowIndex;
        // 获取选定单元格的值
        var rowValue = getRowValues(rowIndex);
        
        CustomPopBox messageBox = new CustomPopBox($"提示信息->《选定整行》", "内容如下:", rowValue);
        messageBox.ShowDialog(this); // 模态显示
    }
}

// 根据行索引获取表格选定行内容
private string getRowValues(int rowIndex)
{
    // 创建一个列表来保存行数据
    var rowValues = new List<object>();
    
    // 遍历指定行的所有单元格
    foreach (DataGridViewCell cell in dataGridView_Data.Rows[rowIndex].Cells)
    {
        // 将单元格的值添加到列表中
        rowValues.Add(cell.Value);
    }
    return string.Join(", ", rowValues);
}

// 显示行JSON数据
private void ShowRowJson_Click(object sender, EventArgs e)
{
    // 获取选定单元格
    if (dataGridView_Data.SelectedCells.Count > 0)
    {
        int rowIndex = dataGridView_Data.SelectedCells[0].RowIndex;
        // 获取选定单元格的值
        var jsonString = getRowJson(rowIndex);
        // 解析 JSON 字符串
        var jsonArray = JArray.Parse(jsonString);
        
        // 格式化 JSON 字符串
        string formattedJson = JsonConvert.SerializeObject(jsonArray, Formatting.Indented);
        // 创建并显示自定义对话框
        CustomPopBox messageBox = new CustomPopBox($"提示信息->《显示行JSON数据》", "内容如下:", formattedJson);
        messageBox.ShowDialog(this); // 模态显示
    }
}

// 根据行索引获取表格选定行的JSON数据
private string getRowJson(int rowIndex)
{
    // 创建一个列表来保存行数据
    var rowValues = new List<RowJsonData>();
    
    // 遍历指定行的所有单元格
    foreach (DataGridViewCell cell in dataGridView_Data.Rows[rowIndex].Cells)
    {
        // 将单元格的值添加到列表中
        RowJsonData rowJsonData = new RowJsonData();
        rowJsonData.ColumnIndex = Convert.ToInt32(cell.ColumnIndex);
        rowJsonData.ColumnIName = dataGridView_Data.Columns[cell.ColumnIndex].Name.ToString();
        
        if (cell.Value != null)
        {
            rowJsonData.TextValue = cell.Value.ToString();
        }
        else
        {
            rowJsonData.TextValue = "";
        }
        
        rowValues.Add(rowJsonData);
    }
    // 将 rowValues 转换为 JSON 字符串
    string jsonString = JsonConvert.SerializeObject(rowValues);
    return jsonString;
}

// 定义行数据的JSON结构类
public class RowJsonData
{
    public int ColumnIndex { get; set; }
    public string ColumnIName { get; set; }
    public string TextValue { get; set; }
}

7. 事件处理

7.1 选择变化事件

通过 SelectionChanged 事件,实现了行选择时的联动操作:

csharp 复制代码
private void dataGridView_Data_SelectionChanged(object sender, EventArgs e)
{
    // 确保有选中的行
    if (dataGridView_Data.SelectedRows.Count > 0)
    {
        // 获取选中的行
        var selectedRow = dataGridView_Data.SelectedRows[0];
        if (selectedRow.Cells["图片名称"].Value != null)
        {
            // 执行你的代码
            // 示例:获取某列的值
            string cellValue = selectedRow.Cells["图片名称"].Value.ToString();
            // 首先将所有项复制到一个列表中
            var items = listBox_JpgList.Items.Cast<object>().ToList();
            
            // 获取当前项的索引
            int index = listBox_JpgList.Items.IndexOf(cellValue);
            listBox_JpgList.SelectedIndex = index;
            listBox_JpgList.Refresh();
            
            //切换预览页卡
            switchToTab("tabPage_InvoiceSumData");
            // 你可以在这里进行其他操作,比如更新 UI 或处理数据
        }
    }
}

7.2 单元格编辑事件

通过 CellEndEdit 事件,实现了单元格编辑后的验证和处理:

csharp 复制代码
private void dataGridView_Data_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
    // 单元格编辑完成后的处理逻辑
    // 可以在这里添加数据验证、格式检查等功能
}

7.3 单元格点击事件

通过 CellContentClick 事件,实现了单元格点击时的交互功能:

csharp 复制代码
private void dataGridView_Data_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
    // 单元格内容点击时的处理逻辑
    // 可以用于实现超链接、按钮等交互功能
}

8. 数据操作与处理

8.1 数据遍历与处理

项目中实现了多种数据遍历和处理方法:

csharp 复制代码
// 遍历 DataGridView 的每一行
foreach (DataGridViewRow row in dataGridView_Data.Rows)
{
    if (row.IsNewRow) continue; // 跳过空行
    
    // 获取行中的数据
    string invoiceNumber = row.Cells["发票号码"].Value.ToString();
    // 处理数据...
}

// 从后向前遍历DataGridView的每一行,以便删除行时不会影响索引
for (int i = dataGridView_Data.Rows.Count - 1; i >= 0; i--)
{
    DataGridViewRow row = dataGridView_Data.Rows[i];
    
    // 判断是否需要删除该行
    if (需要删除的条件)
    {
        dataGridView_Data.Rows.RemoveAt(i);
    }
}

8.2 数据批量更新

实现了发票数据的批量更新功能,如同步税率、项目名称等:

csharp 复制代码
// 将一张发票中有多页的数据,最后一页没有税率,需要提取一页的税率,将最后一页的税率赋值
private void UpdateTaxRates()
{
    // 使用字典来存储发票号码和对应的税率
    Dictionary<string, string> invoiceTaxRates = new Dictionary<string, string>();
    
    // 遍历 DataGridView 的每一行
    foreach (DataGridViewRow row in dataGridView_Data.Rows)
    {
        if (row.IsNewRow) continue; // 跳过空行
        
        // 获取发票号码和税率
        string invoiceNumber = row.Cells["发票号码"].Value?.ToString();
        string taxRate = row.Cells["税率"].Value?.ToString();
        
        // 如果有税率值,存储到字典中
        if (!string.IsNullOrEmpty(invoiceNumber) && !string.IsNullOrEmpty(taxRate))
        {
            if (!invoiceTaxRates.ContainsKey(invoiceNumber))
            {
                invoiceTaxRates.Add(invoiceNumber, taxRate);
            }
        }
    }
    
    // 再次遍历,为没有税率的行设置税率
    foreach (DataGridViewRow row in dataGridView_Data.Rows)
    {
        if (row.IsNewRow) continue;
        
        string invoiceNumber = row.Cells["发票号码"].Value?.ToString();
        string taxRate = row.Cells["税率"].Value?.ToString();
        
        // 如果没有税率值,但字典中有该发票的税率,设置税率
        if (!string.IsNullOrEmpty(invoiceNumber) && string.IsNullOrEmpty(taxRate) && 
            invoiceTaxRates.ContainsKey(invoiceNumber))
        {
            row.Cells["税率"].Value = invoiceTaxRates[invoiceNumber];
        }
    }
}

8.3 数据清空与删除

实现了表格数据的清空和删除功能:

csharp 复制代码
// 清空DataGridView,只保留列名
private void ClearDataGridView()
{
    // 创建一个空的 DataTable,保留原列结构
    DataTable emptyTable = new DataTable();
    foreach (DataGridViewColumn column in dataGridView_Data.Columns)
    {
        DataColumn newColumn = new DataColumn(column.Name, column.ValueType);
        newColumn.Caption = column.HeaderText;
        emptyTable.Columns.Add(newColumn);
    }
    
    // 将 DataGridView 的数据源设置为新的空 DataTable
    dataGridView_Data.DataSource = emptyTable;
}

// 删除选中的行
private void DeleteSelectedRows()
{
    // 从后向前遍历选中的行,以便删除行时不会影响索引
    for (int i = dataGridView_Data.SelectedRows.Count - 1; i >= 0; i--)
    {
        dataGridView_Data.Rows.RemoveAt(dataGridView_Data.SelectedRows[i].Index);
    }
    
    // 刷新DataGridView
    dataGridView_Data.Refresh();
}

9. 数据导出功能

项目中实现了将 DataGridView 数据导出到 Excel 和 PDF 的功能:

9.1 导出到 Excel

csharp 复制代码
public void ExportDataGridViewToXls()
{
    try
    {
        // 导出逻辑
        // ...
        
        // 遍历 DataGridView 的每一行
        foreach (DataGridViewRow row in dgv.Rows)
        {
            if (row.IsNewRow) continue;
            
            // 遍历每一行的单元格
            foreach (DataGridViewCell cell in row.Cells)
            {
                // 处理单元格数据
                // ...
            }
        }
        
        // 保存Excel文件
        // ...
    }
    catch (Exception ex)
    {
        // 异常处理
        // ...
    }
}

9.2 导出到 PDF

csharp 复制代码
public void ExportDataGridViewToPdf()
{
    try
    {
        // 导出逻辑
        // ...
        
        // 遍历 DataGridView 的列
        foreach (DataGridViewColumn col in dgv.Columns)
        {
            // 处理列标题
            // ...
        }
        
        // 遍历 DataGridView 的行
        foreach (DataGridViewRow row in dgv.Rows)
        {
            if (row.IsNewRow) continue;
            
            // 遍历每一行的单元格
            foreach (DataGridViewCell cell in row.Cells)
            {
                // 处理单元格数据
                // ...
            }
        }
        
        // 保存PDF文件
        // ...
    }
    catch (Exception ex)
    {
        // 异常处理
        // ...
    }
}

10. 高级功能与优化

10.1 性能优化

项目中实现了多种性能优化措施:

csharp 复制代码
// 禁用自动调整列宽以提高性能
dataGridView_Data.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None;

// 设置合适的列宽,避免过度绘制
dataGridView_Data.Columns[getColumnIndexByName("购买方地址电话")].Width = 200;

// 使用高效的数据结构存储临时数据
Dictionary<string, string> invoiceTaxRates = new Dictionary<string, string>();

// 从后向前遍历删除行,避免索引问题
for (int i = dataGridView_Data.Rows.Count - 1; i >= 0; i--)
{
    // 删除行操作
    // ...
}

10.2 数据验证与错误处理

实现了完善的数据验证和错误处理机制:

csharp 复制代码
// 数据验证
if (string.IsNullOrEmpty(cellValue))
{
    MessageBox.Show("数据不能为空!");
    return;
}

// 异常处理
try
{
    // 可能引发异常的操作
    // ...
}
catch (Exception ex)
{
    MessageBox.Show($"操作失败:{ex.Message}");
    // 记录日志
    // ...
}

11. 代码示例:完整的 DataGridView 初始化与配置

以下是项目中完整的 DataGridView 初始化和配置代码示例:

csharp 复制代码
// 设置单元格默认字体和颜色
dataGridView_Data.DefaultCellStyle.Font = new System.Drawing.Font("微软雅黑", 10, FontStyle.Regular);
dataGridView_Data.DefaultCellStyle.ForeColor = System.Drawing.Color.Black;

// 设置表头样式
SetColumnHeaderStyle(System.Drawing.Color.SaddleBrown, System.Drawing.Color.White, 12);

// 将eds列表绑定到表格数据源
dataGridView_Data.DataSource = eds;

// 在 DataGridView 属性中启用行标题
dataGridView_Data.RowHeadersVisible = true;

//自动调整列宽
dataGridView_Data.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;

// 1. 先保留当前列宽
var columnWidths = new Dictionary<int, int>();
for (int i = 0; i < dataGridView_Data.Columns.Count; i++)
{
    columnWidths[i] = dataGridView_Data.Columns[i].Width;
}

// 2. 关闭自动调整列宽
dataGridView_Data.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None;

// 3. 设置每一列到刚刚保留的宽度
for (int i = 0; i < dataGridView_Data.Columns.Count; i++)
{
    dataGridView_Data.Columns[i].Width = columnWidths[i];
}

// 设置用户交互选项
dataGridView_Data.AllowUserToResizeColumns = true; // 允许用户调整列宽
dataGridView_Data.AllowUserToResizeRows = true;
dataGridView_Data.AllowUserToAddRows = false; // 禁用添加新行

// 设置特定列的宽度
dataGridView_Data.Columns[getColumnIndexByName("购买方地址电话")].Width = 200;
dataGridView_Data.Columns[getColumnIndexByName("购买方开户行及账号")].Width = 200;
dataGridView_Data.Columns[getColumnIndexByName("销售方地址电话")].Width = 200;
dataGridView_Data.Columns[getColumnIndexByName("销售方开户行及账号")].Width = 200;

// 锁定重要列
dataGridView_Data.Columns[0].Frozen = true; // 锁定列
dataGridView_Data.Columns[1].Frozen = true; // 锁定列
dataGridView_Data.Columns[2].Frozen = true; // 锁定列
dataGridView_Data.Columns[3].Frozen = true; // 锁定列
dataGridView_Data.Columns[4].Frozen = true; // 锁定列

// 添加右键菜单
InitializeContextMenuStrip();

// 刷新表格
dataGridView_Data.Refresh();

// 执行数据更新操作
UpdateTaxRates();//同步发票号码相同的税率
UpdateItemName();//同步发票号码相同的项目名称
UpdateTaxCategories();//同步发票号码相同的税收类别
UpdateCompanyNameOrTaxFileNumber();//对购买方名称和购买方税号与销售方名称和销售方税号进行检测互换

dataGridView_Data.Refresh();

12. 常见问题与解决方案

12.1 行删除时的索引问题

问题:删除行时,如果从前向后遍历,会导致索引错乱,无法正确删除所有目标行。

解决方案:从后向前遍历行,这样删除行不会影响剩余行的索引。

csharp 复制代码
// 从后向前遍历DataGridView的每一行,以便删除行时不会影响索引
for (int i = dataGridView_Data.Rows.Count - 1; i >= 0; i--)
{
    DataGridViewRow row = dataGridView_Data.Rows[i];
    // 删除行操作
    dataGridView_Data.Rows.RemoveAt(i);
}

12.2 性能问题

问题:当 DataGridView 中数据量较大时,会出现卡顿现象。

解决方案

  1. 关闭自动调整列宽
  2. 使用虚拟化模式
  3. 分页加载数据
  4. 减少不必要的刷新操作

12.3 数据绑定问题

问题:修改数据后,DataGridView 没有自动更新。

解决方案

  1. 使用 BindingList 作为数据源
  2. 调用 Refresh() 方法手动刷新
  3. 确保数据类实现了 INotifyPropertyChanged 接口

13. 总结

DataGridView 是 C# WinForms 中功能强大的表格控件,通过本文档介绍的各种技术和方法,可以实现复杂的表格数据展示、编辑、导出等功能。在实际项目中,需要根据具体需求选择合适的配置和实现方式,同时注意性能优化和用户体验。

本文档基于电子发票解析工具项目的实际应用,涵盖了 DataGridView 的核心功能和高级用法,希望对 C# WinForms 开发者有所帮助。

14. 参考资料

  1. Microsoft Docs: DataGridView 控件
  2. C# WinForms 开发文档
  3. 电子发票解析工具项目源码

文档编写日期:2024年 基于 C# .NET Framework 开发

相关推荐
周杰伦fans1 小时前
C# 中的 `Hashtable`
开发语言·c#
lingggggaaaa1 小时前
免杀对抗——C2远控篇&PowerShell&有无文件落地&C#参数调用&绕AMSI&ETW&去混淆特征
c语言·开发语言·笔记·学习·安全·microsoft·c#
咩图2 小时前
WPF+Prism8.0.0.1909+C#创建一个桌面程序
c#·wpf·prism
Charles_go2 小时前
C#中级45、什么是组合优于继承
开发语言·c#
我是唐青枫3 小时前
一文理解 C#.NET Tuples:从基础到高级应用
c#·.net
Charles_go4 小时前
C#中级46、什么是模拟
开发语言·oracle·c#
一只爱做笔记的码农4 小时前
【BootstrapBlazor】移植BootstrapBlazor VS工程到Vscode工程,报error blazor106的问题
笔记·学习·c#
曹牧8 小时前
C#:姓名脱敏
开发语言·c#
缺点内向8 小时前
C# 中 Word 文档目录的插入与删除指南
开发语言·c#·word·.net