C #: поле идентификатора Microsoft.Office.Interop.Excel.Range не сохраняется с Excel-листом - PullRequest
6 голосов
/ 13 мая 2011

прежде чем я начну с проблемы, я хочу мотивировать ее. Моя задача состоит в том, чтобы проанализировать изменения в Excel-Sheet, но в отличие от записи изменений через встроенный механизм, изменения должны обнаруживаться программно, и даже если пользователь деактивировал запись изменений или мой Excel-AddIn не установлен. Для этого я использовал Microsoft.Interop.Excel-Library для доступа к листу и ячейкам внутри.

Теперь к проблеме: для поиска изменений, даже если пользователь отсортировал или переместил данные, я хотел иметь уникальный идентификатор для каждой ячейки, которая остается в ней, даже если она перемещена или скопирована. Конечно, если скопировано, идентификатор будет дважды на листе, а новые добавленные ячейки не будут иметь идентификатора, но это нормально. Кроме того, этот идентификатор не должен быть виден пользователю, и пользователь не должен иметь возможность изменять или удалять его.

Итак, я искал поле и нашел 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 не сохраняется при сохранении файла, и я не знаю почему. Я имею в виду, что после закрытия и повторного открытия книги все идентификаторы исчезли.

Так что мой вопрос, если есть какой-либо другой член объекта Range, который может содержать строку (= Guid) и который невидим для пользователя. Я пробовал имя-участника и комментарий-члена, но оба они видимы для пользователя и могут быть легко изменены.

Или есть способ сообщить 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);

}

Буду признателен за любую идею о том, как сохранить идентификатор в ячейке Excel-Worksheet, который сохраняется при сохранении листа или чего-либо еще на эту тему.

Большое спасибо заранее, Alex

Обновление 14.5.2011:

Поле имени, похоже, не является решением моей проблемы по следующим причинам:

Во-первых, и самым серьезным является тот факт, что кажется, что имя должно быть уникальным, но я хотел дать всем ячейкам подряд один и тот же идентификатор, который не работает.

Во-вторых, доступ к полю имени в C # мне не совсем понятен. Вы можете установить значение с помощью

((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, потому что первое поле имени - это не строка, а целый объект имени, внутри которого есть другое поле имени, содержащее строку.

И, наконец, если вы хотите проверить, есть ли у него имя или нет, вы не можете сделать что-то вроде:

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

потому что он уже генерирует исключение при доступе к несуществующему полю имени.

Ответы [ 3 ]

1 голос
/ 13 июля 2011

На самом деле нет способа сохранить идентификатор непосредственно на листе, как я об этом. Ни в поле ID (которое не сохранено), ни в качестве Имен (разрешены только уникальные имена) или Комментариев (не видны пользователю).

Но в книге есть концепция CustomProperties, которая может содержать строки, и, поскольку все сериализуемые классы могут быть перенесены в строки, это позволяет программисту отдельно сохранять идентификаторы и восстанавливать их при загрузке книги.

В любом случае, для моей цели использовался другой подход, который вычисляет хэш-значения каждой строки и вместо этого сравнивает хэш-значения строки.

1 голос
/ 14 мая 2011

Вы можете попробовать использовать именованный диапазон для каждой ячейки.Имя будет сохраняться.Я не пробовал это с 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 

Из того, что я понимаю, нет логического предела для числа именованных диапазонов в книге.

0 голосов
/ 15 февраля 2016

попробуйте использовать: FormatConditions activecell.FormatConditions.Add xlExpression, формула1: = "test_1234", чтобы получить значение для ID IDRange = mid (activecell.FormatConditions (1) .formula1,2) "test_1234"

...