在 Windows 应用程序中使用 XML、XSL、DHTML 和 Javascript
本文演示了如何动态创建 XML 文档,使用 XSL 转换这些文档,然后将生成的 HTML 显示到浏览器控件中。 还演示了如何使用资源中的 HTML 和图像。
摘要
此应用程序使用 WTL、COM、XML、HTML、DHTML 和 JavaScript。 它将演示创建一个自定义命令栏,该命令栏包括两个组合框和 XML 文档的创建。 它还将演示如何动态地将新元素插入到 XML 文档中,使用 XSL 转换它,并将生成的 HTML 动态地插入到 HTML 页面中。 它还将展示如何使用资源中的 HTML 和图像。
概述
很久很久以前,我为一个客户做了一个项目,该项目涉及将包含来自世界各地各种来源的新闻文章摘要的文本文件转换为 XML 文档。 然后将 XML 输入到数据库中。 在这个阶段完成后,我创建了一个编辑器的原型,可以直接创建 XML 文档,而无需中间文本文件。 由于各种原因,再也没有听到客户的消息。
在开发此原型的过程中,我遇到了几个问题,以前找不到任何示例,所以我认为其他开发人员可能会发现我的解决方案有价值。
详细说明
该应用程序使用 WTL 编写,以使其尽可能小并减少可再分发文件的数量。 它使用基于 XML 的配置文件来控制某些组合框的内容。 这允许最终用户轻松扩展。
使用一个分隔框架在左侧窗格中显示一个表单视图,在右侧窗格中显示一个 HTML 视图。 启动时,右侧窗格是全屏的。
m_Splitter.SetSinglePaneMode(SPLIT_PANE_RIGHT);
要开始一个新文档,请选择文件->新建。 将显示一个对话框,询问卷号和期号。 这用于命名文件,并作为根元素的属性嵌入到 XML 文档中。 单击“确定”后,将使用 MSXML 创建一个包含必要基本元素的新文档。 示例中的 test1_1.xml 文件显示了这个基本文件。
您会注意到定制的命令栏,其中包含两个组合框。 这些是在启动时通过读取 config.xml 文件来填充的。 其内容也用于创建新的 XML 文档框架。
单击“新建文档”对话框上的“确定”后,左侧窗格将全屏显示。 然后,用户可以输入必要的信息,选择适用于文章的关键词,并选择日期。 完成后,他们单击“输入”按钮。
单击“输入”按钮后,使用 DDX 从字段中恢复数据,并将其输入到先前创建的 XML 文档模板中。 要将文章正确放置在文档中,请从当前的组合框选择中格式化 XPATH 搜索,并选择节点。 然后将数据附加到此节点。
CString strSearch; strSearch.Format(_T("/*/*[@name=\"%s\"]"), m_ComboBar.GetSection()); if( FAILED(m_pNewDoc->selectSingleNode(A2OLE(strSearch), &pSection) ) ) return FALSE;
在此步骤之后,新创建的节点将通过 XSL 转换并发送到 HTML 视图。 为了减少支持文件的数量,并且由于 XSL 较小,我将其包含在字符串资源中。
CString strXSL; strXSL.LoadString(IDS_XSL); short bSuccess; if( FAILED(m_pXSLDoc->loadXML(A2OLE(strXSL),&bSuccess) ) ) return FALSE; CComBSTR bstrOutput; if( SUCCEEDED(pArticle->transformNode(m_pXSLDoc,&bstrOutput) ) ) m_HTMLView.InsertElement(OLE2A(bstrOutput));
将转换后的 XML 发送到 HTML 视图后,它会插入到当前文档中。 这是通过从 HTML 控件获取 IWebBrowser2 接口,找到文档的正文元素,并在最后一个元素的末尾插入 HTML 来完成的。 同样,为了减少支持文件,HTML 页面被存储为资源。 展开或折叠文章是通过 JavaScript 完成的,该 JavaScript 连接到标题旁边的图像。
BOOL CXMLView::GetBody() { // Get the browser interface from the embedded HTML control CComPtr<IWEBBROWSER2> spBrowser; if( SUCCEEDED(QueryControl(__uuidof(spBrowser), reinterpret_cast<void**>(&spBrowser))) ) { // Get the document element CComPtr<IDISPATCH> pDisp; if( SUCCEEDED(spBrowser->get_Document(&pDisp)) && pDisp) { // Get the body element if( SUCCEEDED(pDisp.QueryInterface(&m_pDoc)) && m_pDoc ) { if(SUCCEEDED(m_pDoc->get_body(&m_pBody)) && m_pBody ) return TRUE; } } } return FALSE; } void CXMLView::InsertElement(CString strElement) { USES_CONVERSION; // If the body has not been stored yet then attempt to find it if( !m_pBody ) { if( !GetBody() ) { MessageBox(_T("Body not found.\n\nPlease close application"), _T("Critical Error"), MB_ICONERROR); return; } } // Get the elements on this page CComPtr<IDISPATCH> pDisp; if( SUCCEEDED(m_pBody->get_all(&pDisp)) && pDisp) { // Get the ElementCollection CComQIPtr<IHTMLELEMENTCOLLECTION> pColl(pDisp); if(pColl) { // Make sure there are items in the collection long lCount; if(SUCCEEDED(pColl->get_length(&lCount)) && lCount > 0) { // Show/Hide the element ToggleElement(pColl); // Release the dispatch to be reused and get the // the last item in the collection pDisp.Release(); CComVariant varName,varIndex(lCount); pColl->item(varName, varIndex, &pDisp); // Get the element returned above and insert the text // before the end of it CComQIPtr<IHTMLELEMENT> pElement(pDisp); if(pElement) pElement->insertAdjacentHTML(A2OLE("beforeEnd"), A2OLE(strElement)); } } } }
完成文档后,可以通过选择文件->保存来保存它。 这将弹出一个文件夹选择对话框。
改进
总有改进的余地。 由于这是作为原型创建的,我留下了一些未完成的事情。 将来,我可能会增强它以允许编辑先前创建的文档。