[Win32]多文档界面

本文由 Ocrosoft 于 2018-06-16 21:57:05 发表

像WPS,看起来没什么用的样子,不过还挺复杂的…

本质是多窗口,框架窗口->客户窗口->[文档窗口1,文档窗口2,文档窗口3…]

main.c

#include <windows.h>
#include "resource.h"

#define INIT_MENU_POS	0
#define HELLO_MENU_POS	2
#define RECT_MENU_POS	1

#define ID_FIRSTCHILD	50000

LRESULT CALLBACK FrameWndProc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK CloseEnumProc(HWND, LPARAM);
LRESULT CALLBACK HelloWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK RectWndProc(HWND, UINT, WPARAM, LPARAM);

typedef struct tagHELLODATA
{
	UINT iColor;
	COLORREF clrText;
}
HELLODATA, *PHELLODATA;

typedef struct tagRECTDATA
{
	short cxClient;
	short cyClient;
}
RECTDATA, *PRECTDATA;

TCHAR szAppName[] = TEXT("MDIDemo");
TCHAR szFrameClass[] = TEXT("MdiFrame");
TCHAR szHelloClass[] = TEXT("MdiHelloChild");
TCHAR szRectClass[] = TEXT("MdiRectChild");
HINSTANCE hInst;
HMENU hMenuInit, hMenuHello, hMenuRect;
HMENU hMenuInitWindow, hMenuHelloWindow, hMenuRectWindow;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
	HACCEL hAccel;
	HWND hwndFrame, hwndClient;
	MSG msg;
	WNDCLASS wndclass;

	hInst = hInstance;

	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = FrameWndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = hInstance;
	wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = szFrameClass;

	// 注册主窗口类
	if (!RegisterClass(&wndclass))
	{
		MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
		return 0;
	}

	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = HelloWndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = sizeof(HANDLE);
	wndclass.hInstance = hInstance;
	wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = szHelloClass;

	// 注册HelloWorld类
	RegisterClass(&wndclass);

	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = RectWndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = sizeof(HANDLE);
	wndclass.hInstance = hInstance;
	wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = szRectClass;

	// 注册随机矩形类
	RegisterClass(&wndclass);

	// 默认的菜单
	// 只有一个File
	hMenuInit = LoadMenu(hInstance, TEXT("MdiMenuInit"));
	// 打开了HelloWorld子窗口的菜单
	// 包含File、Color、Window
	hMenuHello = LoadMenu(hInstance, TEXT("MdiMenuHello"));
	// 打开了矩形子窗口的菜单
	// 包含File、Window
	hMenuRect = LoadMenu(hInstance, TEXT("MdiMenuRect"));

	// 相应菜单的Window菜单的位置,如HelloWrold的Window菜单是第3(pos2)个
	hMenuInitWindow = GetSubMenu(hMenuInit, INIT_MENU_POS);
	hMenuHelloWindow = GetSubMenu(hMenuHello, HELLO_MENU_POS);
	hMenuRectWindow = GetSubMenu(hMenuRect, RECT_MENU_POS);

	hAccel = LoadAccelerators(hInstance, szAppName);
	
	// 创建框架窗口
	hwndFrame = CreateWindow(szFrameClass, TEXT("MDI Demonstration"),
		WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
		CW_USEDEFAULT, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT,
		NULL, hMenuInit, hInstance, NULL);
	// 客户窗口在框架窗口的WM_CREATE消息中创建
	// 客户窗口是框架窗口的子窗口,可以使用GetWindow获取
	hwndClient = GetWindow(hwndFrame, GW_CHILD);

	ShowWindow(hwndFrame, iCmdShow);
	UpdateWindow(hwndFrame);

	while (GetMessage(&msg, NULL, 0, 0))
	{
		// TranslateMDISysAccel转换客户窗口的快捷键
		// TramslateAccelerator转换框架窗口的快捷键
		if (!TranslateMDISysAccel(hwndClient, &msg) &&
			!TranslateAccelerator(hwndFrame, hAccel, &msg))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	// DestroyMenu?
	DestroyMenu(hMenuHello);
	DestroyMenu(hMenuRect);

	return msg.wParam;
}

