以前在对话框窗体上绘制图形都是先自定义一个继承CView类的绘图类,然后把这个类绑定到对话框上的一个静态控件上来做, 今天突然发现其实直接使用MFC的自定义控件来做更简单。如何使用自定义控件呢?
首先创建一个MFC对话框工程Demo,然后在资源视图中打开这个对话框,在Toolbox里面把Custom Control 控件拖拽到窗体上,
这个时候编译工程,发现Custom Control 还不可用,因为还没有给它创建任何的控制类,就像窗体一样,如果你自己新建一个对话框窗体,在使用这个窗体之前就必须为它创建一个类。那么如何为Custom control 创建一个属于它的类呢? 右键控件选择类向导,然后和创建对话框类一样创建一个属于它的类:
选择New 按钮新建一个类,在弹出来的对话中,将新类命名为MyCustomControl ,并且选择继承的基类,这里选择为CWnd类,当然也可以根据需要选择比如滚动和缩放视图的话,则继承CScrollView类等等。
当完成上面的步骤之后, 就创建了一个MyCustomControl类, 它继承于CWnd类, 是一个窗口类,要使用这个窗口类,就要先windows注册这个窗口才行,所以需要自定义一个注册窗口类RegisterWndClass(), 那么怎么自定义这个类呢?
先在MyCustomControl.h中 做如下声明:
public:
BOOL RegisterWndClass();
然后在MyCustomControl.cpp中实现这个函数
BOOL MyCustomControl::RegisterWndClass()
{
WNDCLASS windowclass;
HINSTANCE hInst = AfxGetInstanceHandle();
if(!(::GetClassInfo(hInst,MYWNDCLASS,&windowclass)))
{
windowclass.style = CS_DBLCLKS;
windowclass.lpfnWndProc = ::DefWindowProc;
windowclass.cbClsExtra = windowclass.cbWndExtra = 0;
windowclass.hInstance =hInst;
windowclass.hIcon = NULL;
windowclass.hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
windowclass.hbrBackground = ::GetSysColorBrush(COLOR_WINDOW);
windowclass.lpszMenuName = NULL;
windowclass.lpszClassName = MYWNDCLASS;
if(!AfxRegisterClass(&windowclass))
{
AfxThrowResourceException();
return FALSE;
}
}
return TRUE;
}
MyCustomControl::MyCustomControl()
{
//Register My window class
RegisterWndClass();
}
#define MYWNDCLASS _T("MyDrawPad");
或者直接在RegisterWndClass()函数中把MYWNDCLASS换成_T("MyDrawPad") 即可。
通过上述步骤我们就完成了自定义窗口类的注册,这个时候就可以使用这个窗口类了。
那现在是不是这个类和custom control 控件已经关联在一起了呢? 其实还没有, 因为此时custom control 它不认得这个窗口类,为什么?
因为我们在注册窗口类的时候给这个窗口类起了个名字叫 MyDrawPad 那这个名字是我们自定义的, custom control 是不知道了, 除非我们告诉它, 那么怎么告诉它呢?
回到资源视图,右键custom control 控件, 属性, 属性栏中一栏 Class 它的值为空, 那么在这里填上 MyDrawPad ,现在就把这个类和custom contro 控件关联起来了
把自定义的窗口类MyCustomControl 和custom control 控件关联上之后, 怎么使得Demo这个应用程序知道我们在custom control控件上做了什么动作呢?
这个就是关于自定义控件和应用程序之间的数据交换和通信的问题, 也很简单, 在资源视图中右键custom control 选择添加变量
变量类型为 MyCustomControl , Value类型为control 因为我们要控制这个custom control 控件,不是接受它上面的值, 接下来就是为这个变量命名,我们取名为
m_drawpad, 然后点击ok 创建完成。 那么我们来看看刚才通过右键属性为custom control 添加的控制变量, 它到底做了什么了呢? 首先它在Demo工程的窗体类中添加了变量:MyCustomControl m_drawpad;
即在 CDemoDlg.h 中添加了
public:
MyCustomControl m_drawpad;
此后在 CDemoDlg.cpp的DoDataExchange()函数中添加了一行控制代码:
DDX_Control(pDX,IDC_MyDrawPad,m_drawPad);
IDC_MyDrawPad 是我们前面添加的custom control控件的ID号
那么现在F5执行的话就可以看到窗体上有个白色的矩形区域了, 这个就是通过自定义控件窗体的 MyDrawPad 窗体!
注意,如果忘记在MyCustomControl 构造函数调用 RegisterWndClass(); 或者没有创建关联custom control 自定义控件和Demo应用程序的控制变量m_drawpad, 此时直接F5的话, 窗口是创建失败的,此时对话框也不会显示! 必须要记得把上面两步做了,才能成功创建窗口对话框!
那么下面我们来为这个窗体来做点什么吧 , 比如添加一个 绘图功能吧 。 绘图功能其实很简单, 它本身就是个消息的处理过程。
比如你绘图是吧, 首先 鼠标左键按下,然后拖动绘制图形, 绘制完了之后松开鼠标左键。 那这就是绘图的整个过程。 其中包含三个消息处理函数:
一个是鼠标左键按下消息的检测WM_LBUTTONDOWN, 鼠标按下之后要移动鼠标来绘图,则会产生一个鼠标移动的消息WM_MOUSEMOVE, 绘制完了之后就是松口鼠标,此时会产生一个鼠标弹起的消息 WM_LBUTTONUP,
那么现在就在类视图中右键 MyCustomControl类,选择添加windows消息句柄,
依次添加 下面三个消息:
WM_LBUTTONDOWN
WM_MOUSEMOVE
WM_LBUTTONUP
上面分析了绘图的过程包括三步: 鼠标按下开始绘制,鼠标移动绘制图形,鼠标弹起结束绘制
那么就需要在鼠标按下的时候获取并记录此时的坐标点的位置,那么我们就需要一个变量来记录这个坐标点, 因此需要在MyCustomControl.h 文件声明一个CPoint oldpt; 变量来记录当前的坐标位置,此时还需要定义一个变量来记录鼠标按下的状态和鼠标弹起的状态,就是要一个BOOL 变量,鼠标按下之后设置为TRUE 就可以随着鼠标移动开始绘制,鼠标弹起之后就设置为FALSE 就结束绘制。
所以还需要在MyCustomControl.h中定义个BOOL flag;
并在构造函数中声明为FALSE;
然后到鼠标按下之后, 在OnLButtonDown(UINT nFlags, CPoint point)消息函数中就要判断此时flag是不是为FALSE 如果是则设置为TRUE ,如果不是则说明之前记录了鼠标按下的状态并且没有改变这个状态就不需要处理,如果是FALSE则改成TRUE并需要做相应的处理:
就是记录当前的坐标位置, 并把状态设置为TRUE;
那么鼠标按下之后,就是要拖动鼠标来绘图了, 那么此时需要在OnMouseMove()函数中来完成绘图的功能:
首先判断鼠标是否按下 即 flag 是否为TRUE, 如果是则绘图,不是则不做处理:
绘图之前要获取当前客户区或窗口区域的上下文环境句柄dc, 之后就可以调用GDI中的函数来绘制图形了。需要使用CDC 来声明一个dc 句柄
CDC *dc = GetDC();
这里的绘图函数就要用到了两个: MoveTo(), LineTo() 即跟着鼠标的移动绘制线条。
注意通过GetDC()获取的dc句柄,使用完之后一定要使用ReleaseDC(dc)释放掉,否则会造成内存泄露!
绘制完后,就会松开鼠标,此时响应鼠标弹起事件, 在这个事件中设置为flag 为FALSE 那么 OnMouseMove就会停止绘制。这部分的具体实现代码如下:
void MyCustomControl::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
if(FALSE == flag)
{
oldpt = point;
flag = TRUE;
}
//CWnd::OnLButtonDown(nFlags, point);
}
void MyCustomControl::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
if(TRUE == flag)
{
CDC *dc = GetDC();
dc->MoveTo(oldpt);
dc->LineTo(point);
oldpt = point;
ReleaseDC(dc);
}
CWnd::OnMouseMove(nFlags, point);
}
void MyCustomControl::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
flag = FALSE;
//CWnd::OnLButtonUp(nFlags, point);
}
本文参考:http://www.codeproject.com/Articles/5032/Creating-and-Using-custom-controls-in-VC