1. 程式人生 > >[Win32 & GDI+Flat] 用分層視窗實現簡單仿Aero透明效果

[Win32 & GDI+Flat] 用分層視窗實現簡單仿Aero透明效果

首先說一下GDI+Flat:我有寫過一個GDI+Flat的系列部落格,一共有10篇,都是順著的,這是第一篇:GdiplusFlat(1)GDI+平面API:用GDI的思想進行GDI+程式設計,有興趣的可以看一看。因為本篇中的繪圖函式都是用的GDI+Flat,因此我們需要自己宣告函式,自己定義資料結構,只用Win32和c語言的基本資料型別。

好了,廢話不多說,先上圖:

測試結果:XP、Win7(Areo/Basic)、Win8.1、Win10下都能很好的工作


如果覺得標題欄和邊框不好看,可以改顏色,也可以改成漸變的,還可以用圖片,比如畫個透明玻璃的PNG圖片,如果是帶陰影的玻璃的圖片,就和Win7的Aero差不多了,可惜我不會PS。

原始碼如下:

#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")

#include<OleCtl.h>
#pragma comment(lib,"OleAut32.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* image;

void WINAPI MyDrawText(HDC hdc, LPWSTR str, LONG left, LONG top, LONG width, LONG height, BOOL iscenter, COLORREF color = 0){
	int graphics;

	GdipCreateFromHDC(hdc, &graphics);//建立Graphics物件
	GdipCreateSolidFill(color, &brush);//建立單色畫刷

	GdipSetTextRenderingHint(graphics, 3);//設定Graphics物件的文字渲染模式
	int* fontfamily;
	GdipSetSmoothingMode(graphics, 4);//設定Graphics物件的渲染質量
	GdipCreateFontFamilyFromName(L"宋體", NULL, &fontfamily);//建立一個基於指定的字體系列的FontFamily物件
	int* format;
	GdipCreateStringFormat(0, 0, &format);//建立一個基於字串的格式標誌和語言的 StringFormat 物件
	int* font;
	GdipCreateFont(fontfamily, 12, 0, 2, &font);//建立字型
	RectF rect; rect = { left, top, width, height };
	GdipDrawString(graphics, str, -1, font, &rect, 0, brush);//畫文字

	GdipDeleteFont(font);//銷燬指定Font物件,釋放資源
	GdipDeleteStringFormat(format);//銷燬指定StringFormat物件,釋放資源
	GdipDeleteFontFamily(fontfamily);//銷燬指定FontFamily物件,釋放資源
	GdipDeleteBrush(brush);//銷燬畫刷

	GdipDeleteGraphics(graphics);//銷燬Graphics物件


}//引數:裝置上下文控制代碼,要繪製的字串,x位置,y位置,寬度,高度,是否繪製在矩形中央,argb顏色


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

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

HDC mdc, mdc2;

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(WS_EX_LAYERED | WS_EX_TOPMOST, AppName, TEXT("視窗標題"), style, 0, 0, 0, 0, 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);
	SetWindowPos(hwnd1, HWND_TOP, 50, 50, 500, 500, SWP_SHOWWINDOW);

	mdc = CreateCompatibleDC(0);//建立記憶體DC
	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);

	int graphics;
	GdipCreateFromHDC(mdc, &graphics);//建立Graphics物件

	GdipCreateSolidFill(0x80FFFFFF, &brush);//建立畫刷,這是整體的背景,有透明度
	GdipFillRectangle(graphics, brush, 0, 0, 500, 500);//畫填充矩形
	GdipDeleteBrush(brush);//銷燬畫刷

	GdipCreatePen1(ARGB(200, 0, 157, 225), 1, 2, &pen);//建立畫筆
	GdipDrawRectangle(graphics, pen, 0, 0, 499, 499);//畫矩形
	GdipDeletePen(pen);//銷燬畫筆

	MyDrawText(mdc, L"神奇的分層視窗", 25, 8, 0, 0, true, ARGB(254,0,0,0));//畫文字,注意透明度用254

	GdipLoadImageFromFile(L"D:\\1.png", &image);//建立Image物件
	GdipDrawImageRect(graphics, image, 3, 3, 20, 20);//畫圖片
	GdipDisposeImage(image);//銷燬圖片,釋放資源

	GdipCreateSolidFill(ARGB(255, 255, 255, 255), &brush);
	GdipFillRectangle(graphics, brush, 5, 30, 490, 465);//這是畫裡面的背景
	GdipDeleteBrush(brush);

	GdipDeleteGraphics(graphics);//銷燬Graphics物件

	POINT point; point.x = 0; point.y = 0;//更新分層視窗
	SIZE size; size.cx = 500; size.cy = 500;
	BLENDFUNCTION blend;
	blend.BlendOp = AC_SRC_OVER;
	blend.BlendFlags = 0;
	blend.AlphaFormat = AC_SRC_ALPHA;
	blend.SourceConstantAlpha = 255;
	UpdateLayeredWindow(hwnd1, NULL, NULL, &size, mdc, &point, 0, &blend, ULW_ALPHA);//。。。。。。更新分層視窗

	//顯示、更新視窗
	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;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg){
	case WM_CREATE:
		return 0;
		break;
	case WM_DESTROY://視窗已經銷燬
		PostQuitMessage(0);//退出訊息迴圈,結束應用程式
		return 0;
		break;
	case WM_LBUTTONDOWN:
		//讓無邊框視窗能夠拖動(在視窗客戶區拖動)
		PostMessage(hwnd, WM_SYSCOMMAND, 61458, 0);
		break;
	default:
		break;
	}
	return DefWindowProc(hwnd, uMsg, wParam, lParam);//其他訊息交給系統處理
}

這裡要說明一下:

1。UpdateLayeredWindow以後就不能在WM_PAINT裡繪圖了,但是WM_PAINT訊息依舊響應。

2。需要作圖時,應該先把圖畫到記憶體DC裡,然後再呼叫UpdateLayeredWindow

3。或者在這個視窗上面再加一個視窗,在WM_MOVING裡面控制另一個視窗移動,要不然分層視窗上是不能放控制元件的,只能自己一個個畫上去