LRESULT CALLBACK FrameWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HWND        hwndClient;
	CLIENTCREATESTRUCT clientcreate;
	HWND               hwndChild;
	MDICREATESTRUCT    mdicreate;

	switch (message)
	{
	case WM_CREATE:
		// 要加入文档子菜单的子菜单句柄
		clientcreate.hWindowMenu = hMenuInitWindow;
		// 与文档列表中第一个文档窗口相关联的菜单ID
		clientcreate.idFirstChild = ID_FIRSTCHILD;

		// 使用MDICLIENT预定义类创建客户子窗口
		hwndClient = CreateWindow(TEXT("MDICLIENT"), NULL,
			WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE,
			0, 0, 0, 0, hwnd, (HMENU)1, hInst,
			(PSTR)&clientcreate);
		return 0;

	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
			// 新的HelloWorld文档窗口
		case ID_FILE_NEWHELLO:
			// mdicreate的成员和CreateWindow几乎完全相同
			mdicreate.szClass = szHelloClass;
			// 文档标题
			mdicreate.szTitle = TEXT("Hello");
			mdicreate.hOwner = hInst;
			mdicreate.x = CW_USEDEFAULT;
			mdicreate.y = CW_USEDEFAULT;
			mdicreate.cx = CW_USEDEFAULT;
			mdicreate.cy = CW_USEDEFAULT;
			mdicreate.style = 0;
			mdicreate.lParam = 0;

			hwndChild = (HWND)SendMessage(hwndClient, WM_MDICREATE, 0,
				(LPARAM)(LPMDICREATESTRUCT)&mdicreate);
			return 0;

			// 新的随机矩形窗口
		case ID_FILE_NEWRECT:
			mdicreate.szClass = szRectClass;
			mdicreate.szTitle = TEXT("Rectangles");
			mdicreate.hOwner = hInst;
			mdicreate.x = CW_USEDEFAULT;
			mdicreate.y = CW_USEDEFAULT;
			mdicreate.cx = CW_USEDEFAULT;
			mdicreate.cy = CW_USEDEFAULT;
			mdicreate.style = 0;
			mdicreate.lParam = 0;

			hwndChild = (HWND)SendMessage(hwndClient, WM_MDICREATE, 0,
				(LPARAM)(LPMDICREATESTRUCT)&mdicreate);
			return 0;

		case ID_FILE_CLOSE:
			// 获取活动子文档窗口的句柄
			hwndChild = (HWND)SendMessage(hwndClient, WM_MDIGETACTIVE, 0, 0);

			// 如果子窗口正常响应关闭
			if (SendMessage(hwndChild, WM_QUERYENDSESSION, 0, 0))
				// 关闭子窗口
				SendMessage(hwndClient, WM_MDIDESTROY, (WPARAM)hwndChild, 0);
			return 0;

		case ID_APP_EXIT:
			// 关闭,给自己发送WM_CLOSE
			SendMessage(hwnd, WM_CLOSE, 0, 0);
			return 0;

		case ID_WINDOW_TILE:
			SendMessage(hwndClient, WM_MDITILE, 0, 0);
			return 0;

		case ID_WINDOW_CASCADE:
			SendMessage(hwndClient, WM_MDICASCADE, 0, 0);
			return 0;

		case ID_WINDOW_ARRANGE:
			SendMessage(hwndClient, WM_MDIICONARRANGE, 0, 0);
			return 0;

		case ID_WINDOW_CLOSEALL: 
			// 对每个窗口调用CloseEnumProc
			EnumChildWindows(hwndClient, CloseEnumProc, 0);
			return 0;

		default:
			// 其他菜单消息,全部转发给文档窗口
			// 获取客户窗口中活动的文档窗口
			hwndChild = (HWND)SendMessage(hwndClient, WM_MDIGETACTIVE, 0, 0);
			// 如果是窗口,转发消息
			if (IsWindow(hwndChild))
				SendMessage(hwndChild, WM_COMMAND, wParam, lParam);

			break;
		}
		break;

	case WM_QUERYENDSESSION:
	case WM_CLOSE:
		// 关闭自身前,尝试关闭所有文档窗口
		SendMessage(hwnd, WM_COMMAND, ID_WINDOW_CLOSEALL, 0);

		// 客户窗口没有子窗口,说明没有文档窗口
		if (NULL != GetWindow(hwndClient, GW_CHILD))
			return 0;

		// 无法关闭所有文档窗口,不处理
		break;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}

	// 不处理的消息有DefrFrameProc处理(不是DefWindowProc)
	return DefFrameProc(hwnd, hwndClient, message, wParam, lParam);
}

