[翻译]VC++中创建并使用自定义控件
介绍
您好!这是我在CodeProject上的第四篇文章。迁移到VC++,我第一个关注的是通过VC++帮助能够创建自定义控件,因为这是一个非常用有的特性,当你要修改任意控件的内容或创建新的控件的时候。所以我决定写这篇文章,这样新的开发者或第一次开发控件的人就能够从中得到帮助。
这就是所有的介绍,现在转到最初的观点:怎么创建自定义控件,为什么要创建自定义控件。我对在Win32 API下开发应用程序很感兴趣,因为它小巧并能独立运行,我从没用VC++开发过,但它是非常强大的语言,并且它强大的特性已经吸引我转向了它。自定义控件就是其中之一。CodeProjet上有很多使用自定义控件的文章。但是第一次阅读它们的时候,我不明白在简单的Windows应用程序中怎样去创建、获取和处理消息。自定义控件给开发者带来了一种方便的方式去创建控件并使其和其它常规控件一样可视化。(因为我是VC++的初学者,所以文章中如果有错请告知我)。
它在哪里
现在的问题是,自定义控件在哪里?答案就在下面。下图显示了自定义控件,它躺在控件栏中。
这个图显示了这个自定义控件的位置。你可以选择它,并能在你的窗体资源中直接绘制。这里最主要的问题是当你把这个控件放到你的窗体在,然后你编译并执行程序,你会看不到效果,因为你还没有为这个控件选择一个类,所以这个问题在后面的章节讨论。
创建一个类
现在,下图显示了自定义控件在窗体中绘制。现在,你在自定义控件上面右击并在弹出的菜单上选择“类向导”(ClassWizard)。
选择一个类
在你点击“类向导”之后,下面这个对话框就会在屏幕上显示。在它上面,选择“添加类”(Add Class)然后选“New...”
现在,当你点击New按钮后,给自定义控件选择基类的对话框就会出现如下面所示。在此,对于基类你有多个选择。意思就是你可以自定义基础控件,比如给静态控件或者编辑控件添加新特性,或者你可以创建一个全新的控件。我决定创建一个全新的控件,像便笺本那样,所以,我选择基础类CWnd作为基类。
最后,你已经给你的控件创建了类。现在,重要部分开始了……
因为之前创建的类使用CWnd作为基类,我们要注册这个类因为这是一个自定义的类。所以,我们必须写函数RegisterWndClass去注册它。函数的代码可能像下面这样……
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
BOOL MyCustomControl::RegisterWndClass() { WNDCLASS windowclass; HINSTANCE hInst = AfxGetInstanceHandle(); //Check weather the class is registerd already if (!(::GetClassInfo(hInst, MYWNDCLASS, &windowclass))) { //If not then we have to register the new class windowclass.style = CS_DBLCLKS;// | CS_HREDRAW | CS_VREDRAW; 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; }
用这种方式,我们已经注册了这个新的窗口类。现在,你要像下面这样添加这个函数到类的默认构造函数中:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
MyCustomControl::MyCustomControl() { //Register My window class RegisterWndClass(); }
我想有人会想MYWNDCLASS是个什么。答案是它是我们自定义控件的类名。它定义在MyCustomControl.h文件的的顶部,就像下面这样:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#define MYWNDCLASS "MyDrawPad"
现在,我们就有了自己的类,名叫MyDrawPad。
附加类到自定义控件
所有这些做好之后,自定义控件我们就要创建完成了。最后剩下的事情是把自定义控件设置为我们创建好的窗口类。为此,在资源视图里右击自定义控件然后选择它的属性。一个对话像下面的对话框将会出现……
然后,设置类名为我们之前创建的MyDrawPad。这里你可以通过改变“Style”的编辑框16进制数的值来选择窗口样式。我已经试过了一些值,你们也可以试试。
实现数据交换
现在,所有的事情都做好了,但数据必须要在窗口和我们的应用程序之间进行交换。所以,在你的对话框类为我们的自定义控件添加变量,像下面这样:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// Implementation protected: HICON m_hIcon; MyCustomControl m_drawpad;//This is our custom control
之后,你得添加在DoDataExchage()函数里添加下面的代码,让它与自定义控件进行交互。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
void CCustomControlDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CCustomControlDlg) // NOTE: the ClassWizard will add DDX and DDV calls here DDX_Control(pDX,IDC_CUSTOM1,m_drawpad); //}}AFX_DATA_MAP }
现在,你做好准备动作了吗???好,按Ctrl+F5编译并执行程序。(但愿你都做对了……我想没有错误!!!)
别忘了在对话框的头文件里写#include "MyCustomControl.h",不然的话会生成很多错误。(我想你不会怪我哈哈哈)。
添加消息处理
上面的关键部分成功后,你就能看到对话框里有一个白色的矩形。这就是我们的自定义控件(相信我!)。这只是个不窗口。现在,我们添加一些窗口消息和我们的控件相交互。请仔细阅读……
为了给窗口添加Windows消息,右击MyCustomControl类并选择Add Windows Message Handler来添加消息,如mouse move,click等等。
这样子,在这么长(很长吗?)的工作后,你已经创建了你的自定义控件。现在放松一下,开始由你们自己写了。请对我的文章进行评论(我喜欢你们评论)。比如,我已经用包含的源代码写了一个简单的DrawPad。
现在我们来梳理一下这篇文件的简短概要:
要创建自定义控件,我们要做下面这些事情:
- 创建包含对话框的简单的MFC应用程序。
- 从控件栏中选择自定义控件。
- 在对话框资源里绘制自定义控件。
- 右击自定义控件选择类向导。
- 从Add Class菜单在添加新的类,选择合适的基类。
- 添加代码并注册Custom Window class.
- 在对话框里给基类(自定义类)添加成员变量。
- 给已经注册的窗口类设置自定义控件。
- 添加DoDataExchange代码。
- 用Ctrl+F5编译并执行程序。
- 在类视图里通过右击自定义控件类来添加/编辑Windows消息处理函数。
如果你喜欢它,给我邮件yogmj@hotmail.com,发送你的建议或文章里的拼写错误。或者这些源代码里的Bugs(因为
我是Bug猎手{我是这么认为的,你认为吗?})