CChart编程非常灵活。前面高四第四课介绍了插件菜单,第五课介绍了客户区自绘,本节课笨笨来介绍另一种可以自定义的因素,那就是——消息响应!
首先看看下列函数。
// 用户自定义鼠标移动前操作,返回true跳过默认例程
void SetPreMouseMoveFunc(bool (*fcnPreMouseMove)( void *plot, HDC hDC, POINT point, UINT ctrlKey, void *para, bool &bContinue ), void *pPara);
// 用户自定义鼠标单击前操作,返回true跳过默认例程
void SetPreLButtonDownFunc(bool (*fcnPreLButtonDown)( void *plot, HDC hDC, POINT point, UINT ctrlKey, void *para, bool &bContinue ), void *pPara);
// 用户自定义鼠标抬起前操作,返回true跳过默认例程
void SetPreLButtonUpFunc(bool (*fcnPreLButtonUp)( void *plot, HDC hDC, POINT point, UINT ctrlKey, void *para, bool &bContinue ), void *pPara);
// 用户自定义鼠标双击前操作,返回true跳过默认例程
void SetPreLButtonDblClkFunc(bool (*fcnPreLButtonDblClk)( void *plot, HDC hDC, POINT point, UINT ctrlKey, void *para, bool &bContinue ), void *pPara);
// 用户自定义按键前操作,返回true跳过默认例程
void SetPreKeyDownFunc(bool (*fcnPreKeyDown)( void *plot, HDC hDC, UINT key, void *para, bool &bContinue ), void *pPara);
// 用户自定义鼠标移动后操作,返回值没有关系
void SetPostMouseMoveFunc(bool (*fcnPostMouseMove)( void *plot, HDC hDC, POINT point, UINT ctrlKey, void *para, bool &bContinue ), void *pPara);
// 用户自定义鼠标单击后操作,返回值没有关系
void SetPostLButtonDownFunc(bool (*fcnPostLButtonDown)( void *plot, HDC hDC, POINT point, UINT ctrlKey, void *para, bool &bContinue ), void *pPara);
// 用户自定义鼠标抬起后操作,返回值没有关系
void SetPostLButtonUpFunc(bool (*fcnPostLButtonUp)( void *plot, HDC hDC, POINT point, UINT ctrlKey, void *para, bool &bContinue ), void *pPara);
// 用户自定义鼠标双击后操作,返回值没有关系
void SetPostLButtonDblClkFunc(bool (*fcnPostLButtonDblClk)( void *plot, HDC hDC, POINT point, UINT ctrlKey, void *para, bool &bContinue ), void *pPara);
// 用户自定义按键后操作,返回值没有关系
void SetPostKeyDownFunc(bool (*fcnPreKeyDown)( void *plot, HDC hDC, UINT key, void *para, bool &bContinue ), void *pPara);
这里共有10个函数。分别处理WM_LBUTTONDOWN、WM_LBUTTONUP、WM_LBUTTONDBLCLK、WM_MOUSEMOVE、WM_KEYDOWN这五个消息,每个消息可以设置两个处理函数,分别处理消息开始和消息结束的动作。
这10个函数的参数都是两个,第一个是函数指针,第二个是准备传递给函数指针的参数。
函数指针的格式如下:
bool (*fcnMouseFunc)( void *plot, HDC hDC, POINT point, UINT ctrlKey, void *para, bool &bContinue );
bool (*fcnKeyFunc)( void *plot, HDC hDC, UINT ctrlKey, void *para, bool &bContinue );
这个函数的参数里,plot是保留参数,不管;hDC是绘图设备环境;point是鼠标位置;ctrlKey是按键值;para用于接收外部传入的指针;bContinue表示是否把消息往后传递,以便系统继续处理。
下面先建立一个和第一课一模一样的程序。
下面我们让程序的状态栏显示鼠标的坐标和数据信息。
首先需要创建一个状态栏。
添加一个HWND变量,和一个函数。
HWND hWndStatus;
HWND CreateStatusBar(HWND hParentWnd)
{
const int PANEL_NUM = 3;
int array[PANEL_NUM]={400,500,-1};
HINSTANCE hInst = GetModuleHandle(NULL);
//创建Statusbar控件
HWND hWndStatus = CreateWindowEx(0, STATUSCLASSNAME, TEXT(""), WS_CHILD|WS_BORDER|WS_VISIBLE, 0, 0, 0, 0, hParentWnd, (HMENU)IDC_STATUSBAR, hInst, NULL);
if (hWndStatus)
{
SendMessage(hWndStatus,SB_SETPARTS,(WPARAM)PANEL_NUM,(LPARAM)array); //设置面板个数
SendMessage(hWndStatus,SB_SETTEXT,(LPARAM)1,(WPARAM)TEXT("欢迎使用")); //设置第二个面板内容
SendMessage(hWndStatus,SB_SETTEXT,(LPARAM)2,(WPARAM)TEXT("CChart数据可视化软件库")); //设置第三个面板内容
}
return hWndStatus;
}
然后在WM_Create消息里添加一句:
hWndStatus = CreateStatusBar(hWnd);
现在的问题是状态栏的位置和CChart的绘图有冲突。
为解决这个问题,修改一下Attach的方式:
GetWindowRect(hWndStatus, &rct1);
GetClientRect(hWnd, &rect);
rect.bottom -= (rct1.bottom-rct1.top);
chartWnd.Attach(hWnd, rect, kTypeXY);
这里需要在前面定义两个RECT变量。
RECT rect, rct1;
同时响应一下WM_SIZE消息:
case WM_SIZE:
SendMessage(hWndStatus, WM_SIZE, 0, 0);
GetWindowRect(hWndStatus, &rct1);
GetClientRect(hWnd, &rect);
rect.bottom -= (rct1.bottom-rct1.top);
chartWnd.GetChart()->SetConfineRect(rect);
break;
有同学要问了,Attach之后,消息不都是被接管了吗?这里怎么还能响应WM_SIZE消息呢?
说的没错!但实际上CChartWnd只处理了少量必要消息,其余消息仍然传递出来了的!
现在显示正常了。
下面创建一个响应WM_MOUSEMOVE的函数。
bool PreMouseMove( void *plot, HDC hDC, POINT point, UINT ctrlKey, void *pPara, bool &bContinue )
{
if(!pPara)return false;
CChart *pChart = (CChart *)pPara;
RECT plotRect = pChart->GetLastPlotRect();
if(!PtInRect(&plotRect, point))return false;
double data[2];
pChart->ClientToData(&point, data);
TCHAR str[64];
_stprintf(str, _T("鼠标位置在(%d, %d), 对应的数据值是(%g, %g)"), point.x, point.y, data[0], data[1] );
SendMessage(hWndStatus,SB_SETTEXT,(LPARAM)0,(WPARAM)str); //设置第一个面板内容
return true;
}
注意这里的参数,plot指针是保留参数;hDC是绘图设备环境;point是鼠标位置;ctrlKey是按键信息;pPara指针是关键参数,我们从这里传入了一个CChart类指针,这才获得了绘图的相关数据;bContinue表示消息是否继续传递。
下面在WM_CREATE消息里面把上面这个函数插入到CChart的消息响应序列里。
chartWnd.GetChart()->SetPreMouseMoveFunc(&PreMouseMove, chartWnd.GetChart());
注意到,这里用SetPreMouseMoveFunc的第二个参数,把CChart的指针传递给了PreMouseMove函数。
好了,看看效果吧!
正如我们设计的,在状态栏的第一个窗格里面,显示了鼠标位置信息和相应的数据信息。
同学们可以试试看在PreMouseMove函数把bContinue设置成false会怎么样。
好了,这节课介绍了消息响应的自定义方法,下节课将重磅推出CChart的子类化编程,进行更彻底的自定义,敬请期待。