用MFC实现WebGUI--(CDHtmlDialog)
自从去年年底一次棘手的界面,开始研究用web做界面到现在大约1年,这一年间不是局限在实现层面,也并非一直研究这一个问题,有很多问题其实不是问题,只是自己没有想清楚或者思想没放开。对于一个界面开发人员,想必拉的对话框不少于100个,腻味不必说,光是对话框大小改变导致控件跟着变化都需要一番功夫,加上界面美观,界面的风格统一,界面的灵活多变......,头痛。在对话框里面加载位图,加载gif,超链接......,啊,没法控制了吧!在考虑远点,现在.net3.0技术已经完全打破应用和桌面的界限,我们的界面html资源完全可以放在一个web站点上,这样界面是完全动态的。
其间写过2篇这方面的文章,基于vc6实现,绕弯很大。在vc7.1、vc8里面要简单很多,主要是把几个以前为公开的类公开了,最重要的是在CWnd里面加入了一个虚函数CreateControlSite使得有机会改变控件站点以修改控件行为。在mfc类层次上,CHTMLView和CDHtmlDialog为开发者提供了创建webgui的一系列基础设施,包括事件机制、窗口行为、以及对html文档操纵接口。我们在此基础上实现webgui很简单,然而仍然困惑我很久,经理也催过我几次我一直未肯决定最终方案。在我脑袋里一直琢磨是要应用程序完全操纵html文档,还是html访问应用获取信息,其实就是它们之间的通信模式。一直到昨天我才定下方案,应用通过IWebBrowser2接口操纵html元素,html通过vbscript、javascript脚本响应本身事件,访问应用。主要是考虑通信自然畅通,而以前我一味想通过应用指令完全控制html元素,导致去解析html文档,费力不讨好。下面开始我的想法:
写一个dll,封装CDHtmlDialog,提供一个类似html容器的对话框,功能就是加载html网页,以及创建与html呼应的com组件。它本身不包含与应用功能有关代码,应用有关的部分是html页面和对于的com功能组件。这里需要对CDHtmlDialog进行了适当的改造以适合自己的目标:
首先从CDHtmlDialog派生一个类CHTMLContainerDlg,默认情况下会生成一个网页资源,这个网页是这个对话框创建时加载的,我们需要的其实是一个容器而不是一个具体的对话框,所以删除网页资源,修改对话框头文件:
这样就可以加载html网页,但是html页面里面的元素风格却是2k风格(至少在ie7以下版本是如此),这个怕是没起到一点美观作用,为之我考虑了半天,问过做web的人是否有办法,最终还是灵感光临,误撞上了。重载GetHostInfo函数:
下面就可以演示了,在vs2005里面找个向导来show一下:
对话框标题其实可以通过解析html文档获取title标题设置,目前还未处理。下面看看html与应用交互的组件。
生成一个atl工程,TestWebCom,添加一个com组件WebComCtrl,添加方法处理上面那个带...的按钮(文件夹浏览按钮):
这里不作具体处理,只是象征性弹出一个对话框。好了,上面我们在对话框里面已经设置了com组件的progid,这里可以把html和组件关联上了,通过脚本可以访问com组件方法:
这个很恼人,用户可没有耐心忍受每次多弹出这个对话框询问组件是否安全。我开始打算将组件实现安全接口解决掉此问题,不过不知道缘何,没有成功,网上搜索一下好像说在ie7里面无效,没办法还是看mfc源码来解决问题。
CDHtmlDialog类获取external代码如下:
这下就好了,按下网页选择文件夹按钮,弹出对话框:
一套流程完备,方案个人觉得不错,各司其职,通信自然畅通,一个html配对一个com功能组件,功能组件化不仅使代码封装性好,而且可以用于多种语言。
由于此技术不用于公司开发,今整理提供下载
其间写过2篇这方面的文章,基于vc6实现,绕弯很大。在vc7.1、vc8里面要简单很多,主要是把几个以前为公开的类公开了,最重要的是在CWnd里面加入了一个虚函数CreateControlSite使得有机会改变控件站点以修改控件行为。在mfc类层次上,CHTMLView和CDHtmlDialog为开发者提供了创建webgui的一系列基础设施,包括事件机制、窗口行为、以及对html文档操纵接口。我们在此基础上实现webgui很简单,然而仍然困惑我很久,经理也催过我几次我一直未肯决定最终方案。在我脑袋里一直琢磨是要应用程序完全操纵html文档,还是html访问应用获取信息,其实就是它们之间的通信模式。一直到昨天我才定下方案,应用通过IWebBrowser2接口操纵html元素,html通过vbscript、javascript脚本响应本身事件,访问应用。主要是考虑通信自然畅通,而以前我一味想通过应用指令完全控制html元素,导致去解析html文档,费力不讨好。下面开始我的想法:
写一个dll,封装CDHtmlDialog,提供一个类似html容器的对话框,功能就是加载html网页,以及创建与html呼应的com组件。它本身不包含与应用功能有关代码,应用有关的部分是html页面和对于的com功能组件。这里需要对CDHtmlDialog进行了适当的改造以适合自己的目标:
首先从CDHtmlDialog派生一个类CHTMLContainerDlg,默认情况下会生成一个网页资源,这个网页是这个对话框创建时加载的,我们需要的其实是一个容器而不是一个具体的对话框,所以删除网页资源,修改对话框头文件:
enum { IDD = IDD_HTMLCONTAINERDLG, IDH = 0 };
这里把IDH修改为0,因为我们删除了网页资源。然而在对话框创建后会加载该资源,在CDHtmlDialog的OnInitDialog函数里面我们可以看到:if (m_nHtmlResID)
LoadFromResource(m_nHtmlResID);
else if (m_szHtmlResID)
LoadFromResource(m_szHtmlResID);
else if (m_strCurrentUrl)
Navigate(m_strCurrentUrl);
结果就是对话框一出现就会出现加载一个无效地址的页面,出现无法打开链接的页面,为了避免这个问题,需要重载OnInitDialog函数。其实就是拷贝mfc代码然后去掉上面那段代码就ok,强制不加载页面。那么为了加载指定页面,需要一个函数:LoadFromResource(m_nHtmlResID);
else if (m_szHtmlResID)
LoadFromResource(m_szHtmlResID);
else if (m_strCurrentUrl)
Navigate(m_strCurrentUrl);
void CHTMLContainerDlg::SetHtmlAndCom(CString strURL, CString strProg)
{
HRESULT hr = NOERROR;
m_strURL = strURL;
hr = m_spComDisp.CoCreateInstance(strProg);
if(FAILED(hr))
{
TRACE(_T("Some error when create com object\n"));
}
SetExternalDispatch(m_spComDisp);
}
指定html的url和对应功能组件的progid,这样在网页里面可以通过脚本window.external访问该com组件。{
HRESULT hr = NOERROR;
m_strURL = strURL;
hr = m_spComDisp.CoCreateInstance(strProg);
if(FAILED(hr))
{
TRACE(_T("Some error when create com object\n"));
}
SetExternalDispatch(m_spComDisp);
}
这样就可以加载html网页,但是html页面里面的元素风格却是2k风格(至少在ie7以下版本是如此),这个怕是没起到一点美观作用,为之我考虑了半天,问过做web的人是否有办法,最终还是灵感光临,误撞上了。重载GetHostInfo函数:
STDMETHODIMP CHTMLContainerDlg::GetHostInfo(DOCHOSTUIINFO* pInfo)
{
pInfo->dwFlags = DOCHOSTUIFLAG_THEME;
return S_OK;
}
这个多得不说,^_^。{
pInfo->dwFlags = DOCHOSTUIFLAG_THEME;
return S_OK;
}
下面就可以演示了,在vs2005里面找个向导来show一下:
CHTMLContainerDlg dlg;
TCHAR szPath[MAX_PATH] = { 0 };
CString strPath;
GetCurrentDirectory(MAX_PATH, szPath);
strPath = szPath;
strPath += _T("\\Default.htm");
dlg.SetHtmlAndCom(strPath, _T("TestWebCom.WebComCtrl"));
dlg.DoModal();
TCHAR szPath[MAX_PATH] = { 0 };
CString strPath;
GetCurrentDirectory(MAX_PATH, szPath);
strPath = szPath;
strPath += _T("\\Default.htm");
dlg.SetHtmlAndCom(strPath, _T("TestWebCom.WebComCtrl"));
dlg.DoModal();
对话框标题其实可以通过解析html文档获取title标题设置,目前还未处理。下面看看html与应用交互的组件。
生成一个atl工程,TestWebCom,添加一个com组件WebComCtrl,添加方法处理上面那个带...的按钮(文件夹浏览按钮):
STDMETHODIMP CWebComCtrl::ShowFolderBrowser(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: 在此添加实现代码
AfxMessageBox(_T("In Com, you can show folder select dialog"));
return S_OK;
}
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: 在此添加实现代码
AfxMessageBox(_T("In Com, you can show folder select dialog"));
return S_OK;
}
这里不作具体处理,只是象征性弹出一个对话框。好了,上面我们在对话框里面已经设置了com组件的progid,这里可以把html和组件关联上了,通过脚本可以访问com组件方法:
<BUTTON CLASS="buttonClass3Custom" ID="BrowseBtn" TYPE="BUTTON" TITLE="浏览头文件。" onClick="OnBrowseHeaderFile();"></BUTTON>
脚本如下:function OnBrowseHeaderFile()
{
window.external.ShowFolderBrowser();
}
下面运行试一试,按下选择文件夹按钮会出现如下询问组件是否安全的对话框:{
window.external.ShowFolderBrowser();
}
这个很恼人,用户可没有耐心忍受每次多弹出这个对话框询问组件是否安全。我开始打算将组件实现安全接口解决掉此问题,不过不知道缘何,没有成功,网上搜索一下好像说在ie7里面无效,没办法还是看mfc源码来解决问题。
CDHtmlDialog类获取external代码如下:
STDMETHODIMP CDHtmlDialog::GetExternal(IDispatch **ppDispatch)
{
if(ppDispatch == NULL)
return E_POINTER;
*ppDispatch = NULL;
if (m_spExternalDisp.p && CanAccessExternal())
{
m_spExternalDisp.p->AddRef();
*ppDispatch = m_spExternalDisp.p;
return S_OK;
}
return E_NOTIMPL;
}
看到CanAccessExternal函数,肯定就是验证安全性的代码,找到函数声明,幸好是虚函数,重载直接返回TRUE:{
if(ppDispatch == NULL)
return E_POINTER;
*ppDispatch = NULL;
if (m_spExternalDisp.p && CanAccessExternal())
{
m_spExternalDisp.p->AddRef();
*ppDispatch = m_spExternalDisp.p;
return S_OK;
}
return E_NOTIMPL;
}
BOOL CHTMLContainerDlg::CanAccessExternal()
{
// we trust all com object (haha, you can make virus)
return TRUE;
}
有兴趣的朋友可以看下内部实现。{
// we trust all com object (haha, you can make virus)
return TRUE;
}
这下就好了,按下网页选择文件夹按钮,弹出对话框:
一套流程完备,方案个人觉得不错,各司其职,通信自然畅通,一个html配对一个com功能组件,功能组件化不仅使代码封装性好,而且可以用于多种语言。
由于此技术不用于公司开发,今整理提供下载
FeedBack:
# re: 用MFC实现WebGUI--(CDHtmlDialog)
2006-12-19 22:40 | noname
的确不错. 用 html 做界面在开发效率, 扩展性等方面都很好. 正巧这两天需要做一个程序. 界面要求比较灵活. 就试试万兄的 idea 了.
用 WTL 做了一第一次做这样的应用, 先不考虑代码复用了. 所以没有做成 DLL. 连页面的 ExternalDispatch 都是在程序内部实现的.
用 WTL 做了一第一次做这样的应用, 先不考虑代码复用了. 所以没有做成 DLL. 连页面的 ExternalDispatch 都是在程序内部实现的.
既然万兄没有给出源码, 我把个 demo. 因为是代码放上来. 算是狗尾续貂把. :)
http://nicoster.googlepages.com/wtlhtml.rar
lieph $(at) 163 $(dot) com 回复 更多评论
http://nicoster.googlepages.com/wtlhtml.rar
lieph $(at) 163 $(dot) com 回复 更多评论
# re: 用MFC实现WebGUI--(CDHtmlDialog)
# re: 用MFC实现WebGUI--(CDHtmlDialog)
2006-12-25 10:51 | shaolong
# re: 用MFC实现WebGUI--(CDHtmlDialog)
# re: 用MFC实现WebGUI--(CDHtmlDialog)
2006-12-26 23:59 | xhl
# re: 用MFC实现WebGUI--(CDHtmlDialog)
2006-12-27 00:01 | xhl
# re: 用MFC实现WebGUI--(CDHtmlDialog)
# re: 用MFC实现WebGUI--(CDHtmlDialog)
# re: 用MFC实现WebGUI--(CDHtmlDialog)
# re: 用MFC实现WebGUI--(CDHtmlDialog)
2007-03-05 22:47 | 万连文
# re: 用MFC实现WebGUI--(CDHtmlDialog)
# re: 用MFC实现WebGUI--(CDHtmlDialog)
# re: 用MFC实现WebGUI--(CDHtmlDialog)
# re: 用MFC实现WebGUI--(CDHtmlDialog)
# re: 用MFC实现WebGUI--(CDHtmlDialog)
# re: 用MFC实现WebGUI--(CDHtmlDialog)
2007-12-03 16:12 | Ason Jia
最近我也在弄这个,但是我有遇见一个问题,因为中间的html大小(不是文件大小,是html的宽度和高度)是会变的,所以我有想动态从html获取大小,具体做法是,html中有一个变量来保存html大小,当html被加载的时候,就由js动态算出大小,然后我们的dialog去获取这个大小(使用IWebBrowser2直接去查询html中此值),然后动态调整dialog的大小以适应html的大小,但是问题也就出在这儿:html在加载的时候,经常会因为一些原因使得DOM中这个element没有创建出来,或者此element有创建出来,但是大小却没有计算出来(也就是此节点有,但是value是“0”),汗~~~~~这个问题我想了很久都解决不了
不知道作者可否提供一些帮助,再次非常感谢~~~ 回复 更多评论
不知道作者可否提供一些帮助,再次非常感谢~~~ 回复 更多评论
# re: 用MFC实现WebGUI--(CDHtmlDialog)
2007-12-04 10:10 | 万连文
# re: 用MFC实现WebGUI--(CDHtmlDialog)
2007-12-04 15:04 | Ason Jia
# re: 用MFC实现WebGUI--(CDHtmlDialog)
2007-12-04 15:14 | 万连文
# re: 用MFC实现WebGUI--(CDHtmlDialog)
2007-12-05 17:38 | Ason Jia
# re: 用MFC实现WebGUI--(CDHtmlDialog)
# re: 用MFC实现WebGUI--(CDHtmlDialog)
2008-10-13 17:58 | 阿里
# re: 用MFC实现WebGUI--(CDHtmlDialog)[未登录]
# re: 用MFC实现WebGUI--(CDHtmlDialog)
2008-11-10 10:28 | 万连文
来自原子时代--您的email有问题???
很抱歉这么晚回复你,因为最近病了。首先,我对一个女孩子从事C++开发表达敬佩。说到界面开发,绝对不会是一个简单的任务,甚至连一本真正的好的技术书籍都没有。CDHtmlDialog是vs2003以后的sdk添加进去的,不过这个用的人也不多,但是简单很多。我一般使用的是atl里面提供的一个窗口类ATLAXWIN_CLASS来实现的,这个在vc6里面也有。根据我的经验,能使用html+css+JS来实现界面编程的人技术都不一般,至少对com有一些了解。对于您目前的情况,如果使用vc6开发可以考虑ATLAXWIN_CLASS这个类进行编程,依赖atl库,跟mfc无关。具体的例子我手头没有,可以查阅www.codeproject.com
www.codeguru.com等技术网站。如果你自己尝试之后还是无法得到结果,请与我联系,我会做一个demo。因为这段时间确实病的不轻,本想顺手做一个给你,但是一看电脑脑袋晕。不好意思。
总是发不出去,再试试. 回复 更多评论
很抱歉这么晚回复你,因为最近病了。首先,我对一个女孩子从事C++开发表达敬佩。说到界面开发,绝对不会是一个简单的任务,甚至连一本真正的好的技术书籍都没有。CDHtmlDialog是vs2003以后的sdk添加进去的,不过这个用的人也不多,但是简单很多。我一般使用的是atl里面提供的一个窗口类ATLAXWIN_CLASS来实现的,这个在vc6里面也有。根据我的经验,能使用html+css+JS来实现界面编程的人技术都不一般,至少对com有一些了解。对于您目前的情况,如果使用vc6开发可以考虑ATLAXWIN_CLASS这个类进行编程,依赖atl库,跟mfc无关。具体的例子我手头没有,可以查阅www.codeproject.com
www.codeguru.com等技术网站。如果你自己尝试之后还是无法得到结果,请与我联系,我会做一个demo。因为这段时间确实病的不轻,本想顺手做一个给你,但是一看电脑脑袋晕。不好意思。
总是发不出去,再试试. 回复 更多评论
# re: 用MFC实现WebGUI--(CDHtmlDialog)[未登录]
# re: 用MFC实现WebGUI--(CDHtmlDialog)
# re: 用MFC实现WebGUI--(CDHtmlDialog)
# re: 用MFC实现WebGUI--(CDHtmlDialog)
2009-12-30 09:45 | Eleanor
# re: 用MFC实现WebGUI--(CDHtmlDialog)
# re: 用MFC实现WebGUI--(CDHtmlDialog)
# re: 用MFC实现WebGUI--(CDHtmlDialog)
# re: 用MFC实现WebGUI--(CDHtmlDialog)
# re: 用MFC实现WebGUI--(CDHtmlDialog)
# re: 用MFC实现WebGUI--(CDHtmlDialog)
# re: 用MFC实现WebGUI--(CDHtmlDialog)
# re: 用MFC实现WebGUI--(CDHtmlDialog)
# re: 用MFC实现WebGUI--(CDHtmlDialog)
# re: 用MFC实现WebGUI--(CDHtmlDialog)