C#:Microsoft.Office.Interop.Excel.Range的ID字段不保存与Excel表单

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

现在到了这个问题:即使用户已经sorting或者移动了数据,为了find更改,我希望每个单元格都有一个唯一的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); 

这是完美的,因为我能够存储一个string(在这种情况下,一个Guid作为string,如123463-fc34-c43a-a391-399fc2)并读取它。 它也粘在细胞上,被移动,当细胞被移动等。

但不幸的是,这个ID字段没有被保存,当文件被保存时,我不知道为什么。 我的意思是closures并重新打开工作簿后,所有的ID都不见了。

所以我的问题是,如果有任何其他成员的范围对象,可以容纳一个string(= GUID),哪些是不可见的用户。 我尝试了名称成员和评论成员,但它们都是用户可见的,并且可以很容易地修改。

或者,有没有办法告诉Excel,我也想保存ID字段时,保存工作表?

对于testing,您可以创build一个项目,添加对Microsoft.Office.Interop.Excel-Dll的引用并添加以下代码(您必须在系统上安装Excel)。 这是一个unit testing,与JUnit一起运行,但只需删除Assert-Command即可在没有JUnit的情况下进行testing:

 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. 

但是它有严重的问题。 如果名称已经设置,则会抛出exception,如果没有设置名称,则尝试使用该名称进行访问

 string name = ((Range)worksheet.Cells[rowNr, columnNr]).Name.Name; 

你会得到一个例外。 而且你需要Name.Name,因为第一个Name-field不是string,而是一个完整的Name-Object,里面有另一个Name-Field,它包含一个string。

最后,如果你想检查它是否有名字,你不能这样做:

 if(((Range)worksheet.Cells[rowNr, columnNr]).Name == null) //Do something 

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

您可以尝试为每个单元格使用命名的范围。 这个名字将会持续。 我还没有尝试过与互操作,但它适用于良好的旧版本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 

根据我的理解,对于工作簿中命名范围的数量没有逻辑限制 。

实际上,就我个人而言,没有办法直接将ID保留在表格中。 无论是在ID字段(不是持久的),也不是名称(只允许唯一的名称)或评论(用户可见)。

但是在工作簿中有一个CustomProperties的概念,它可以保存string,并且由于所有可序列化的类都可以编组为string,这允许程序员分别保存这些ID并在加载工作簿时恢复它们。

无论如何,为了我的目的,我们使用了另一种方法,即计算每行的散列值,然后比较行散列值。

尝试使用:FormatConditions activecell.FormatConditions.Add xlExpression,formula1:=“test_1234”来获得ID IDRange = mid(activecell.FormatConditions(1).formula1,2)的值“test_1234”

Interesting Posts