生成状态报告
我的示例依据Excel对象模型使用Idispatch访问来自进度表智能文档工作表的信息以生成状态报告。我也可以选择开发一些C++(www.cppentry.com)代码来驱动Word对象模型建立状态文档,但是我选择了另一种途径,它也利用了Word 2003中的新XML特性的优点。我把状态报告生成为XML文件,并使用XSLT在Word中打开它以提供良好的格式化。这种方法利用了使用XSLT很容易把几个XML文件(可能是整个小组的状态报告)合并成一个大的报告的优点,如果它们作为Word文档保存就很难合并了。
在花费一段时间研究如何实现XML保存后,我决定定义一个类来管理将显示在状态报告上的数据项列表。当开发者更新进度表中的信息的时候,智能文档操作DLL中的代码就会建立并更新这个事务列表,这与文档操作工作面板中放置的控件对应。最后,开发者点击工作面板中的控件生成XML状态报告。每个类负责使用MSXML DOM正确地保持存储在XML中的信息。处理这种情行的处理方法是CScheduleTaskList和CScheduleTask。我用于格式化它的Word XSL样式表是ScheduleReport.xsl,它在示例代码中提供了。当你安装智能文档解决方案的时候,这个XLST文件就会被安装好,并让Word知道它,这样当你在Word中打开XML状态报告的时候,它将自动应用这个样式表。你也可以使用Word中的XML数据视图事务面板来选择这个样式表。
在我生成的XML中有一个指令值得提起:
< mso-application progid="Word.Document" >
这条指令告诉Windows资源管理器这个XML文件的默认的应用程序是Word。你也可能注意到结果是这个文件的图标改变了。
让这个Word样式表精确地显示状态报告需要花一段时间,因此此处我没有提供详细信息。但是,我要警告你,如果你查看我提供的样式表,你会对显示的这个Word XML大纲的复杂程度感到惊奇。其中的很多是样板代码,很多都是在Word中建立示例状态文档(我希望通过这种方式设置样式和字体)并把该文档保存为XML格式的时候获得的。我把生成的XML中的大部分复制到自己的XSLT样式表中。但是,我不能确定这种办法生成的XML是在Word中生成良好格式化的状态报告所需要的最小的XML.
我可以根据自己的经验为开发XSLT提供一些建议。要记住XML是一种非常精确的、大小写敏感的语言。在这方面它与C++(www.cppentry.com)没有不同,但是如果出错了,你接收到的诊断信息可能是某种类型的“不能应用样式表”信息,而不是编译器提供的详细描述。如果你对某些已经在运行的东西做大范围的修改,应该逐步增加并做好版本控制,这样才能回滚到以前的修改去。我遇到了一个问题,即样式表引用了ScheduleSmartDocument名字空间,但是由于操作DLL中出了错误,在生成的XML状态报告中该名字空间的名称变成了ScheduleSmartDoc。结果当我在XML状态报告上应用该XSLT的时候,Word简单地显示了一个空文档,因为样式表中使用的XPATH与XML中的不匹配。
让Word自动地应用正确的样式表需要花点力气。最后,我让安装智能文档操作DLL的清单也安装该样式表。为了让Word知道这个样式表,清单中的解决方案条目必须含有context属性,它引用该Word XML名字空间(http://schemas.microsoft.com/office/word/2003/wordml)。
最后一步是把智能文档URI(示例中的是ScheduleSmartDocument)作为清单、样式表和生成的XML状态报告文档的XML名字空间。在例子中,我把ScheduleSmartDocument作为名字空间的名称。你应该根据组织中的唯一的URI来使用名字空间。
反向操作,从Word文档生成XML看起来是个好的办法。但是,幅面和切割结果的Word输出的时候要非常小心。例如,我试图减小列表部分的复杂性(它描述了文档中使用的格式列表),因为我的状态报告只需要一个简单的、一层的符号列表。在一个小时以后,我放弃了,留下大量的未改动的部分,虽然我确信其中的很多是我不需要的。
如果你在Visual Studio中打开一个生成好的Word XML文件,你会发现它对于Visual Studio .NET大纲查看器来说太复杂了。因此,我建议当XML打开的时候,首先点击“数据视图”,强制Visual Studio分解该XML,接着返回“XML视图”查看良好格式化的版本(它在Notepad中打开的时候,实际上是不可阅读的)。 我的最后一条建议是在调整样式表的时候可以使用一个非常强大的工具。MSXSL.EXE命令行工具允许你给XML文件应用样式表并查看生成的XML输出。你可以从链接 http://msdn.microsoft.com/library/en-us/dnxml/html/msxsl.asp处下载它。
结论
我首先是在C++(www.cppentry.com)中开始开发操作DLL的,这是因为目前Office编码的太多示例都是用Visual Basic编写的。但是,使用C++(www.cppentry.com)执行一些甚至于很简单的事务(例如引用Excel中的一个单元值)也比使用Visual Basic明显复杂多了。图11显示了与Visual Basic中ActiveSheet.Range("A1").Value语句等同的C++(www.cppentry.com)代码(注意这个方法的示例代码有不同的版本,因为我用设置值方法把它重新编写为共享代码)。我提供了智能文档处理程序希望对Excel对象模型执行的少量操作的C++(www.cppentry.com)代码,但是我编写的示例根本没有因为使用C++(www.cppentry.com)而受益。我鼓励读者认真的考虑使用C++(www.cppentry.com)与Visual Basic之间的代价。
图11. C++(www.cppentry.com)中的ActiveSheet.Range("A1").Value
HRESULT CExcelWorkbook::HrCellValue( int iRow, int iColumn, CComVariant *pValue) { USES_CONVERSION; HRESULT hr;
ATLASSERT(p); // IDispatch必须在调用方法前设置 if (pValue == NULL) return E_INVALIDARG;
pValue->Clear();
AssureDispidRange();
TCHAR tzRangeReference[20];
ATLASSERT(iColumn<26); // 支持的不能多于26个,
// 为"CurrentRow"处理特定的值 if (iRow == iCurrentRow && FAILED(hr=GetSelectedRow(iRow))) return hr;
wsprintf(tzRangeReference, "%c%d", 'A'+(iColumn-1), iRow);
CComVariant varRangeName(T2COLE(tzRangeReference)); CComVariant varRange;
if (SUCCEEDED(hr=GetProperty1(m_dispidRange, &varRangeName, &varRange))) { ATLASSERT(varRange.vt == VT_DISPATCH); ATLASSERT(varRange.pdispVal); LPDISPATCH lpRange = varRange.pdispVal;
CComDispatchDriver dispRange(lpRange);
AssureDispidRangeValue(dispRange);
hr = dispRange.GetProperty(m_dispidRangeValue, pValue); }
return hr; } |
Office 2003中的另一个选择是C#,它是有吸引力的,但是请你确保自己清楚了解非受控代码(Office)调用受控代码(用C#或Visual Basic .NET编写的操作处理程序部件)的性能问题。有可能使用Visual Basic是最好的办法。
智能文档为开发者提供了非常强大的用户界面范例。你可以在利用微软Office应用程序的所有功能的时候,同时提供无缝地集成到Office事务面板用户界面中的自定义UI和行为。我认为智能文档将很快在大型组织中广泛使用,以简化业务过程,如同目前使用的带有宏的电子表格一样。
|