BOOL CALLBACK CloseEnumProc(HWND hwnd, LPARAM lParam)
{
	if (GetWindow(hwnd, GW_OWNER))
		return TRUE;

	SendMessage(GetParent(hwnd), WM_MDIRESTORE, (WPARAM)hwnd, 0);

	if (!SendMessage(hwnd, WM_QUERYENDSESSION, 0, 0))
		return TRUE;

	SendMessage(GetParent(hwnd), WM_MDIDESTROY, (WPARAM)hwnd, 0);
	return TRUE;
}

// HelloWorld的窗口过程
LRESULT CALLBACK HelloWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static COLORREF clrTextArray[] = { 
		RGB(0,   0, 0), RGB(255, 0,   0),
		RGB(0, 255, 0), RGB(0, 0, 255),
		RGB(255, 255, 255) };
	static HWND     hwndClient, hwndFrame;
	HDC             hdc;
	HMENU           hMenu;
	PHELLODATA      pHelloData;
	PAINTSTRUCT     ps;
	RECT            rect;

	switch (message)
	{
	case WM_CREATE:
		pHelloData = (PHELLODATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HELLODATA));
		pHelloData->iColor = ID_COLOR_BLACK;
		pHelloData->clrText = RGB(0, 0, 0);
		SetWindowLong(hwnd, 0, (long)pHelloData);

		hwndClient = GetParent(hwnd);
		hwndFrame = GetParent(hwndClient);
		return 0;

	case WM_COMMAND:
		// 框架窗口转发过来的菜单消息
		switch (LOWORD(wParam))
		{
		case ID_COLOR_BLACK:
		case ID_COLOR_RED:
		case ID_COLOR_GREEN:
		case ID_COLOR_BLUE:
		case ID_COLOR_WHITE:
			pHelloData = (PHELLODATA)GetWindowLong(hwnd, 0);

			hMenu = GetMenu(hwndFrame);

			CheckMenuItem(hMenu, pHelloData->iColor, MF_UNCHECKED);
			pHelloData->iColor = wParam;
			CheckMenuItem(hMenu, pHelloData->iColor, MF_CHECKED);

			pHelloData->clrText = clrTextArray[wParam - ID_COLOR_BLACK];

			InvalidateRect(hwnd, NULL, FALSE);
		}
		return 0;

	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);

		pHelloData = (PHELLODATA)GetWindowLong(hwnd, 0);
		SetTextColor(hdc, pHelloData->clrText);

		GetClientRect(hwnd, &rect);

		DrawText(hdc, TEXT("Hello, World!"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);

		EndPaint(hwnd, &ps);
		return 0;

		// 窗口活动状态改变时的消息,可以用来更改菜单
	case WM_MDIACTIVATE:
		// 相等表示这个窗口是活动窗口(变成了活动窗口)
		if (lParam == (LPARAM)hwnd)
			// 修改菜单
			SendMessage(hwndClient, WM_MDISETMENU,
			(WPARAM)hMenuHello, (LPARAM)hMenuHelloWindow);

		pHelloData = (PHELLODATA)GetWindowLong(hwnd, 0);
		CheckMenuItem(hMenuHello, pHelloData->iColor,
			(lParam == (LPARAM)hwnd) ? MF_CHECKED : MF_UNCHECKED);

		if (lParam != (LPARAM)hwnd)
			SendMessage(hwndClient, WM_MDISETMENU, (WPARAM)hMenuInit,
			(LPARAM)hMenuInitWindow);

		DrawMenuBar(hwndFrame);
		return 0;

	case WM_QUERYENDSESSION:
	case WM_CLOSE:
		// 询问是否关闭
		if (IDOK != MessageBox(hwnd, TEXT("OK to close window?"),
			TEXT("Hello"), MB_ICONQUESTION | MB_OKCANCEL))
			return 0;

		break;

	case WM_DESTROY:
		// 释放空间
		pHelloData = (PHELLODATA)GetWindowLong(hwnd, 0);
		HeapFree(GetProcessHeap(), 0, pHelloData);
		return 0;
	}

	return DefMDIChildProc(hwnd, message, wParam, lParam);
}

