说实话,这几天为了CMFCToolBarComboBoxButton上的莫名其妙自动切换选择的bug烦死了,浪费了很多时间。也是过其它替代方法,发现都实现不了需要的界面效果。
我也不知道为什么自己对界面效果这么纠结。伤不起啊。本来我就没用过vs2008更新版本的MFC,也没打算用这个。由于上一次做的东西用到了切分视图,但是发现FormView自适应控件大小的实现非常麻烦,老是出现些不爽的bug,而且添加新的控件还得编辑界面,非常不爽。所以,打算使用新版本MFC里面的属性窗口。
刚好发现vs2010的项目导航可以生成这样的工程,果断试验之。虽然过程无比艰辛,总算可以使用这些自适应的DockPane窗口了。而且还发现这样的结构非常合适添加子窗口,以后的项目都可以采用这样的界面了,只要平台在windows下。
但是,美中最不足的是CFrameWndEx(使用CDockablePane必须使用扩展的框架窗口),只能使用恶心的CMFCToolBar,因为CFrameWndEx的菜单和工具栏都是可以DockPane的子类。我只能说CMFCToolBar的使用太TMD的讲究了,一个不注意效果就不对。因为使用这个东西的人少,网上资料也很少。只能一直google,还有查看类定义的源码,慢慢尝试了。
现在,我这里说的只是添加组合框。
首先,在资源编辑器中新建工具栏IDR_BUILD,留下一个空位,如ID_BUILD_CHOOSE。如下图,第二个就是我预留的ID_BUILD_CHOOSE的空位。
然后,在CMainFrame::OnCreate中创建两个工具栏,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) || !m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) { TRACE0("未能创建工具栏\n"); return -1; }
if (!m_wndToolBarBuild.Create(this, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_HIDE_INPLACE | CBRS_SIZE_DYNAMIC| CBRS_GRIPPER | CBRS_BORDER_3D, IDC_MFCTOOLBAR_BUILD) || !m_wndToolBarBuild.LoadToolBar(IDR_BUILD)) { TRACE0("未能创建build工具栏\n"); return -1; }
if (!m_wndStatusBar.Create(this)) { TRACE0("未能创建状态栏\n"); return -1; } m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT));
m_wndMenuBar.EnableDocking(CBRS_ALIGN_ANY); m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY); m_wndToolBarBuild.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY); DockPane(m_wndMenuBar); DockPane(m_wndToolBarBuild); DockPaneLeftOf(m_wndToolBar, m_wndToolBarBuild);
|
注意,创建第二个工具栏的时候需要使用Create而不是CreateEx,如果使用CreateEx就不能使用后面的ID参数。
现在需要实现三个消息。
1 2 3
| ON_REGISTERED_MESSAGE(AFX_WM_RESETTOOLBAR, CMainFrame::OnToolbarReset) ON_COMMAND(ID_BUILD_CHOOSE, CMainFrame::OnClickChoose) ON_CBN_SELCHANGE(ID_BUILD_CHOOSE, CMainFrame::OnSelChangeClick)
|
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| LRESULT CMainFrame::OnToolbarReset(WPARAM wParam, LPARAM lParam) { UINT uiToolBarId = (UINT)wParam;
switch (uiToolBarId) { case IDR_MAINFRAME: break;
case IDR_BUILD: { CMFCToolBarComboBoxButton comboButton(ID_BUILD_CHOOSE, GetCmdMgr()->GetCmdImage(ID_BUILD_CHOOSE, FALSE), CBS_DROPDOWN); comboButton.EnableWindow(TRUE); comboButton.SetCenterVert(); comboButton.SetFlatMode(); comboButton.AddItem(_T("Example Stack")); comboButton.AddItem(_T("Upsample Pyramid")); comboButton.AddItem(_T("Jitter Pyramid")); comboButton.AddItem(_T("Correct Pyramid")); comboButton.SelectItem(0); m_wndToolBarBuild.ReplaceButton(ID_BUILD_CHOOSE, comboButton); } break;
default: break; }
return 0; }
void CMainFrame::OnClickChoose() {
}
void CMainFrame::OnSelChangeClick() { CMFCToolBarComboBoxButton* pCombo = CMFCToolBarComboBoxButton::GetByCmd(ID_BUILD_CHOOSE, TRUE);
int nIndex = pCombo->GetCurSel(); theAppState.ts.m_nDisplayMode = nIndex; theAppState.ts.UpdateDisplayWnd(); }
|
OnToolbarReset中实现的是添加组合框,根据MSDN的文档,应该采用这样的方法,当然也可以采用CMFCToolBar的protected方法InsertButton,不过需要把这个方法改成public的,这个就需要修改类定义的源码。必须响应ID_BUILD_CHOOSE的点击消息,否则组合框是灰的,无法点击。
OnSelChangeClick响应的是组合框选择变化的消息,这个消息的实现必须用 CMFCToolBarComboBoxButton::GetByCmd获得组合框的指针,否则得不到正确的效果。
最后要讲的是我碰到的一个bug,恶心了我一周了。我在工具栏上面定义了向前和向后操作,如果我一直点击向前或者向后按钮,组合框会自动切换选择到最后一项。我调试了很久,发现跟我的算法实现代码一点关系都没有,我什么都不做,操作界面就会出现这个bug。最后,我偶然发现修改CMFCToolBarComboBoxButton comboButton(ID_BUILD_CHOOSE, GetCmdMgr()->GetCmdImage(ID_BUILD_CHOOSE, FALSE), CBS_DROPDOWNLIST);的最后一个参数为CBS_DROPDOWN就不会出现这样的bug。只是有一个不爽的地方,那就是点击组合框的时候,会出现光标而已。勉强接受了吧。