在VSTO Excel中,如何检测单元格中的数据?

7

一种快速检测给定工作表中是否存在数据的过程,而无需实际遍历工作表中的所有行/列。

对于我的当前流程,我目前正在遍历整个工作表,在导入时有一些明显的延迟时间。


2
我知道这已经过时了,但除了关闭这些事件、填充工作表之外,还可以这样做:currentInstance.EnableEvents = false/true; currentInstance.ScreenUpdating = false/true; - Jeremy Thompson
5个回答

16
为了避免循环并且利用近乎瞬间的执行速度,你可以使用Excel.WorksheetFunction.CountA方法,它返回与=CountA()工作表函数相同的结果。 假设你的Excel.Application引用被命名为'excelApp',你的Excel.Worksheet引用被命名为'worksheet',你可以在C# 4.0中使用以下代码:
// C# 4.0
int dataCount = (int)excelApp.WorksheetFunction.CountA(worksheet.Cells);

if (dataCount == 0)
{
    // All cells on the worksheet are empty.
}
else
{
    // There is at least one cell on the worksheet that has non-empty contents.
}
在C# 3.0及以下版本中,它显得有些冗长,因为你必须明确提供缺失的可选参数:
// C# 3.0 and below
int dataCount = (int)excelApp.WorksheetFunction.CountA(
    worksheet.Cells, 
    Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
    Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
    Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
    Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
    Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);

if (dataCount == 0)
{
    // All cells on the worksheet are empty.
}
else
{
    // There is at least one cell on the worksheet that has non-empty contents.
}

强制转换为int类型不起作用,因为CountA返回的是double类型。 - Maxence

4

我已经有一段时间在使用VSTO和Excel,而且使用非常频繁,所以我希望能够与您分享我学到的东西。

根据您提供的信息,我建议将其转换为对象数组并使用该信息进行操作。基本上,您可以按以下方式访问这些值:

object[,] arrayValues = (object[,])ExcelRange.Value2;

arrayValues是一个2D数组([行,列])。Excel可以非常快地填充数组,并且对数组的操作当然会非常高效(不用担心装箱操作的性能问题,这不是个问题,相信我)。


谢谢James, 我正在为Excel开发一个VSTO插件,经常遇到一些问题,这些问题可以很快得到专业人士的解答,但在互联网上查找却很费时间。我会更多地利用stackoverflow,因为这里有很多VSTO专家! - Mohamed Nuur
@coder4life,你知道是否有一种方法可以像获取值一样进行“批量”获取单元格颜色等属性吗?我已经使用了与你描述的值相同的方法几个月了,但我还没有找到一种能够获取所有颜色(range.Interior.Color)而不是逐行逐列获取的方法。这太慢了!谢谢! - Michael Zlatkovsky - Microsoft
实际上,我为了这个获取颜色的问题新开了一个线程,这样其他人就可以更容易地找到它,而不是浏览评论。链接是https://dev59.com/Sl7Va4cB1Zd3GeqPGyTq。 - Michael Zlatkovsky - Microsoft
这是否意味着我们知道要获取的范围?有时这是否意味着过度获取所需内容。我用你所说的方法进行读写,但是这条评论虽然有效,但对于手头的问题并没有帮助。或者我错过了什么? - ΩmegaMan

0

我找到了下面这个解决方案,它也是瞬间完成的,但我不确定它的准确性,目前它已经通过了我所有的测试。

以下是想要知道的人:

Worksheet sheet = (Worksheet)this.Application.ActiveSheet;
Range usedRange = sheet.UsedRange;
bool isUsed = (usedRange.Count > 1);
if (usedRange.Count == 1)
{
  isUsed = (usedRange.Value2 != null) &&
           (!string.IsNullOrEmpty(usedRange.Value2.ToString()));
}

if(isUsed)
{
  // worksheet cells not empty
}

我想这比每次进行检查时都清空剪贴板或计算工作表中所有非空单元格要简单得多。感谢Mikael和Mike,我很感激你们的回答。


momad,你的方法并不差,但是它需要更多的工作量,并且会错误地将一些没有数据的工作表报告为包含数据,因为UsedRange可能会受到单元格格式等因素的影响,而不仅仅是值。如果你使用Application.WorksheetFunction.CountA方法,你可以在一行中捕获所有数据和仅仅数据,而不是五行。 - Mike Rosenblum
两种方法肯定比使用剪贴板好得多。剪贴板的方式还会考虑到带有“空格”的单元格,而其他方式则不会,但那是一个特殊情况 :) - Mikael Svenson
Mike,我不知道格式问题。如果是这样的话,那么我绝对同意使用工作表函数CountA是更好的方法。然而,在我的具体情况下,即使只是格式更改,我也不想覆盖单元格。感谢您的澄清! - Mohamed Nuur
“IsNullOrEmpty” 应该改为 “IsNullOrWhiteSpace” 吗?如果你取一个空单元格并调用 “String.Length(cell.ToString())”,我认为它会返回 18,即使实际的单元格是空的且一直为空。 - user2140173

0

怎么样?

public static bool IsSheetEmpty(int sheetNo)
{
    bool isEmpty = false;

    if (sheetNo <= Globals.ThisAddIn.Application.Worksheets.Count)
    {
        Worksheet ws = Globals.ThisAddIn.Application.Worksheets[sheetNo];

        if (ws.UsedRange.Address.ToString() == "$A$1" && String.IsNullOrWhiteSpace(ws.get_Range("A1").Value2))
        {
            isEmpty = true;
        }
    }
    else
    {
        // or add your own error handling when sheetNo is not found
    }

    return isEmpty;
}

示例调用

bool isFirstEmpty = IsSheetEmpty(1);

-1

这应该很快:

    private void CheckForContent()
    {
        Worksheet activeSheet = ActiveSheet;
        var range = activeSheet.get_Range("A1", GetExcelColumnName(activeSheet.Columns.Count)+activeSheet.Rows.Count.ToString() );
        range.Select();
        range.Copy();
        string text = Clipboard.GetText().Trim();
        if(string.IsNullOrEmpty(text))
        {
            MessageBox.Show("No text");
        }
    }

    private string GetExcelColumnName(int columnNumber)
    {
        int dividend = columnNumber;
        string columnName = String.Empty;
        int modulo;

        while (dividend > 0)
        {
            modulo = (dividend - 1) % 26;
            columnName = Convert.ToChar(65 + modulo).ToString() + columnName;
            dividend = (int)((dividend - modulo) / 26);
        }
        return columnName;
    }

选择和复制永远不快速 ;) - user2140173

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接