分类: C/C++
2009-06-28 13:07:01
对于我们使用ArcEngine开发,最常用的控件就是地图控件了。这个是一个ActiveX控件。但是,如果问如何创建MapControl控件?这个问题看起来不值得问。因为太简单了啊,凡是写过几行代码的人都会,不就是在窗体上拖一个控件摆上嘛。
其实啊,我不这样想。我觉得,对于拖一个控件到窗体上的方法,有一个弊端。那就是万一当我们的程序放到忘记安装ArcEngine运行时的用户电脑上,运行程序时候,效果是直接出来一个醒目的感叹号,死的非常难看。
当然,这样的情况应该不会经常遇到的。不过,从程序的健壮,稳定性,用户感受的角度来说,还是避免那个难看的错误为好。如果我们的程序一打开,判断出了差错,马上友好告诉用户,“哈嘿,您的电脑还要安装ArcEngine运行时才行呢”。你看,这多好啊!
有没有办法避免?能避免。因为我们程序员是聪明的,办法总是能找到啊。
办法一,是在运行时候提前检查系统能否运行你的程序,根据情况做相应的处理。这个怎么检查呢?我觉得,办法是查看注册表。这个方法有点难度,因为你现在知道ArcEngine9.0注册表是这样,万一到9.4版本时候,ArcEngine在注册表存储的方式变化了呢? 没有办法,打开注册表看看ArcEngine信息具体是怎么存储的,更改程序。
办法二,运行时候创建MapControl控件。我个人觉得该方法很好。好处是,不用理会注册表内容。由于控件是在运行时候才创建的,如果创建不成功,基本上肯定当前系统没有安装ArcEngine运行时了。这个时候提示用户,然后结束运行。
我们要怎么办,才能在运行时候创建这个控件呢?目前我知道方法有两个:
· 用VC的向导事先生成一个控件类“CMapControl”
class CMapControl : public CWnd
{
protected:
DECLARE_DYNCREATE(CMapControl)
public:
CLSID const& GetClsid()
{
static CLSID const clsid
= { 0xC552EA94, 0x6FBB, 0x11D5, { 0xA9, 0xC1, 0x0, 0x10, 0x4B, 0xB6, 0xFC, 0x1C } };
return clsid;
}
virtual BOOL Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle,
const RECT& rect, CWnd* pParentWnd, UINT nID,
CCreateContext* pContext = NULL)
{
return CreateControl(GetClsid(), lpszWindowName, dwStyle, rect, pParentWnd, nID);
}
BOOL Create(LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd,
UINT nID, CFile* pPersist = NULL, BOOL bStorage = FALSE,
BSTR bstrLicKey = NULL)
{
return CreateControl(GetClsid(), lpszWindowName, dwStyle, rect, pParentWnd, nID,
pPersist, bStorage, bstrLicKey);
}
// 属性
public:
省略
};
有了上述的类定义,那么创建的方法很简单了:
if (!m_wndMapCtrl9.Create(NULL, WS_CHILD|WS_VISIBLE, CRect(0,0,30,30), pParent, MapControlID))
{
AfxMessageBox(strError, MB_OK | MB_ICONSTOP);
return FALSE;
}
· 方法二。方法一看起来很好,也没有什么问题,也确实没有什么问题。不过,ArcEngine9.0和ArcEngine9.2生成的类函数不一样了,也就是说,不同版本的函数定义有差异,没有办法通用了。这个就是采用高度封装带来便利的同时,也带来了弊端。我们有办法解决这个问题吗?答案是有的。我的办法是不用上述的类了。我先定义一个窗体对象(这里采用WTL的实现,其实用MFC也是一样的)和一个地图控件接口:
IMapControl2Ptr m_ipMapControl2;
CAxWindow m_wndMapControl;
然后,接下来需要做工作的是创建一个容纳地图控件的窗体:
CRect rcClient;
GetClientRect(&rcClient);
HWND hWnd = m_wndMapControl.Create(*this, rcClient, _T(""), WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN, 0, IDC_MAPCONTROL1);
if (NULL == hWnd)
{
::MessageBox(::GetActiveWindow(), _T("未知错误,无法创建MapControl控件容器。"), _T("严重错误"), MB_OK | MB_ICONSTOP);
return FALSE;
}
窗体有了,那么接着的工作就是得到地图控件的接口:
CComPtr<IUnknown> punkCtrl = NULL;
HRESULT hr = m_wndMapControl.CreateControlEx(L"{C552EA94-6FBB-11D5-A9C1-00104BB6FC1C}", NULL, NULL, &punkCtrl );
if (FAILED(hr) || NULL == punkCtrl)
{
::MessageBox(::GetActiveWindow(), _T("当前系统没有安装ArcEngine环境,无法创建MapControl控件。"), _T("严重错误"), MB_OK | MB_ICONSTOP);
return FALSE;
}
hr = m_wndMapControl.QueryControl(IID_IMapControl2, (void**)&m_ipMapControl2);
看,到了这里,动态创建的步骤结束了,一个ActiveX控件就出来了。每个步骤都是可以控制的,随时知道问题出在那里。程序运行过程不会出现难看的感叹号了。
上述方式解决了动态创建的问题,紧接着的问题是,我们怎么监听到地图控件的事件呢?比如鼠标按下、移动等等? 这个问题其实很简单的,去这里参见我的文章 http://blog.chinaunix.net/u2/79895/showart_1954500.html。