GdiplusFlat(2)WM_PAINT消息,窗口子类化和设备上下文

本博文由CSDN博主zuishikonghuan所作,版权归zuishikonghuan所有,转载请注明出处:http://blog.csdn.net/zuishikonghuan/article/details/47169605

上一篇博文(GdiplusFlat(1)GDI+平面API:用GDI的思想进行GDI+编程 ,地址:http://blog.csdn.net/zuishikonghuan/article/details/46982559)中,我介绍了GdiplusFlat的使用意义和使用方法——自己声明API原型,自己定义struct,并给出了具体的方法,然后连接Gdiplus.lib。同时说明了GdiplusFlat编程的意义。

那么我们如何用GDI+Flat绘图呢,我们知道,对于GDI,我们通常在窗口的WM_PAINT消息里绘图,这是为什么呢?让我们先来看看WM_PAINT消息是什么。

MSDN:https://msdn.microsoft.com/en-us/library/dd145213.aspx

The WM_PAINT message is sent when the system or another application makes a request to paint a portion of an application's window. The message is sent when the UpdateWindow or RedrawWindow function is called, or by the DispatchMessage function when the application obtains a WM_PAINT message by using the GetMessage or PeekMessage function.

A window receives this message through its WindowProc function.

简单说,就是窗口需要重新绘制时发送WM_PAINT消息。

如果我们不在WM_PAINT消息里面绘图,那么我们绘图后,窗口一刷新,我们绘制的东西就没了,所以,我们在这个消息里绘制,那么每次窗口刷新我们都可以绘制一便,就可以让绘制的图形一直存在了。

WM_PAINT消息使用方法

对于窗口,直接使用WndProc拦截WM_PAINT消息即可,但对于控件(子窗口),必须先子类化!

子类化:我们知道,对于控件(子窗口)(下面只说“控件”)而言,他也是有一个WndProc(窗口过程回调函数)的,但是控件都不是我们自己做的,而是使用的windows的通用控件,因此我们不能直接处理控件的windows消息,因为控件在收到特定消息时会对父窗口发送一个通知,我们一般在父窗口的窗口过程中处理这个通知即可。但不是什么消息都是可以利用通知间接控制的,比如窗口重新绘制的WM_PAINT消息,因此我们就需要子类化掉这个控件,修改控件的窗口过程回调函数,拦截WM_PAINT消息,对于我们不需要处理的消息,再重新丢给控件原来的回调函数,这个问题就解决了,而这种做法,就叫做“窗口子类化”或“窗口消息子类化”。

子类化的方法如下:

我们首先需要一个全局变量:

WNDPROC lastproc;


WNDPROC是回调函数指针类型,用于指向一个窗口过程回调函数。

第二步,我们写一个回调函数,只处理WM_PAINT消息。

对于不处理的消息,不再给DefWindowProc处理了,而是调用CallWindowProc丢给原来的回调函数,像这样:

<pre class="cpp" name="code">LRESULT CALLBACK Control1WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg){
	case WM_PAINT:
		HDC hdc;
		PAINTSTRUCT ps;
		hdc = BeginPaint(hwnd, &ps);
		//这里先用GDI演示一下绘图
		MoveToEx(hdc, 20, 20, NULL);
		LineTo(hdc, 50, 50);

		EndPaint(hwnd, &ps);
		return 0;//告诉系统,WM_PAINT消息我已经处理了,你那儿凉快哪儿玩去吧。
	}
	return CallWindowProc(lastproc, hwnd, uMsg, wParam, lParam);//让原来的窗口回调函数处理其他消息
}

lastproc现在还是一个空指针,别急,在创建控件下面进行子类化。

我就用我之前写的win32窗口控件的源码(参见我之前的博客)上进一步编写:

<pre class="cpp" name="code">case WM_CREATE:
//...
		button1 = CreateWindow(TEXT("Button"), TEXT("默认按钮"), WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 10, 60, 100, 30, hwnd, (HMENU)4, (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE), NULL);
		SendMessage(button1, WM_SETFONT, (WPARAM)GetStockObject(17), 0);

		lastproc = (WNDPROC)SetWindowLongPtr(button1, GWL_WNDPROC, (LONG)&Control1WndProc);
//...


使用SetWindowLongPtr修改了窗口的回调函数:

第一个参数:要修改的窗口的句柄

第二个参数:GWL_WNDPROC表示修改窗口回调函数

第三个参数:新回调函数的指针

返回值:以前的窗口回调函数指针。

为何用SetWindowLongPtr不用SetWindowLong?原因:便于移植到64位程序。

效果图:

GdiplusFlat(2)WM_PAINT消息,窗口子类化和设备上下文_第1张图片

可以看到,原来的“默认按钮”不见了,取而代之的是我们绘制的一条线段。同时,我们发现单击这个直线也能执行父窗口WM_COMMAND里预定的动作!这就CallWindowProc函数又把消息丢给了原来的回调函数的功劳了!

设备上下文:

在上面绘图的过程中,有这么一行代码:

hdc = BeginPaint(hwnd, &ps);

其实这个hdc就是设备上下文句柄。

什么是设备上下文(Device Context)呢?

MSDN的解释:

A device context is a structure that defines a set of graphic objects and their associated attributes, as well as the graphic modes that affect output. Thegraphic objects include a pen for line drawing, a brush for painting and filling, a bitmap for copying or scrolling parts of the screen, a palette for defining the set of available colors, a region for clipping and other operations, and a path for painting and drawing operations. The remainder of this section is divided into the following three areas.

我的理解:

1。设备上下文是一个内核对象,应用程序不需要也无法访问设备上下文struct在内存中的指针,应用程序只能通过设备上下文的句柄(HDC)访问设备上下文。

2。GDI函数的绘图就是基于设备上下文(DC)的

3。设备上下文可以抽象成一个设备场景,相当于给我们提供了一个画板,我们在画板上绘图,由图形驱动程序负责将设备场景中的数据绘制到显存中。

遗憾的是,GDI+虽然是对GDI的封装,但不再使用设备上下文了,使用Graphic对象,但幸运的是,GDI+类其实是对GdiplusFlat封装实现对GDI的间接封装,GdiplusFlat依旧需要使用设备上下文。

如何获取设备上下文句柄(HDC):

1。使用BeginPaint函数和EndPaint函数

就像上面的代码一样。

2。使用GetDC/GetWindowDC函数

参数:窗口句柄

返回值:窗口的设备上下文句柄

注意使用GetDC/GetWindowDC函数创建的DC应使用ReleaseDC释放

3。创建内存场景,比如CreateDC和CreateCompatibleDC函数,我会在以后一篇讲双缓冲自绘的博客中详细说。





你可能感兴趣的:(Win32,windows,GDI+,gdiplus)