ArcMap下停靠栏的设计与实现

ArcMap 9.3下的UI风格着实比较粗糙,和国内很多软件UI相比汗颜呀,但是ArcMap在GIS编辑上的重要性是不言而喻的,最近想试着深入一下ArcMap的定制。ArcMap的可定制部分,无外乎菜单,工具栏,以及停靠栏,前二者,一般用户可能都接触过,例子也比较多,各种语言和代码都能找到,停靠栏在网上也能找到,VBA和C#比较多,但是C++开发的代码没有,原因是嵌入窗体到停靠栏中不容易实现。

在开发自定义的停靠栏之前,说说停靠栏能干吗用。在ArcMap中,TOC控件就是用得最多的停靠栏,还有质量检查控件也是停靠栏。如果用户交互中涉及的属性与图形显示非常相关,强烈建议采用停靠栏的方式,例如在质检过程,如果非常多的信息需要通过图形关系来判断,打开属性表的方式并不是特别方便,应为属性表和Map容易相互遮盖影响交互,特别是在大数据量的情况下交互非常别扭。

话题涉及到如何开发,很多人在网上问到如何开发,特别是采用C++开发,事实上用C++开发很不容易,因为你需要精确的控制非模式化窗口的每个过程,从创建到销毁都需要手工控制。

因为9.3的后续版本下(10.0,10.1),ArcMap的风格完全改变到了,风格类似office2007,可能一定程度上会打消9.3下一部分用户的开发需求。但是接触了很多用户后,我发现大部分用户,仍然习惯9.3,而且9.3的用户也巨大。

开始停靠栏的开发,我们需要准备好开发环境,vc6,ArcMap9.3。如果熟悉ATL更好。

1、准备一个测试用例,我做得很简单,就是一个Command用于**停靠栏

STDMETHOD(OnClick)() { HRESULT hr; IDockableWindowManagerPtr ipDockableWindowManager; ipDockableWindowManager=m_ipDispatch; IUIDPtr ipUID(CLSID_UID); hr=ipUID->put_Value(CComVariant("{F884D1F0-C82F-40F1-A2EB-359D51F635F7}")); IDockableWindowPtr ipDockableWindow; //实例化停靠栏组件 hr=ipDockableWindowManager->GetDockableWindow(ipUID,&ipDockableWindow); VARIANT_BOOL varVisual; hr=ipDockableWindow->IsVisible(&varVisual); //if (varVisual==VARIANT_TRUE) { //得到当前停靠栏的大小 IWindowPositionPtr ipWindowPosition; ipWindowPosition=ipDockableWindow; long width; long height; hr=ipDockableWindow->Show(VARIANT_TRUE); hr=ipDockableWindow->Dock(esriDockBottom); hr=ipWindowPosition->get_Width(&width); hr=ipWindowPosition->get_Height(&height); } return S_OK; }

2、扩展IDockableWindowDef

STDMETHOD(OnCreate)(IDispatch * hook) { //传递IApplication; HRESULT hr; IApplicationPtr ipApplication; ipApplication=hook; OLE_HANDLE AppOLEHandle; hr=ipApplication->get_hWnd(&AppOLEHandle); HWND hwnd=(HWND)AppOLEHandle; m_TableForm.Create(hwnd,(LPARAM)(hook)); // m_TableForm.Create(hwnd); ATLTRACE("\n***新建窗口\n"); return S_OK; }

3、建立自己的窗口组件

ArcMap下停靠栏的设计与实现

4、添加自己的属性列表

ArcMap下停靠栏的设计与实现

属性列表是放在属性页中的,属性页被嵌入到每个Tab control中,

LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ m_ipDispatch=(IDispatch * )lParam; if(m_ipDispatch==NULL) return 0; USES_CONVERSION; HRESULT hr; //添加tab页 //path From dll TCHAR szFullPathName[_MAX_PATH]; GetModuleFileName((HMODULE)(_Module.m_hInst),szFullPathName,_MAX_PATH); TCHAR drive[_MAX_DRIVE]; TCHAR dir[_MAX_DIR]; TCHAR fname[_MAX_FNAME]; TCHAR ext[_MAX_EXT]; _wsplitpath(szFullPathName, drive, dir, fname,ext); TCHAR szPathOfMdb[_MAX_PATH]; wcscpy(szPathOfMdb,drive); wcscat(szPathOfMdb,dir); wcscat(szPathOfMdb,_T("diyingli.mdb")); CComBSTR bstrPathOfmdb=szPathOfMdb; //Open mdb IWorkspaceFactoryPtr ipWSF(CLSID_AccessWorkspaceFactory); IWorkspacePtr ipWorkspace; hr=ipWSF->OpenFromFile(bstrPathOfmdb,NULL,&ipWorkspace); if (FAILED(hr)) { ::MessageBox(NULL,_T("没有找到地应力数据库"),_T("错误"),MB_ICONERROR); return 1; } //打开数据库加载空间数据表格 //esriDTTable IStringArrayPtr ipStringArray(CLSID_StrArray); IEnumDatasetNamePtr ipEnumDatasetName; hr=ipWorkspace->get_DatasetNames(esriDTTable,&ipEnumDatasetName); hr=ipEnumDatasetName->Reset(); IDatasetNamePtr ipDatasetName; while (!ipEnumDatasetName->Next(&ipDatasetName)) { CComBSTR bstrName; hr=ipDatasetName->get_Name(&bstrName); hr=ipStringArray->Add(bstrName); } long lStrCount; hr=ipStringArray->get_Count(&lStrCount); HWND hwnd=GetDlgItem(IDC_TAB1); //Get Name and table Info from gdb for (long i=0;i<lStrCount;i++) { CComBSTR bstrItemName; hr=ipStringArray->get_Element(i,&bstrItemName); TCITEM tci; tci.mask = TCIF_TEXT; tci.pszText =OLE2T(bstrItemName); tci.cchTextMax = wcslen(OLE2T(bstrItemName)) + 1; tci.iImage = -1; tci.lParam = 0; TabCtrl_InsertItem(hwnd,i,&tci); //Add table to tab control ITablePtr ipTable; IFeatureWorkspacePtr ipFWS; ipFWS=ipWorkspace; hr=ipFWS->OpenTable(bstrItemName,&ipTable); //添加属性页 AddTableToTabControl(ipTable,hwnd); } //发送消息TCN_SELCHANGE // 设置Tab第一项属性页为可显示 NMHDR nmhdr; nmhdr.code = TCN_SELCHANGE; nmhdr.hwndFrom = hwnd; nmhdr.idFrom=IDC_TAB1; ::SendMessage(hwnd,WM_NOTIFY,MAKELONG(TCN_SELCHANGE,0),(LPARAM)(&nmhdr)); return 1; // Let the system set the focus}

最后的效果就是下面的图了。
ArcMap下停靠栏的设计与实现