LRESULT CALLBACK RectWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HWND hwndClient, hwndFrame;
	HBRUSH      hBrush;
	HDC         hdc;
	PRECTDATA   pRectData;
	PAINTSTRUCT ps;
	int         xLeft, xRight, yTop, yBottom;
	short       nRed, nGreen, nBlue;

	switch (message)
	{
	case WM_CREATE:
		pRectData = (PRECTDATA)HeapAlloc(GetProcessHeap(),
			HEAP_ZERO_MEMORY, sizeof(RECTDATA));

		SetWindowLong(hwnd, 0, (long)pRectData);

		SetTimer(hwnd, 1, 250, NULL);

		hwndClient = GetParent(hwnd);
		hwndFrame = GetParent(hwndClient);
		return 0;

	case WM_SIZE:
		// 非最小化状态,保存窗口大小
		if (wParam != SIZE_MINIMIZED)
		{
			pRectData = (PRECTDATA)GetWindowLong(hwnd, 0);

			pRectData->cxClient = LOWORD(lParam);
			pRectData->cyClient = HIWORD(lParam);
		}

		// 文档窗口的WM_SIZE必须被传递到DefWindowProc
		break;

	case WM_TIMER:
		// 随机绘制一个矩形
		pRectData = (PRECTDATA)GetWindowLong(hwnd, 0);
		xLeft = rand() % pRectData->cxClient;
		xRight = rand() % pRectData->cxClient;
		yTop = rand() % pRectData->cyClient;
		yBottom = rand() % pRectData->cyClient;
		nRed = rand() & 255;
		nGreen = rand() & 255;
		nBlue = rand() & 255;

		hdc = GetDC(hwnd);
		hBrush = CreateSolidBrush(RGB(nRed, nGreen, nBlue));
		SelectObject(hdc, hBrush);

		Rectangle(hdc, min(xLeft, xRight), min(yTop, yBottom),
			max(xLeft, xRight), max(yTop, yBottom));

		ReleaseDC(hwnd, hdc);
		DeleteObject(hBrush);
		return 0;

	case WM_PAINT:
		// 任何WM_PAINT消息都清空屏幕
		InvalidateRect(hwnd, NULL, TRUE);
		hdc = BeginPaint(hwnd, &ps);
		EndPaint(hwnd, &ps);
		return 0;

	case WM_MDIACTIVATE:
		// 修改菜单
		if (lParam == (LPARAM)hwnd)
			SendMessage(hwndClient, WM_MDISETMENU, (WPARAM)hMenuRect,
			(LPARAM)hMenuRectWindow);
		else
			SendMessage(hwndClient, WM_MDISETMENU, (WPARAM)hMenuInit,
			(LPARAM)hMenuInitWindow);

		DrawMenuBar(hwndFrame);
		return 0;

	case WM_DESTROY:
		pRectData = (PRECTDATA)GetWindowLong(hwnd, 0);
		HeapFree(GetProcessHeap(), 0, pRectData);
		KillTimer(hwnd, 1);
		return 0;
	}

	return DefMDIChildProc(hwnd, message, wParam, lParam);
}

