GdiplusFlat(8)自绘按钮的实现(方法1)

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

在上几篇中,我们说到了通过GdipCreateFromHDC函数将HDC(设备上下文句柄)装换成GDI+的Graphics对象,讲到了画笔和画刷的使用,演示了何利用GDI+Flat来画图片和画文字,并讲到了双缓冲绘图和DC复制。

这一篇博文中,我们来聊聊如何用GdiplusFlat来绘制按钮。

还是和以前一样,我们必须自己声明GDI+Flat函数,自己定义GDI+Flat的数据结构。自己动手,丰衣足食。~~

其实,自绘按钮有两种形式,这两种形式分别是:

1。我们不创建按钮,我们直接在窗口的设备上下文场景的一部分上绘制按钮,也就是说不存在一个子窗口,按钮完全是我们自己画上去的。

2。将一个标准的win32按钮子类化(子类化介绍),在按钮的WM_PAINT、WM_MOUSEMOVE、WM_MOUSELEAVE、WM_LBUTTONDOWN、WM_LBUTTONUP消息里面控制按钮的绘制,也就是说,我们直接基于一个Win32按钮,我们是改变了这个win32按钮的实现逻辑,绘制成我们需要的样式,按钮依旧作为子窗口存在。

这一篇文章讲一讲方法1

先看看效果图:(GIF本身的限制,动画有颜色失真)

GdiplusFlat(8)自绘按钮的实现(方法1)_第1张图片

首先我们用GDI写一个画文字的函数,你可能会问为何费用要用GDI?因为GDI+画文字的居中精度不准,而且GDI绘制效率高,同时我们也不需要花里胡哨的文字。

void WINAPI MyDrawText(HDC hdc, LPCTSTR str, LONG left, LONG top, LONG width, LONG height, BOOL iscenter, COLORREF color = 0){
	SetBkMode(hdc, TRANSPARENT);
	RECT r = { left, top, left + width, top + height };
	HFONT hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
	HFONT hOldFont = (HFONT)SelectObject(hdc, hFont);
	SetTextColor(hdc, color);
	DrawText(hdc, str, -1, &r, iscenter ? DT_CENTER | DT_VCENTER | DT_SINGLELINE : 0);
	SelectObject(hdc, hOldFont);
	DeleteObject(hFont);
}//参数:设备上下文句柄,要绘制的字符串,x位置,y位置,宽度,高度,是否绘制在矩形中央,颜色

之后我们写一个ARGB宏,用于生成ARGB颜色

#define ARGB(a,r,g,b) ((int)(((BYTE)a)<<24)|(((BYTE)r)<<16)|(((BYTE)g)<<8)|((BYTE)b))


我们写一个画按钮的函数

void WINAPI drawbutton(HDC hdc,unsigned int argb){//参数:设备上下文句柄;按钮背景ARGB颜色
	int graphics;
	GdipCreateFromHDC(mdc, &graphics);//创建Graphics对象

	GdipCreateSolidFill(0xFFFFFFFF, &brush);
	GdipFillRectangle(graphics, brush, 0, 0, 500, 500);
	GdipDeleteBrush(brush);//将内存DC背景涂白

	GdipCreateSolidFill(argb, &brush);//创建单色画刷
	GdipFillRectangle(graphics, brush, 20, 20, 120, 25);//用单色画刷填充矩形
	GdipDeleteBrush(brush);//销毁画刷
	
	GdipCreatePen1(ARGB(255, 185, 206, 230), 1, 2, &pen);//创建画笔
	GdipDrawRectangle(graphics, pen, 20, 20, 120, 25);//画矩形
	GdipDeletePen(pen);//销毁画笔

	MyDrawText(mdc, TEXT("按钮"), 20, 20, 120, 26, true);

	StretchBlt(hdc, 0, 0, 500, 500, mdc, 0, 0, 500, 500, SRCCOPY);//DC复制
	GdipDeleteGraphics(graphics);//销毁Graphics对象
}


WM_MOUSEMOVE消息:

鼠标在窗口上移动,判断移动位置是否在我们要绘制的按钮里:

	case WM_MOUSEMOVE://鼠标移动
		xPos = GET_X_LPARAM(lParam);//鼠标位置X坐标
		yPos = GET_Y_LPARAM(lParam);//鼠标位置Y坐标//不要用LOWORD和HIWORD获取坐标,因为坐标有可能是负的

		//..........TrackMouseEvent注册WM_MOUSELEAVE
		if (xPos < 140 && xPos>20 && yPos > 20 && yPos < 45){
			HDC hdc; hdc = GetDC(hwnd);
			drawbutton(hdc, ARGB(255, 64, 198, 255));
		}
		else{
			HDC hdc; hdc = GetDC(hwnd);
			drawbutton(hdc, ARGB(255, 240, 240, 240));
		}
		return 0;
		break;


