PAYATHON 2026

如何在 DataGridView 中筛选 Payment 行并求和

支付老李

结论

不必遍历所有列。逐行检查描述列,找到包含 Payment 的行后,读取该行的金额列,将其转换为 decimal 并累加。最后在按钮单击事件中把总额写入 TextBox。

private void btnCalculate_Click(object sender, EventArgs e)
{
    decimal total = 0m;

    foreach (DataGridViewRow row in dataGridView1.Rows)
    {
        // 忽略 DataGridView 底部用于新增数据的空白行
        if (row.IsNewRow)
            continue;

        string description = Convert.ToString(row.Cells[0].Value);

        if (description.IndexOf(
                "Payment",
                StringComparison.OrdinalIgnoreCase) < 0)
        {
            continue;
        }

        object value = row.Cells[1].Value;

        if (value == null || value == DBNull.Value)
            continue;

        if (decimal.TryParse(value.ToString(), out decimal amount))
        {
            total += amount;
        }
    }

    textBox1.Text = total.ToString("0.##");
}

按照题目中的数据计算,total 最终是 173.8。

为什么不需要内层循环

数据结构是固定的:

  • 第一列是描述;
  • 第二列是金额。

每行只需检查 Cells[0],匹配成功后再读取 Cells[1]。原代码中的内层 for 循环还会检查其他列,没有必要,而且列顺序调整后容易读错单元格。

如果已经为 DataGridView 的列设置了 Name,建议直接使用列名,代码更容易理解:

string description = Convert.ToString(row.Cells["Description"].Value);
object value = row.Cells["Amount"].Value;

金额列存储的是数值时

金额单元格最好直接存储 decimal,美元符号通过单元格格式显示。这样求和时不需要处理 $ 等货币符号:

private void btnCalculate_Click(object sender, EventArgs e)
{
    decimal total = 0m;

    foreach (DataGridViewRow row in dataGridView1.Rows)
    {
        if (row.IsNewRow)
            continue;

        string description =
            Convert.ToString(row.Cells["Description"].Value);

        if (description.IndexOf(
                "Payment",
                StringComparison.OrdinalIgnoreCase) < 0)
        {
            continue;
        }

        object value = row.Cells["Amount"].Value;

        if (value == null || value == DBNull.Value)
            continue;

        try
        {
            total += Convert.ToDecimal(value);
        }
        catch (FormatException)
        {
            // 当前单元格不是有效金额,可按业务需求记录或提示
        }
        catch (InvalidCastException)
        {
            // 当前值无法转换为 decimal
        }
    }

    textBox1.Text = total.ToString("0.##");
}

可以把金额列设置为货币显示格式:

dataGridView1.Columns["Amount"].DefaultCellStyle.Format = "C";

这项设置只影响显示,不会把底层的 decimal 数据转换成字符串。

单元格中确实包含 $ 时

如果金额单元格保存的是 "$45.9" 这类文本,就需要按货币格式解析:

using System.Globalization;

private void btnCalculate_Click(object sender, EventArgs e)
{
    decimal total = 0m;
    CultureInfo currencyCulture = CultureInfo.GetCultureInfo("en-US");

    foreach (DataGridViewRow row in dataGridView1.Rows)
    {
        if (row.IsNewRow)
            continue;

        string description = Convert.ToString(row.Cells[0].Value);

        if (description.IndexOf(
                "Payment",
                StringComparison.OrdinalIgnoreCase) < 0)
        {
            continue;
        }

        string amountText = Convert.ToString(row.Cells[1].Value);

        if (decimal.TryParse(
                amountText,
                NumberStyles.Currency,
                currencyCulture,
                out decimal amount))
        {
            total += amount;
        }
    }

    textBox1.Text = total.ToString("C", currencyCulture);
}

使用标准货币格式后,结果通常会显示为:

$173.80

如果必须显示成示例中的 $173.8,可以这样写:

textBox1.Text = "$" + total.ToString(
    "0.##",
    CultureInfo.InvariantCulture);

注意事项

  • row.IsNewRow 用于排除允许用户新增行时出现在表格底部的空白行。
  • 金额应尽量存储为 decimal,不要把货币符号和数值一起保存为字符串。
  • OrdinalIgnoreCase 能同时匹配 Payment、payment 等不同大小写形式。
  • 如果描述必须以 Payment 开头,可以改用 StartsWith,以免匹配到其他仅在中间包含该单词的描述:
if (description.StartsWith(
        "Payment",
        StringComparison.OrdinalIgnoreCase))
{
    // 累加金额
}
  • decimal.TryParse 解析失败时不会抛出异常,适合处理可能包含空值或无效文本的表格数据。

备注:内容仅供参考。