我创建了一个Excel功能区插件,需要在Excel会话之间保留用户选择。使用自定义XML部件似乎是最佳选择。但是,如果没有获得COMExceptions,我就无法使用它。
MSDN文档不是很有用(http://msdn.microsoft.com/en-us/library/bb608612.aspx)。有人能给我一个在Excel Ribbon插件中进行此工作的示例吗?
答案 0 :(得分:4)
我知道有三种不同的方法:
自定义XML部分 对于应用程序级别的添加,这是我首选的方法,用于存储需要保存在已保存的xls文件中的任何应用程序数据,而不会让用户看到。
http://msdn.microsoft.com/en-us/library/bb608612.aspx
缓存数据群岛 这仅适用于文档级添加项。如果您尝试在应用程序级别添加中使用它,则会出现异常。
http://blogs.msdn.com/b/eric_carter/archive/2004/04/23/119294.aspx
隐藏的工作表 使用VSTO,您可以创建用户无法看到的隐形工作表。这很好用,但导致很多笨拙的编码转换你的数据以适应excel表。
更新(2014年): 因此,最终使用Custom XML部件成为一个性能问题,因此我的应用程序必须更改回使用隐藏的工作表。显然,一旦XML达到一定的大小,Excel变得非常迟缓。我的插件有自定义部件到达数千个节点,XML越大,Excel中的所有就越慢。例如,只需点击任何单元格就会产生非常明显的延迟。
答案 1 :(得分:2)
如果要存储来自具有特定文档的应用程序级别加载项的任何类型的元数据,可以将数据序列化为某种字符串(base64,xml等),并将其保存为“非常隐藏”片。可见性设置为“非常隐藏”的工作表只能通过编程API访问,因此即使用户发现隐藏的工作表,他们仍然无法访问它,或者甚至知道它已存在。
// create sheet for this save
workbook.Sheets.Add();
newSettingsWorksheet = workbook.ActiveSheet;
newSettingsWorksheet.Name = hiddenSheetName;
newSettingsWorksheet.Visible = Excel.XlSheetVisibility.xlSheetVeryHidden;
需要注意的一件重要事项是,如果要存储长度超过32767个字符的字符串(适合单元格的最大字符数),则必须将其剪切成块并将其分布在多个单元格中。
关于您遇到的COM异常,您应该知道Excel可以随时为触及COM对象(例如工作表,单元格或任何属于Excel的任何内容)的任何请求抛出COM异常(如果是忙于另一个请求(例如,用户正在打字,这是重新计算公式)。您可以预期的例外情况是:
HRESULT:0x800AC472(忽略)
HRESULT:0x8000101A(稍后重试)
我想Excel应用程序开发人员会这样做,因此加载项无法使Excel本身看起来很糟糕/没有响应。
答案 2 :(得分:1)
您应该使用注册表来存储一些信息,例如在关闭应用程序或需要在多个实例之间共享时需要保留的用户首选项和历史记录。
用户的配置单元(HKEY_CURRENT_USER)永远不会有权限问题。只需参考.NET注册表类:http://msdn.microsoft.com/en-us/library/microsoft.win32.registry.aspx
答案 3 :(得分:1)
考虑使用自定义属性。每个Excel工作表在场景后面维护程序员可以轻松使用的属性列表。例如,我使用自定义属性“记住”为特定工作表选择了功能区下拉列表中的哪些项目;当工作表更改时,请拉出该工作表的自定义属性,以找出上次活动时选择的下拉项目。
每张工作表和文档都会保留自定义属性。
using System;
using Microsoft.Office.Interop.Excel;
public partial class CustPropExample
{
/// <summary>
/// delete and then store the custom property by passed key and value
/// </summary>
bool bExcelCustProp_Replace(Worksheet wkSheet,
string custPropKey,
string custPropVal)
{
if (!ExcelCustProp_DeleteByKey(wkSheet, custPropKey))
return (false);
if (!ExcelCustProp_Add(wkSheet, custPropKey, custPropVal))
return (false);
return (true);
}
/// <summary>
/// return the custom property value of passed key
/// </summary>
string ExcelCustProp_Get(Worksheet wkSheet,
string key)
{
try
{
for (int i = 1; i <= wkSheet.CustomProperties.Count; i++) // NOTE: 1-based !!!!!!!!
{
if (wkSheet.CustomProperties.get_Item(i).Name == key)
return (wkSheet.CustomProperties.get_Item(i).Value);
}
}
catch (Exception ex)
{
ShowErrorMsg("Error with getting cust prop; key [" + key + "], exc: " + ex.Message, false);
}
return (string.Empty);
}
/// <summary>
/// add cust prop
/// </summary>
bool ExcelCustProp_Add(Worksheet wkSheet,
string key,
string custPropVal)
{
try
{
wkSheet.CustomProperties.Add(key, custPropVal);
}
catch (Exception ex)
{
return(ShowErrorMsg("Error in adding cust prop: " + ex.Message, false));
}
return (true);
}
/// <summary>
/// if passed key exists, delete it
/// </summary>
bool ExcelCustProp_DeleteByKey(Worksheet wkSheet,
string key)
{
try
{
for (int i = 1; i <= wkSheet.CustomProperties.Count; i++) // NOTE: 1-based !!!!!!!!
{
if (wkSheet.CustomProperties.Item[i].Name == key)
{
wkSheet.CustomProperties.Item[i].Delete();
break;
}
}
}
catch (Exception ex)
{
return(ShowErrorMsg("Error deleting cust prop (key='" + key + "') - " + ex.Message, false));
}
return (true);
}
/// <summary>
/// stub for error handling
/// </summary>
bool ShowErrorMsg(string msg,
bool retval)
{
System.Windows.Forms.MessageBox.Show(msg);
return (retval);
}
}