聚類算法演示程序-2


程序框架搭建

上一篇文章提到了使用的BCG版本是BCGControlBar Pro25.10,安裝過程中會識別安裝的VC版本瞒瘸,自動安裝相應的Wizard模板用來輔助生成應用程序。


BCG Application 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)的。具體包括以下幾個步驟

  1. 分別為兩種編輯界面從基類繼承各自的字框架窗口類
class CChildFrame : public CBCGPMDIChildWnd   //列表編輯界面子框架窗口類
class CChildFrameWithRuler : public CBCGPMDIChildWnd  //繪圖編輯界面子框架窗口類
  1. 在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;
  1. 響應切換按鈕事件
    將兩個按鈕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é)就比較模糊了,還得翻來覆去的琢磨自己寫的代碼。哈哈犹菇,溫故而知新德迹!

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市项栏,隨后出現(xiàn)的幾起案子浦辨,更是在濱河造成了極大的恐慌,老刑警劉巖沼沈,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件流酬,死亡現(xiàn)場離奇詭異,居然都是意外死亡列另,警方通過查閱死者的電腦和手機芽腾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來页衙,“玉大人摊滔,你說我怎么就攤上這事〉昀郑” “怎么了艰躺?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長眨八。 經(jīng)常有香客問我腺兴,道長,這世上最難降的妖魔是什么廉侧? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任页响,我火速辦了婚禮,結(jié)果婚禮上段誊,老公的妹妹穿的比我還像新娘闰蚕。我一直安慰自己,他們只是感情好连舍,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布没陡。 她就那樣靜靜地躺著,像睡著了一般索赏。 火紅的嫁衣襯著肌膚如雪诗鸭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天参滴,我揣著相機與錄音强岸,去河邊找鬼。 笑死砾赔,一個胖子當著我的面吹牛蝌箍,可吹牛的內(nèi)容都是我干的青灼。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼妓盲,長吁一口氣:“原來是場噩夢啊……” “哼杂拨!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起悯衬,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤弹沽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后筋粗,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體策橘,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年娜亿,在試婚紗的時候發(fā)現(xiàn)自己被綠了丽已。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡买决,死狀恐怖沛婴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情督赤,我是刑警寧澤嘁灯,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站躲舌,受9級特大地震影響丑婿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜孽糖,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一枯冈、第九天 我趴在偏房一處隱蔽的房頂上張望毅贮。 院中可真熱鬧办悟,春花似錦、人聲如沸滩褥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瑰煎。三九已至铺然,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間酒甸,已是汗流浹背魄健。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留插勤,地道東北人沽瘦。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓革骨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親析恋。 傳聞我的和親對象是個殘疾皇子良哲,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容