我可以成功地将一段VBA代码注入到生成的excel工作簿中,但我要做的是使用Workbook_Open()事件,以便在文件打开时执行VBA代码。我将sub添加到我的xlsm模板文件中的“ThisWorkbook”对象。然后我使用openxml生产力工具来反映代码并获取编码的VBA数据。
生成文件并查看VBA时,我看到“ThisWorkbook”和“ThisWorkbook1”对象。我的VBA在“ThisWorkbook”对象中,但代码永远不会在打开时执行。如果我将我的VBA代码移动到“ThisWorkbook1”并重新打开该文件,它可以正常工作。为什么要创建一个额外的“ThisWorkbook”?是否无法使用Workbook_Open()子系统注入excel电子表格?以下是我正在使用的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
有人有想法吗?
答案 0 :(得分:4)
根据我的研究,没有办法以您可以在C#中操作的格式插入项目部件数据。在OpenXML格式中,VBA项目仍以二进制格式存储。但是,将VbaProjectPart
从一个Excel文档复制到另一个Excel文档应该有效。因此,您必须事先确定您希望项目部分说出什么。
如果你没问题,那么你可以将以下代码添加到'ThisWorkbook'Microsoft Excel对象中的模板Excel文件中,以及相应的宏代码:
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);
}
}
希望有所帮助。
答案 1 :(得分:0)
您需要在“xl / workbook..xml”对象中指定“codeName”属性 用宏输入VbaProjectPart后。添加以下代码:
var workbookPr = spreadsheetDocument.WorkbookPart.Workbook.Descendants<WorkbookProperties>().FirstOrDefault();
workbookPr.CodeName = "ThisWorkBook";
打开文件后,现在一切都会正常工作。
因此,要添加宏,您需要:
将文档类型更改为启用宏
添加VbaProjectPart并将其与之前创建的宏
在xl / workbook..xml中添加workbookPr codeName attr,其值为“ThisWorkBook”
另存为.xlsm分机。
答案 2 :(得分:0)
我发现其他答案仍然导致重复的“工作表”对象。我使用了与@ZlotaMoneta所说的相似的解决方案,但使用了different syntax found here:
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";
}
}