在开始问题之前,我想激发它。我的任务是分析Excel表格中的更改,但与通过内置机制记录更改不同,应该以编程方式检测更改,即使用户停用了更改记录或未安装我的Excel-AddIn。因此,我已经使用Microsoft.Interop.Excel-Library来访问工作表和其中的单元格。

现在要解决的问题是:为了查找更改,即使用户已经对数据进行了排序或移动,我也希望每个单元格都有一个唯一的ID,即使移动或复制它也要坚持下去。当然,如果复制了该ID,则该ID在工作表中是两次,并且新添加的单元格没有ID,但这没关系。此外,该ID对用户不可见,并且用户不应该能够对其进行修改或删除。

因此,我寻找了一个字段,发现Range-Object可以代表一个单元格并具有可以访问的不同成员。 ID字段吸引了我一个特别的领域,它看起来像我在寻找的内容。

Guid guid = Guid.NewGuid();
((Range) worksheet.Cells[rowNr, columnNr]).ID = guid.ToString();

并且像
Guid guid = Guid.Parse(((Range) worksheet.Cells[rowNr, columnNr]).ID);

这是完美的,因为我能够存储一个字符串(在本例中为Guid作为字符串,例如123463-fc34-c43a-a391-399fc2)并读取它。当单元被移动等时,它也粘在单元上并被移动。

但是,不幸的是,保存文件时,此ID字段并未保留,我也不知道为什么。我的意思是,在关闭并重新打开工作簿后,所有ID都消失了。

所以我的问题是,Range对象是否还有其他成员可以容纳字符串(= Guid),并且用户不可见。我尝试了Name-Member和Comment-Member,但是它们对用户都是可见的,并且可以轻松修改。

还是有一种方法可以告诉Excel在保存工作表时也要保存ID字段?

为了进行测试,您可以创建一个项目,添加对Microsoft.Office.Interop.Excel-Dll的引用,并添加以下代码(您的系统上必须安装了Excel)。它是一个单元测试,与JUnit一起运行,但是只需删除Assert-Command即可在没有JUnit的情况下对其进行测试:
using System;
using System.IO;
using Microsoft.Office.Interop.Excel;
using Excel = Microsoft.Office.Interop.Excel;
public void AddGuidAndRead()
{
    Excel.Application excelApp = new Excel.Application();
    Workbook excelWorkbook = excelApp.Workbooks.Add(Type.Missing);
    Worksheet worksheet = excelWorkbook.Sheets[1]; //1-based index

    Guid rowGuid1 = Guid.NewGuid();
    const string filename = "C:\\temp\\anyTemporaryFilename.xlsx";

    //Make sure, this file does not exist previously
    if (File.Exists(filename))
        File.Delete(filename);

    //Write the ID to the worksheet
    ((Range)worksheet.Cells[1, 1]).ID = rowGuid1.ToString();

    //Act (save and close the workbook)
    excelWorkbook.SaveAs(filename);
    excelWorkbook.Close();

    //Now open the workbook again
    Workbook openedWorkbook = excelApp.Workbooks.Open(filename);
    //Fetch the worksheet, where we worked previously
    Worksheet openedWorksheet = openedWorkbook.Sheets[1]; //1-based index

    //Read the ID from the cell
    string guid1 = ((Range)openedWorksheet.Cells[1, 1]).ID;

    //Cleanup
    openedWorkbook.Close(false);
    File.Delete(filename);
    excelWorkbook.Close(false, Type.Missing, Type.Missing);
    excelApp.Quit();

    //Assert - this fails!!
    Assert.AreEqual(rowGuid1.ToString(), guid1);

}

我将不胜感激,在保存工作表或与此主题相关的任何内容时,如何将ID设置为持久化的Excel-Worksheet-Cell。

提前谢谢了,
亚历克斯

2011年5月14日更新:

由于以下原因,“名称”字段似乎无法解决我的问题:

首先,最严重的事实是,该名称似乎必须唯一,但是我想为行中的所有单元格赋予相同的ID,这是行不通的。

其次,对我来说,访问C#中的Name-Field并不是很清楚。您可以使用
((Range)worksheet.Cells[rowNr, columnNr]).Name = guid.ToString();
//Remark: Special dealing with guids required,
//if they start with a number or contain special characters.

但是它有严重的问题。如果名称已经设置,则抛出异常,如果未设置名称,则尝试使用
string name = ((Range)worksheet.Cells[rowNr, columnNr]).Name.Name;

你会得到一个异常(exception)。并且您需要Name.Name,因为第一个Name-field不是字符串,而是整个Name-Object,它的内部还有另一个包含字符串的Name-Field。

最后,如果要检查它是否有名称,则不能执行以下操作:
if(((Range)worksheet.Cells[rowNr, columnNr]).Name == null)
    //Do something

因为它在访问不存在的名称字段时已经引发了异常。

最佳答案

您可以尝试为每个单元格使用命名范围。该名称将保持不变。我没有用interop尝试过,但是它可以与旧的vba一起使用。在下面的代码中,请注意,可以向用户隐藏名称。

Function try()
    Dim x As Integer
    Dim y As String

    Worksheets("Sheet1").Range("a1").Name = "_firstCell"
    Range("_firstCell").Value = 9999
    Dim nm As Name
    'hide
     For Each nm In ActiveWorkbook.Names
        If Left(nm.Name, 1) = "_" Then
            nm.Visible = False
        End If
    Next
    'move the named cell
    Range("_firstCell").Cut Range("b1")
    'check the value and address
    x = Range("_firstCell").Value
    y = Range("_firstCell").Address

End Function

据我了解,工作簿中命名范围的数量没有逻辑limit

关于C#:Microsoft.Office.Interop.Excel.Range的ID字段未与Excel工作表保持一致,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/5995575/

10-09 18:20
查看更多