問題を始める前に、それをやる気にさせたいと思います。私の仕事はExcelシートの変更を分析することですが、ビルド内のメカニズムを介して変更を記録するのとは異なり、変更はプログラムで検出する必要があります。ユーザーが変更の記録を無効にしたり、Excel-AddInがインストールされていない場合でも変更を検出する必要があります。そのため、Microsoft.Interop.Excel-Libraryを使用して、シートとその中のセルにアクセスしました。
ここで問題になります。変更を見つけるために、ユーザーがデータを並べ替えたり移動したりした場合でも、移動またはコピーした場合でも、セルごとに一意のIDを保持する必要がありました。もちろん、コピーされた場合、IDはシートに2回あり、新しく追加されたセルにはIDがありませんが、それは問題ありません。さらに、このIDはユーザーに表示されないようにし、ユーザーがIDを変更または削除できないようにする必要があります。
そこで、フィールドを探して、単一のセルを表すことができ、アクセスできるさまざまなメンバーを持つRange-Objectを見つけました。私が探していたもののように見えるIDフィールドという1つの特別なフィールドが私の注意を引きました。
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を試しましたが、どちらもユーザーに表示され、簡単に変更できます。
または、シートを保存するときにIDフィールドも保存するようにExcelに指示する方法はありますか?
テストのために、プロジェクトを作成し、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);
}
ワークシートやこのテーマに関する何かを保存するときに、永続化されるExcel-Worksheet-CellにIDを配置する方法についてのアイデアをいただければ幸いです。
よろしくお願いします、アレックス
アップデート14.5.2011:
名前フィールドは、次の理由で私の問題の解決策ではないようです。
まず、最も深刻なのは、名前が一意である必要があるように見えるという事実ですが、行のすべてのセルに同じ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;
例外が発生します。また、Name.Nameが必要です。これは、最初のName-fieldが文字列ではなく、Name-Object全体であり、内部に文字列を含む別のName-Fieldがあるためです。
そして最後に、名前があるかどうかを確認したい場合は、次のようなことはできません。
if(((Range)worksheet.Cells[rowNr, columnNr]).Name == null)
//Do something
存在しない名前フィールドにアクセスすると、すでに例外がスローされるためです。