通过“if (xPos < 140 && xPos>20 && yPos > 20 && yPos < 45)”判断是否在按钮内,在的话就把背景颜色改成蓝色,否则为灰色(正常状态)

另外通过TrackMouseEvent注册WM_MOUSELEAVE,参见下面的完整代码或者:这篇博文。在WM_MOUSELEAVE里也把按钮背景绘制为正常状态,这么做是为了防止鼠标从按钮上过快地离开窗口以至于来不及响应WM_MOUSEMOVE消息。

WM_LBUTTONDOWN和WM_LBUTTONUP消息:鼠标按下和弹起的消息,在这两个消息里绘制按钮在鼠标按下和弹起的状态,要响应WM_LBUTTONUP需要在WM_LBUTTONDOWN捕获鼠标:

case WM_LBUTTONDOWN:
		//让无边框窗口能够拖动(在窗口客户区拖动)
		PostMessage(hwnd, WM_SYSCOMMAND, 61458, 0);
		xPos = GET_X_LPARAM(lParam);
		yPos = GET_Y_LPARAM(lParam);
		if (xPos < 140 && xPos>20 && yPos > 20 && yPos < 45){
			hdc = GetDC(hwnd);
			drawbutton(hdc, ARGB(50, 255, 0, 0));
			SetCapture(hwnd);//捕获鼠标
		}
		return 0;
		break;
	case WM_LBUTTONUP:
		if (1){
			hdc = GetDC(hwnd);
			drawbutton(hdc, ARGB(255, 240, 240, 240));
		}
		xPos = GET_X_LPARAM(lParam);
		yPos = GET_Y_LPARAM(lParam);
		if (xPos < 140 && xPos>20 && yPos > 20 && yPos < 45){
			//在这里添加按钮单击的事件
			MessageBox(NULL, TEXT("你按下了按钮"), TEXT("自绘按钮"), MB_ICONINFORMATION);
		}


完整源码:

#include "stdafx.h"
#include <gdiplus.h>
#include <gdiplusflat.h>
#pragma comment(lib,"gdiplus.lib")//very important

#include <windows.h>
#include <windowsx.h>
#pragma comment(lib,"user32.lib")
#pragma comment(lib,"gdi32.lib")

#pragma comment(lib,"Msimg32.lib")

//GDI+Flat

#define ARGB(a,r,g,b) ((int)(((BYTE)a)<<24)|(((BYTE)r)<<16)|(((BYTE)g)<<8)|((BYTE)b))

typedef struct _GdiplusStartupInput{
	unsigned int GdiplusVersion;
	unsigned int DebugEventCallback;
	BOOL SuppressBackgroundThread;
	BOOL SuppressExternalCodecs;
}GdiplusStartupInput;

extern "C" int WINAPI GdiplusStartup(int* token, GdiplusStartupInput *input, int *output);
extern "C" void WINAPI GdiplusShutdown(int token);
extern "C" int WINAPI GdipCreateFromHDC(HDC hdc, int* graphics);
extern "C" int WINAPI GdipDeleteGraphics(int graphics);
//画笔
extern "C" int WINAPI GdipCreatePen1(unsigned int argb_color, float width, int unit, int** pen);
extern "C" int WINAPI GdipDeletePen(int* pen);
//画矩形 画直线
extern "C" int WINAPI GdipDrawRectangle(int graphics, int* pen, float x, float y, float width, float height);
extern "C" int WINAPI GdipDrawLine(int graphics, int* pen, float x1, float y1, float x2, float y2);
//画刷
typedef struct _PointF{
	float x;
	float y;
}PointF;
extern "C" int WINAPI GdipCreateSolidFill(unsigned int argb_color, int** brush);
extern "C" int WINAPI GdipCreateLineBrush(PointF* point1, PointF* point2, unsigned int argb_color1, unsigned int argb_color2, int wrapMode, int** lineGradient);
extern "C" int WINAPI GdipDeleteBrush(int* brush);
//画填充矩形
extern "C" int WINAPI GdipFillRectangle(int graphics, int* brush, float x, float y, float width, float height);
//画图片
extern "C" int WINAPI GdipLoadImageFromFile(WCHAR* filename, int** image);
extern "C" int WINAPI GdipLoadImageFromStream(LPSTREAM stream, int** image);
extern "C" int WINAPI GdipGetImageDimension(int* image, float* width, float* height);
extern "C" int WINAPI GdipDrawImageRect(int graphics, int* image, float x, float y, float width, float height);
extern "C" int WINAPI GdipDisposeImage(int* image);
//画文字
typedef struct _RectF{
	float x;
	float y;
	float Width;
	float Height;
}RectF;
extern "C" int WINAPI GdipSetTextRenderingHint(int graphics, int mode);
extern "C" int WINAPI GdipSetSmoothingMode(int graphics, int smoothingMode);
extern "C" int WINAPI GdipCreateFontFamilyFromName(WCHAR* name, int* fontCollection, int** fontFamily);
extern "C" int WINAPI GdipCreateStringFormat(int formatAttributes, short language, int** format);
extern "C" int WINAPI GdipSetStringFormatAlign(int* format, int align);
extern "C" int WINAPI GdipSetStringFormatLineAlign(int* format, int align);
extern "C" int WINAPI GdipCreateFont(int* fontFamily, float emSize, int style, int unit, int** font);
extern "C" int WINAPI GdipDrawString(int graphics, WCHAR* string, int length, int* font, RectF* layoutRect, int* stringFormat, int* brush);
extern "C" int WINAPI GdipDeleteFont(int* font);
extern "C" int WINAPI GdipDeleteStringFormat(int* format);
extern "C" int WINAPI GdipDeleteFontFamily(int* fontFamily);

