前一段时间写一个程序,其中涉及到在工具栏上创建一个滑块控件(功能大致是通过拖动滑块控件拖动客户区的一幅bmp图像)。从中我认识到MFC滑块控件的一个设计缺陷:它并没有足够的消息宏来处理它的一些动作。使用过滑块控件的朋友都有这样的经验:当用户拖动滑块时,滑动条控制将向其父窗口发送WM_HSCROLL消息,所以这个消息处理函数是在应用程序中重栽父窗口的OnHScroll()函数。在基于对话框的程序估计体会不到它的设计失误,因为一般来说对话框是滑块控件的父窗口嘛,直接重载OnHScroll()函数得了。但是当你在基于单文档或多文档的程序的工具栏上创建一个滑块控件和其它类(如视图类)进行交互时,你就会发现滑块控件的设计缺陷——把滑块控件的滑动消息交给父窗口进行处理是不明智的,因为它会限制滑块控件和其它类的交互。因为滑块控件的父窗口是某一工具栏,因为必须在该工具栏上重载OnHScroll()函数。这样滑块控件和视图的交流代码就放在这里了。这意味着滑块控件在滑动时和视图的交互必须通过它的父窗口。如果工具栏类和视图类是在同一个工程里,似乎也没什么。要命的是我碰到了这样一种情况:工具栏类是早已在另一个工程封装好了,是一个底层基类,不能派生,而且只能加载这种工具栏对象,框架加载不了自己新建的工具栏类对象。当然公司的这个工具栏基类不能派生就不是一种好的设计。但是不管怎么说,滑块控件和其它类交互必须通过它的父窗口本身就不是一种好的设计。
实际上滑块控件是有自己的消息处理的,如NM_RELEASEDCAPTURE消息。测试程序:
1. 打开MainFrm.cpp 添加创建代码:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
......
// TODO: Delete these three lines if you don't want the toolbar to
// be dockable
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);
//-------------------------------------------------------------------
// 下面是添加的代码 int index = m_wndToolBar.CommandToIndex(IDC_SLIDER);
m_wndToolBar.SetButtonInfo(index, IDC_SLIDER, TBBS_SEPARATOR, 80);
CRect rect;
m_wndToolBar.GetItemRect(index, &rect); // 创建滑动条并显示
if (!m_Slider.Create(WS_CHILD|WS_VISIBLE | TBS_HORZ | TBS_AUTOTICKS |TBS_BOTTOM ,
rect, &m_wndToolBar, IDC_SLIDER))
{
TRACE0("Failed to create slider ctrl\n");
return FALSE;
}
//设置滑动的范围
m_Slider.SetRange(0,100);
m_Slider.SetPos(20);
m_Slider.ShowWindow(SW_SHOW);
//-------------------------------------------------------------------
return 0;
}
2. 打开视类CPP文件,添加如下的代码:
......
#include "mainfrm.h" //包含框架类的头文件
......
BEGIN_MESSAGE_MAP(CTbSliderView, CView)
......
ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER, OnReleasedcaptureSlider)
END_MESSAGE_MAP()//当用鼠标调整滑动条的位置,释放鼠标以后,滑动条会发送这个NM_RELEASEDCAPTURE消息
/
// 响应函数
void CTbSliderView::OnReleasedcaptureSlider()
{
CMainFrame * pMain = (CMainFrame*)AfxGetMainWnd();
int pos = pMain->m_Slider.GetPos();CString str;
Str.format(“%d”, pos)
AfxMessage(str) ; // 显示滑块控件的当前滑动到达的位置}
我查了一下MFC的头文件,滑块控件可能可以响应下列消息:
#define NM_OUTOFMEMORY (NM_FIRST-1)
#define NM_CLICK (NM_FIRST-2) // uses NMCLICK struct
#define NM_DBLCLK (NM_FIRST-3)
#define NM_RETURN (NM_FIRST-4)
#define NM_RCLICK (NM_FIRST-5) // uses NMCLICK struct
#define NM_RDBLCLK (NM_FIRST-6)
#define NM_SETFOCUS (NM_FIRST-7)
#define NM_KILLFOCUS (NM_FIRST-8)
#if (_WIN32_IE >= 0x0300)
#define NM_CUSTOMDRAW (NM_FIRST-12)
#define NM_HOVER (NM_FIRST-13)
#endif
#if (_WIN32_IE >= 0x0400)
#define NM_NCHITTEST (NM_FIRST-14) // uses NMMOUSE struct
#define NM_KEYDOWN (NM_FIRST-15) // uses NMKEY struct
#define NM_RELEASEDCAPTURE (NM_FIRST-16)
#define NM_SETCURSOR (NM_FIRST-17) // uses NMMOUSE struct
#define NM_CHAR (NM_FIRST-18) // uses NMCHAR struct
#endif
但是当我用程序进行测试时,发现只有
NM_RELEASEDCAPTURE和NM_CUSTOMDRAW两个消息可以响应。
开始我以为滑动消息是NM_NCHITTEST,但是我实在搞不清uses NMMOUSE struct是什么意思?该怎么用NMMOUSE呢?当我用google和百度搜索响应滑块控件滑动消息时,我所看到的都是说在父窗口重载OnHScroll()函数。我也不清楚这是不是因为大家都不清楚怎么响应滑块控件的其它消息。
这样滑块控件似乎给人一种半成品的感觉。滑块控件为何不能设计成像combox控件和Edit控件那样有完备的消息。这样设计多好,比如combox控件,想和框架类交互,在框架类添加一个映射宏:ON_CBN_SELCHANGE((id),(memberFxn)),想和视图类交互,在视图类里添加一个映射宏:ON_CBN_SELCHANGE((id),(memberFxn)),而滑块控件的滑动消息却不得不可怜地局限在父窗口里。