OpenXML SDK Внедрить VBA в книгу Excel - PullRequest
6 голосов
/ 14 марта 2012

Я могу успешно вставить фрагмент кода VBA в сгенерированную книгу Excel, но я пытаюсь использовать событие Workbook_Open (), чтобы код VBA выполнялся при открытии файла. Я добавляю sub к объекту "ThisWorkbook" в моем файле шаблона xlsm. Затем я использую инструмент производительности openxml для отображения кода и получения закодированных данных VBA.

Когда файл генерируется и я просматриваю VBA, я вижу объекты "ThisWorkbook" и "ThisWorkbook1". Мой VBA находится в объекте "ThisWorkbook", но код никогда не выполняется при открытии. Если я перенесу свой код VBA в «ThisWorkbook1» и заново открою файл, он будет работать нормально. Почему создается дополнительная книга ThisWorkbook? Разве невозможно внедрить электронную таблицу Excel с подпрограммой Workbook_Open ()? Вот фрагмент кода C #, который я использую:

private string partData = "...";  //base 64 encoded data from reflection code
//open workbook, myWorkbook
VbaProjectPart newPart = myWorkbook.WorkbookPart.AddNewPart<VbaProjectPart>("rId1");
System.IO.Stream data = GetBinaryDataStream(partData);
newPart.FeedData(data);
data.Close();
//save and close workbook

У кого-нибудь есть идеи?

Ответы [ 3 ]

4 голосов
/ 17 июля 2012

Основываясь на моих исследованиях, нет способа вставить данные части проекта в формат, которым вы можете манипулировать в C #. В формате OpenXML проект VBA по-прежнему хранится в двоичном формате. Однако копирование VbaProjectPart из одного документа Excel в другой должно работать. В результате вам нужно будет заранее определить, что вы хотели сказать в части проекта.

Если вы согласны с этим, вы можете добавить следующий код в файл шаблона Excel в объекте Microsoft Excel «ThisWorkbook» вместе с соответствующим кодом макроса:

Private Sub Workbook_Open()
    Run "Module1.SomeMacroName()"
End Sub

Чтобы скопировать объект VbaProjectPart из одного файла в другой, вы должны использовать следующий код:

public static void InsertVbaPart()
{
    using(SpreadsheetDocument ssDoc = SpreadsheetDocument.Open("file1.xlsm", false))
    {
        WorkbookPart wbPart = ssDoc.WorkbookPart;
        MemoryStream ms;
        CopyStream(ssDoc.WorkbookPart.VbaProjectPart.GetStream(), ms);

        using(SpreadsheetDocument ssDoc2 = SpreadsheetDocument.Open("file2.xlsm", true))
        {
            Stream stream = ssDoc2.WorkbookPart.VbaProjectPart.GetStream();
            ms.WriteTo(stream);
        }
    }
}

public static void CopyStream(Stream input, Stream output)
{
    byte[] buffer = new byte[short.MaxValue + 1];
    while (true)
    {
        int read = input.Read(buffer, 0, buffer.Length);
        if (read <= 0)
            return;
        output.Write(buffer, 0, read);
    }
}

Надеюсь, это поможет.

0 голосов
/ 14 июня 2018

Я обнаружил, что другие ответы по-прежнему приводят к дублированию объекта «Рабочий лист». Я использовал решение, подобное тому, что сказал @ZlotaMoneta, но с другим синтаксисом, найденным здесь :

List<VbaProjectPart> newParts = new List<VbaProjectPart>();
using (var originalDocument = SpreadsheetDocument.Open("file1.xlsm"), false))
{
    newParts = originalDocument.WorkbookPart.GetPartsOfType<VbaProjectPart>().ToList();

    using (var document = SpreadsheetDocument.Open("file2.xlsm", true))
    {
        document.WorkbookPart.DeleteParts(document.WorkbookPart.GetPartsOfType<VbaProjectPart>());

        foreach (var part in newParts)
        {
            VbaProjectPart vbaProjectPart = document.WorkbookPart.AddNewPart<VbaProjectPart>();
            using (Stream data = part.GetStream())
            {
                vbaProjectPart.FeedData(data);
            }                    
        }

        //Note this prevents the duplicate worksheet issue
        spreadsheetDocument.WorkbookPart.Workbook.WorkbookProperties.CodeName = "ThisWorkbook";
    }
}
0 голосов
/ 16 мая 2018

Вам необходимо указать атрибут «codeName» в объекте «xl / workbook..xml». После заполнения VbaProjectPart макросом.Добавьте этот код:

var workbookPr = spreadsheetDocument.WorkbookPart.Workbook.Descendants<WorkbookProperties>().FirstOrDefault();
workbookPr.CodeName = "ThisWorkBook";

После открытия файла все должно работать.

Итак, чтобы добавить макрос, вам необходимо:

  1. ИзменитьТип документа в макрос включензначение "ThisWorkBook"

  2. Сохранить как с расширением .xlsm

...