int token;
int* pen;
int* brush;
int* linebrush;
int* image;

HDC mdc;

BOOL Track;

void WINAPI MyDrawText(HDC hdc, LPCTSTR str, LONG left, LONG top, LONG width, LONG height, BOOL iscenter, COLORREF color = 0){
	SetBkMode(hdc, TRANSPARENT);
	RECT r = { left, top, left + width, top + height };
	HFONT hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
	HFONT hOldFont = (HFONT)SelectObject(hdc, hFont);
	SetTextColor(hdc, color);
	DrawText(hdc, str, -1, &r, iscenter ? DT_CENTER | DT_VCENTER | DT_SINGLELINE : 0);
	SelectObject(hdc, hOldFont);
	DeleteObject(hFont);
}//参数:设备上下文句柄,要绘制的字符串,x位置,y位置,宽度,高度,是否绘制在矩形中央,颜色

//*************************************************************
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

WNDCLASS wc;
const TCHAR* AppName = TEXT("MyWindowClass1");
HWND hwnd1;

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
	_In_opt_ HINSTANCE hPrevInstance,
	_In_ LPTSTR    lpCmdLine,
	_In_ int       nCmdShow)
{
	//GDI+开启
	GdiplusStartupInput StartupInput = { 0 };
	StartupInput.GdiplusVersion = 1;
	if (GdiplusStartup(&token, &StartupInput, NULL))MessageBox(0, TEXT("GdiPlus开启失败"), TEXT("错误"), MB_ICONERROR);

	//这里是在构建窗口类结构
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = WndProc;//窗口回调函数指针
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = hInstance;//实例句柄
	wc.hIcon = LoadIcon(hInstance, TEXT("ICON_1"));
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);//默认指针
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wc.lpszMenuName = NULL;
	wc.lpszClassName = AppName;//窗口类名

	//注册窗口类
	if (!RegisterClass(&wc))
	{
		MessageBox(NULL, TEXT("注册窗口类失败!"), TEXT("错误"), MB_ICONERROR);
		return 0;
	}

	//创建窗口
	int style = WS_OVERLAPPEDWINDOW;
	hwnd1 = CreateWindowEx(NULL, AppName, TEXT("窗口标题"), style, 50, 50, 500, 500, 0, LoadMenu(hInstance, TEXT("MENU1")), hInstance, 0);
	if (hwnd1 == NULL)
	{
		MessageBox(NULL, TEXT("创建窗口失败!"), TEXT("错误"), MB_ICONERROR);
		return 0;
	}
	//无边框窗口
	SetWindowLong(hwnd1, GWL_STYLE, WS_OVERLAPPED | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);

	//显示、更新窗口
	ShowWindow(hwnd1, nCmdShow);
	UpdateWindow(hwnd1);

	//消息循环
	MSG msg;
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	//GDI+关闭
	GdiplusShutdown(token);//可以把这个写在消息循环后面,程序退出就销毁,或者在不需要GDI+时调用,比如GDI+窗口的WM_DESTROY消息里调用
	return msg.wParam;

}