resource.h

#define ID_FILE_NEWHELLO	40001
#define ID_FILE_NEWRECT		40002
#define ID_APP_EXIT			40003
#define ID_FILE_CLOSE		40004
#define ID_COLOR_BLACK		40005
#define ID_COLOR_RED		40006
#define ID_COLOR_GREEN		40007
#define ID_COLOR_BLUE		40008
#define ID_COLOR_WHITE		40009
#define ID_WINDOW_CASCADE	40010
#define ID_WINDOW_TILE		40011
#define ID_WINDOW_ARRANGE	40012
#define ID_WINDOW_CLOSEALL	40013

.rc

/////////////////////////////////////////////////////////////////////////////
// Menu

MDIMENUINIT MENU DISCARDABLE
BEGIN
POPUP "&File"
BEGIN
MENUITEM "New &Hello", ID_FILE_NEWHELLO
MENUITEM "New &Rectangle", ID_FILE_NEWRECT
MENUITEM SEPARATOR
MENUITEM "E&xit", ID_APP_EXIT
END
END

MDIMENUHELLO MENU DISCARDABLE
BEGIN
POPUP "&File"
BEGIN
MENUITEM "New &Hello", ID_FILE_NEWHELLO
MENUITEM "New &Rectangle", ID_FILE_NEWRECT
MENUITEM "&Close", ID_FILE_CLOSE
MENUITEM SEPARATOR
MENUITEM "E&xit", ID_APP_EXIT
END
POPUP "&Color"
BEGIN
MENUITEM "&Black", ID_COLOR_BLACK
MENUITEM "&Red", ID_COLOR_RED
MENUITEM "&Green", ID_COLOR_GREEN
MENUITEM "B&lue", ID_COLOR_BLUE
MENUITEM "&White", ID_COLOR_WHITE
END
POPUP "&Window"
BEGIN
MENUITEM "&Cascade\tShift+F5", ID_WINDOW_CASCADE
MENUITEM "&Tile\tShift+F4", ID_WINDOW_TILE
MENUITEM "Arrange &Icons", ID_WINDOW_ARRANGE
MENUITEM "Close &All", ID_WINDOW_CLOSEALL
END
END

MDIMENURECT MENU DISCARDABLE
BEGIN
POPUP "&File"
BEGIN
MENUITEM "New &Hello", ID_FILE_NEWHELLO
MENUITEM "New &Rectangle", ID_FILE_NEWRECT
MENUITEM "&Close", ID_FILE_CLOSE
MENUITEM SEPARATOR
MENUITEM "E&xit", ID_APP_EXIT
END
POPUP "&Window"
BEGIN
MENUITEM "&Cascade\tShift+F5", ID_WINDOW_CASCADE
MENUITEM "&Tile\tShift+F4", ID_WINDOW_TILE
MENUITEM "Arrange &Icons", ID_WINDOW_ARRANGE
MENUITEM "Close &All", ID_WINDOW_CLOSEALL
END
END

/////////////////////////////////////////////////////////////////////////////
// Accelerator

MDIDEMO ACCELERATORS DISCARDABLE
BEGIN
VK_F4, ID_WINDOW_TILE, VIRTKEY, SHIFT, NOINVERT
VK_F5, ID_WINDOW_CASCADE, VIRTKEY, SHIFT, NOINVERT
END

欢迎转载,请保留出处与链接。Ocrosoft » [Win32]多文档界面

点赞 (0)or拍砖 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址