程序框架搭建
上一篇文章提到了使用的BCG版本是BCGControlBar Pro25.10,安裝過程中會識別安裝的VC版本瞒瘸,自動安裝相應的Wizard模板用來輔助生成應用程序。
一路點“下一步”就可以生成一個非常類似VisualStudio界面的多文檔應用程序谁尸。程序的界面風格有十幾種效果可以切換箱沦,還自動生成了Workspace抚岗、Property扒寄、Output陀愎模靠欄,透帽啵靠效果非称荆酷炫,另外菜單欄和工具條不僅可以涂慰ⅲ靠還可以定制嘉赎。
與VC標準Wizard生成代碼相比,BCG生成的代碼主要的變化包括:CWinApp變成了CBCGPWinApp于樟,CMDIFrameWnd變成了CBCGPMDIFrameWnd公条。其中App類在構造函數(shù)初始化界面風格菜單和工具條右鍵彈出菜單,在InitInstance中設置了程序外觀設置存放的注冊表目錄迂曲。Mainframe類在OnCreate中創(chuàng)建各個桶谐鳎靠欄、菜單欄和工具條路捧。
一點兒微調(diào)
對于本文設計目標這樣的小程序关霸,菜單和工具條都可以拖動和定制,看起來比較別扭杰扫,要想辦法把它去掉队寇。
- 去掉菜單和工具條拖動功能:
注釋掉OnCreate中這兩行
//m_wndMenuBar.EnableDocking(CBRS_ALIGN_ANY);
//m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
- 去掉工具條定制功能
注釋掉App類構造函數(shù)中這行
//SetToolbarOptions(toolbarOptions);
去掉后變成這樣,順眼多了
菜單和工具條
接下來要做的事就是根據(jù)軟件功能安排菜單和工具條對應的命令章姓。
父菜單 | 子菜單ID | 功能 |
---|---|---|
Project | ID_FILE_NEW | 新建項目文件 |
Project | ID_FILE_OPEN | 打開項目文件 |
Project | ID_FILE_CLOSE | 關閉項目文件 |
Project | ID_FILE_SAVE | 保存項目文件 |
Project | ID_FILE_SAVE_AS | 另存項目文件 |
Project | ID_FILE_NEW | 新建項目文件 |
Edit | ID_EDIT_DRAW | 切換到繪圖編輯模式 |
Edit | ID_EDIT_LIST | 切換到表格編輯模式 |
Edit | ID_EDIT_ARROW | 繪圖模式箭頭工具 |
Edit | ID_EDIT_PEN | 繪圖模式畫筆工具 |
Edit | ID_EDIT_DRAWFILL | 繪圖模式范圍填充工具 |
Edit | ID_EDIT_HAND | 繪圖模式手形拖拽工具 |
Edit | ID_EDIT_DELETE | 繪圖模式刪除工具 |
Edit | ID_EDIT_COPY | 繪圖模式復制工具 |
Edit | ID_EDIT_ZOOMIN | 繪圖模式視圖放大工具 |
Edit | ID_EDIT_ZOOMOUT | 繪圖模式視圖縮小工具 |
Edit | ID_EDIT_FITSIZE | 繪圖模式設置畫布范圍工具 |
Edit | ID_EDIT_INSERTROW | 表格模式插入行 |
Edit | ID_EDIT_DELETEROW | 表格模式刪除行 |
Edit | ID_EDIT_FILL | 表格模式填充工具 |
Edit | ID_EDIT_EXPORT | 表格模式數(shù)據(jù)導出 |
Edit | ID_EDIT_IMPORT | 表格模式數(shù)據(jù)導入 |
Edit | ID_EDIT_SORT | 表格模式排序工具 |
Run | ID_RUN_RUN | 運行演示 |
添加好菜單命令后就改修改工具條了佳遣。由于BCG的工具條加載256色的圖片資源,在VC的編輯器中不方便處理凡伊,BCG提供了一個工具條編輯器苍日,很好用,可以直接打開VC的工程文件修改其中的工具條資源窗声。
這樣做好的工具條就是這樣的:
其中奔跑小人后面的空白按鈕用來放置選擇算法的下拉選擇框相恃。
BCG提供了一系列的工具條按鈕控件類供用戶在工具條上創(chuàng)建控件,包括下拉選擇笨觅、字體/顏色選擇控件等等拦耐。下圖是工具條按鈕控件類的種類和繼承層次。
在工具條上創(chuàng)建控件之需要兩步见剩。首先杀糯,在編輯工具條時給控件留下一個空白位置。接下來苍苞,在OnToolbarReset處理程序中構造一個按鈕控件(本例中是CBCGPToolbarComboBoxButton)固翰,調(diào)用CBCGToolBar::ReplaceButton將控件復制到工具條上去狼纬。
const int nComboboxWidth = globalUtils.ScaleByDPI(100);
CBCGPToolbarComboBoxButton comboButton(ID_RUN_ALGORITHM_COMBO,
#ifdef _BCGSUITE_INC_
GetCmdMgr()->GetCmdImage(ID_RUN_ALGORITHM_COMBO, FALSE),
#else
CImageHash::GetImageOfCommand(ID_RUN_ALGORITHM_COMBO, FALSE),
#endif
CBS_DROPDOWNLIST, nComboboxWidth);
comboButton.AddItem(_T("K-means"));
comboButton.AddItem(_T("DBSCAN"));
comboButton.SelectItem(0);
int iRet = m_wndToolBar.ReplaceButton(ID_RUN_ALGORITHM_COMBO, comboButton);
如果需要,在MainFrame中可以通過ON_CBN_SELENDOK(ID_RUN_ALGORITHM_COMBO, OnAlgorithmCombo)
來響應控件的事件骂际,還可以通過如下代碼取得控件實例并訪問其屬性:
CBCGPToolbarComboBoxButton* pCombobox = DYNAMIC_DOWNCAST(CBCGPToolbarComboBoxButton,
m_wndToolBar.GetButton(m_wndToolBar.CommandToIndex(ID_RUN_ALGORITHM_COMBO)));
ASSERT_VALID(pCombobox);
int iSel = pCombobox->GetCurSel(); //獲取當前選擇項序號
最終制作好的菜單和工具條就是這樣的
為了讓界面顯得緊湊些疗琉,特意在兩種編輯方式切換時隱藏用不到的按鈕,所以上圖在下拉選擇控件后面顯示的只是繪圖編輯方式用到的按鈕歉铝。
切換編輯方式實現(xiàn)
在前面討論程序結(jié)構時確定了本例程序采用多文檔結(jié)構盈简,這樣兩種編輯界面的切換是通過切換各自的字框架窗口的顯示隱藏來實現(xiàn)的。具體包括以下幾個步驟
- 分別為兩種編輯界面從基類繼承各自的字框架窗口類
class CChildFrame : public CBCGPMDIChildWnd //列表編輯界面子框架窗口類
class CChildFrameWithRuler : public CBCGPMDIChildWnd //繪圖編輯界面子框架窗口類
- 在App::InitInstance中定義兩個文檔模板
m_pTemplateDrawView = new CMultiDocTemplate(IDR_ClusterMDITYPE,
RUNTIME_CLASS(CClusterMDIDoc),
//RUNTIME_CLASS(CChildFrame), // custom MDI child frame
RUNTIME_CLASS(CChildFrameWithRuler), // custom MDI child frame
RUNTIME_CLASS(CClusterMDIView));
if (!m_pTemplateDrawView)
return FALSE;
AddDocTemplate(m_pTemplateDrawView);
m_pTemplateGridView = new CMultiDocTemplate(IDR_ClusterMDITYPE,
RUNTIME_CLASS(CClusterMDIDoc),
RUNTIME_CLASS(CChildFrame), // custom MDI child frame
RUNTIME_CLASS(CGridView));
if (!m_pTemplateGridView)
return FALSE;
- 響應切換按鈕事件
將兩個按鈕ID的值調(diào)整成兩個緊挨著的整數(shù)值太示,就可以用范圍影射命令將兩個按鈕的事件傳遞到同一個消息處理函數(shù)柠贤。
ON_COMMAND_RANGE(ID_EDIT_DRAW, ID_EDIT_LIST, OnDrawOrList)
在切換處理函數(shù)中首先根據(jù)傳入的ID判斷切換的目標,然后判斷當前View是否是切換目標类缤,如果是則啥也不用做了臼勉,否則準備進行切換。
決定要進行切換之后餐弱,要先判斷切換目標框架是否已經(jīng)創(chuàng)建了宴霸。因為程序第一次顯示時只創(chuàng)建了缺省編輯界面的子框架窗口(當前是先顯示繪圖界面),另一個子框架待第一次切換到它時才創(chuàng)建的岸裙。
如果切換目標框架已創(chuàng)建,只需要將它激活并顯示出來就可以速缆,否則還需要調(diào)用App里存放的文檔模板的CreateNewFrame函數(shù)來創(chuàng)建一個新的目標框架窗口降允。
void CMainFrame::OnDrawOrList(UINT nID)
{
CMDIChildWnd* pActiveFrame = MDIGetActive();
if (!pActiveFrame)
return;
CView* pCurView = pActiveFrame->GetActiveView();
CDocument* pDoc = pActiveFrame->GetActiveDocument();
CFrameWnd* pCurFrame = pCurView->GetParentFrame();
CRuntimeClass *pTargetViewClass = NULL;
CMultiDocTemplate* pTargetDocTemplate = NULL;
switch (nID)
{
case ID_EDIT_DRAW:
pTargetViewClass = RUNTIME_CLASS(CClusterMDIView);
pTargetDocTemplate = theApp.m_pTemplateDrawView;
break;
case ID_EDIT_LIST:
pTargetViewClass = RUNTIME_CLASS(CGridView);
pTargetDocTemplate = theApp.m_pTemplateGridView;
break;
}
if (pCurView->IsKindOf(pTargetViewClass))
{
// ok!
return;
}
CMDIChildWnd *pTargetFrame = NULL;
CView* pTargetView = NULL;
POSITION pos = pDoc->GetFirstViewPosition();
while (pos != NULL)
{
pTargetView = pDoc->GetNextView(pos);
if (pTargetView->IsKindOf(pTargetViewClass))
{
pTargetFrame = (CMDIChildWnd*)pTargetView->GetParentFrame();
break;
}
}
if (!pTargetFrame)
{
pTargetFrame = (CMDIChildWnd*)pTargetDocTemplate->CreateNewFrame(pDoc,NULL);
pTargetDocTemplate->InitialUpdateFrame(pTargetFrame, pDoc);
}
else
{
// 禁止刷新,否則切換時能看到子窗口先顯示出來艺糜,然后再填滿整個框架
::SendMessage(m_hWndMDIClient, WM_SETREDRAW, 0, 0);
pTargetFrame->MDIActivate();
pTargetFrame->ShowWindow(SW_MAXIMIZE);
::SendMessage(m_hWndMDIClient, WM_SETREDRAW, 1, 0);
::RedrawWindow(m_hWndMDIClient, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
if(pTargetView->IsKindOf(RUNTIME_CLASS(CClusterMDIView)))
{
// 繪圖界面切換過來后需要初始化它的工具狀態(tài)
// 并且由于在列表編輯狀態(tài)可能添加了新的數(shù)據(jù)點剧董,
// 有可能超出它當前的顯示范圍
// 在這里進行處理
CClusterMDIView* pClusterView = DYNAMIC_DOWNCAST(CClusterMDIView, pTargetView);
ASSERT_VALID(pClusterView);
pClusterView->ClearViewBeforSwitch();
}
}
}
上面代碼中注釋禁止刷新的地方很重要,否則切換時閃爍的利害破停,很不美觀翅楼,肉眼能看到子窗口先在主框架中顯示出來然后放大填滿。所以要先將要顯示的窗口刷新禁止真慢,然后激活它毅臊,等它準備好后再讓它重畫一遍。
最后黑界,為了讓這兩個切換按鈕在當前編輯狀態(tài)時保持按下狀態(tài)管嬉,響應它們的UPDATE_COMMAND_UI消息,根據(jù)當前顯示狀態(tài)設置各自check狀態(tài)朗鸠。
小結(jié)
程序結(jié)構搭建完畢蚯撩,接下來就是介紹具體編輯和演示功能的實現(xiàn)。
好記心不如爛筆頭烛占,只不過過了幾個星期胎挎,寫上面的東西時一些細節(jié)就比較模糊了,還得翻來覆去的琢磨自己寫的代碼。哈哈犹菇,溫故而知新德迹!