//参数:设备上下文句柄;按钮背景ARGB颜色
void WINAPI drawbutton(HDC hdc,unsigned int argb){
	int graphics;
	GdipCreateFromHDC(mdc, &graphics);//创建Graphics对象

	GdipCreateSolidFill(0xFFFFFFFF, &brush);
	GdipFillRectangle(graphics, brush, 0, 0, 500, 500);
	GdipDeleteBrush(brush);//将内存DC背景涂白

	GdipCreateSolidFill(argb, &brush);//创建单色画刷
	GdipFillRectangle(graphics, brush, 20, 20, 120, 25);//用单色画刷填充矩形
	GdipDeleteBrush(brush);//销毁画刷
	
	GdipCreatePen1(ARGB(255, 185, 206, 230), 1, 2, &pen);//创建画笔
	GdipDrawRectangle(graphics, pen, 20, 20, 120, 25);//画矩形
	GdipDeletePen(pen);//销毁画笔

	MyDrawText(mdc, TEXT("按钮"), 20, 20, 120, 26, true);

	StretchBlt(hdc, 0, 0, 500, 500, mdc, 0, 0, 500, 500, SRCCOPY);//DC复制
	GdipDeleteGraphics(graphics);//销毁Graphics对象
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	int xPos, yPos;
	switch (uMsg){
	case WM_PAINT:
		HDC hdc;
		PAINTSTRUCT ps;
		hdc = BeginPaint(hwnd, &ps);
		drawbutton(hdc, ARGB(255, 240, 240, 240));
		EndPaint(hwnd, &ps);
		return 0;//告诉系统,WM_PAINT消息我已经处理了,你那儿凉快哪儿玩去吧。
	case WM_CREATE://窗口创建完毕时创建内存DC
		mdc = CreateCompatibleDC(0);
		BITMAPINFO bitmapinfo;
		RtlZeroMemory(&bitmapinfo, sizeof(BITMAPINFO));

		bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
		bitmapinfo.bmiHeader.biWidth = 500;
		bitmapinfo.bmiHeader.biHeight = 500;
		bitmapinfo.bmiHeader.biPlanes = 1;
		bitmapinfo.bmiHeader.biBitCount = 32;

		HBITMAP b, old;
		b = CreateDIBSection(0, &bitmapinfo, 0, 0, 0, 0);
		old = (HBITMAP)SelectObject(mdc, b);
		if (old != NULL){
			DeleteObject(old);
		}
		DeleteObject(b);
		return 0;
		break;
	case WM_DESTROY://窗口已经销毁
		DeleteDC(mdc);
		PostQuitMessage(0);//退出消息循环,结束应用程序
		return 0;
		break;
	case WM_MOUSEMOVE://鼠标移动
		xPos = GET_X_LPARAM(lParam);//鼠标位置X坐标
		yPos = GET_Y_LPARAM(lParam);//鼠标位置Y坐标//不要用LOWORD和HIWORD获取坐标,因为坐标有可能是负的

		if (Track == false){
			TRACKMOUSEEVENT a;
			a.cbSize = sizeof(TRACKMOUSEEVENT);
			a.dwFlags = TME_LEAVE;
			a.hwndTrack = hwnd;
			if (TrackMouseEvent(&a) != 0){
				Track == true;
			}
		}
		if (xPos < 140 && xPos>20 && yPos > 20 && yPos < 45){
			HDC hdc; hdc = GetDC(hwnd);
			drawbutton(hdc, ARGB(255, 64, 198, 255));
		}
		else{
			HDC hdc; hdc = GetDC(hwnd);
			drawbutton(hdc, ARGB(255, 240, 240, 240));
		}
		return 0;
		break;
	case WM_MOUSELEAVE:
		Track = false;
		if (1){
			hdc = GetDC(hwnd);
			drawbutton(hdc, ARGB(255, 240, 240, 240));
		}
		return 0;
		break;
	case WM_LBUTTONDOWN:
		//让无边框窗口能够拖动(在窗口客户区拖动)
		PostMessage(hwnd, WM_SYSCOMMAND, 61458, 0);
		xPos = GET_X_LPARAM(lParam);
		yPos = GET_Y_LPARAM(lParam);
		if (xPos < 140 && xPos>20 && yPos > 20 && yPos < 45){
			hdc = GetDC(hwnd);
			drawbutton(hdc, ARGB(50, 255, 0, 0));
			SetCapture(hwnd);//捕获鼠标
		}
		return 0;
		break;
	case WM_LBUTTONUP:
		if (1){
			hdc = GetDC(hwnd);
			drawbutton(hdc, ARGB(255, 240, 240, 240));
		}
		xPos = GET_X_LPARAM(lParam);
		yPos = GET_Y_LPARAM(lParam);
		if (xPos < 140 && xPos>20 && yPos > 20 && yPos < 45){
			//在这里添加按钮单击的事件
			MessageBox(NULL, TEXT("你按下了按钮"), TEXT("自绘按钮"), MB_ICONINFORMATION);
		}
		return 0;
		break;
	default:
		break;
	}
	return DefWindowProc(hwnd, uMsg, wParam, lParam);//其他消息交给系统处理
}



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