这些都是初学者很容易迷惑的地方,我也是很迷惑,不过经过一番折腾终于弄明白这些问题了.对于这些问题,我想只用两个图片来说明最直观!如下:
1,在一个MDI(多文档)应用程序中每个概念如下图所标示:
效果如下图
1,窗口框架的客户区就是指图中的灰色部分:即蓝色方框内区域,框架的非客户区就是指蓝色方框之外的窗口区域,包括程序图标,标题栏,菜单,最大最小/关闭按纽,左右两个框和底下的状态条.
2,视图的客户区就是指图中绿色部分:即红色方框内的区域,视图的非客户区就是指视图部分红色方框之外的区域,包括:文件图标,标题栏,三个按纽和两个滚动条.
3,从图中可以看出,工具栏(工具条)不属于框架的非客户区,它跟视图是框架客户区内的"兄弟".它也是一个窗口,
**************************************
一个MDI的应用拥有下面三种窗口:框架窗口(frame window),MDI客户区窗口(MDI client window,即上图2中蓝色框,注意它也是一个窗口,故需要创建,在内存中药分配空间的,以句柄返回)以及一些子窗口(child window)。
框架窗口就像应用的主窗口,它的外观就是我们看到的拥有边框、标题栏、窗口菜单的那个窗口。
MDI应用并不使用框架窗口的客户区来直接输出,而是用客户区来显示MDI客户区窗口。这是一种特殊类型的子窗口,预先以窗口类名MDICLIENT注册过。它是框架窗口的一个子窗口,作为其他子窗口的背景而起作用,还提供创建和操纵其他子窗口的支持。例如,一个MDI的应用可以通过发送消息给MDI client window来创建、激活或者最大化子窗口。
当用户打开或者创建一个文档,客户区窗口为这个文档创建一个子窗口,它本身是所有其他子窗口的父窗口。所有的子窗口依附于客户区窗口,不能超出它的大小。
为了创建一个子窗口,一个MDI应用可以有两种选择,调用CreateMDIWindow()或者向MDI客户区窗口发送WM_MDICREATE消息。一种更有效地的方法是调用CreateWindowEx(),指定WS_EX_MDICHILD扩展风格。一个MDI应用中的线程可以使用CreateMDIWindow()或者CreateWindowEx()来创建另一个线程中的子窗口,而
WM_MDICREATE消息只是在同一线程中才能使用。
#include
#include "resource.h"
#define INIT_MENU_POS 0
#define RECT_MENU_POS 1
#define HELLO_MENU_POS 2
#define IDM_FIRSTCHILD 50000
LRESULT CALLBACK FrameWndProc(HWND,UINT,WPARAM,LPARAM);
LRESULT CALLBACK HelloWndProc(HWND,UINT,WPARAM,LPARAM);
BOOL CALLBACK CloseEnumProc(HWND,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;
RegisterClass(&wndclass);
wndclass.lpfnWndProc = RectWndProc;
wndclass.lpszClassName = szRectClass;
RegisterClass(&wndclass);
hAccel = LoadAccelerators(hInst,szAppName);
hMenuInit = LoadMenu(hInstance,TEXT("MdiMenuInit"));
hMenuHello = LoadMenu(hInstance,TEXT("MdiMenuHello"));
hMenuRect = LoadMenu(hInstance,TEXT("MdiMenuRect"));
hMenuInitWindow = GetSubMenu(hMenuInit,INIT_MENU_POS);
hMenuHelloWindow = GetSubMenu(hMenuHello,HELLO_MENU_POS);
hMenuRectWindow = GetSubMenu(hMenuRect,RECT_MENU_POS);
hwndFrame = CreateWindow(szFrameClass,TEXT("MDI Demonstration"),WS_OVERLAPPEDWINDOW |
WS_CLIPCHILDREN,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,
hMenuInit,hInstance,NULL);
//因为已经将WM_CREATE消息直接发送到了窗口函数,故hwndClient = CreateWindow(TEXT("MDICLIENT"),NULL,WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE,0,0,0,0,hwnd,(HMENU)1,hInst,(PSTR)&clientcreate);执行了。
//所以下面能够正确获取MDI Client Window句柄
hwndClient = GetWindow(hwndFrame,GW_CHILD);
ShowWindow(hwndFrame,iCmdShow);
UpdateWindow(hwndFrame);
while(GetMessage(&msg,NULL,0,0))
{
if(!TranslateMDISysAccel(hwndClient,&msg) && !TranslateAccelerator(hwndFrame,hAccel,&msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
DestroyMenu(hMenuHello);
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;
clientcreate.idFirstChild = IDM_FIRSTCHILD;
hwndClient = CreateWindow(TEXT("MDICLIENT"),NULL,WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE,
0,0,0,0,hwnd,(HMENU)1,hInst,(PSTR)&clientcreate);//"MDICLIENT"是预定义的类,
return 0;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDM_FILE_NEWHELLO:
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);//SendMessage消息的处理结果依赖于所发送的消息。
return 0;
case IDM_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;
SendMessage(hwndClient,WM_MDICREATE,0,(LPARAM)&mdicreate);
return 0;
case IDM_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 IDM_APP_EXIT:
SendMessage(hwnd,WM_CLOSE,0,0);
return 0;
case IDM_WINDOW_CASCADE:
SendMessage(hwndClient,WM_MDICASCADE,0,0);
return 0;
case IDM_WINDOW_TILE:
SendMessage(hwndClient,WM_MDITILE,0,0);
return 0;
case IDM_WINDOW_ARRANGE:
SendMessage(hwndClient,WM_MDIICONARRANGE,0,0);
return 0;
case IDM_WINDOW_CLOSEALL:
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,IDM_WINDOW_CLOSEALL,0);
if(NULL!=GetWindow(hwndClient,GW_CHILD))
{
return 0;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
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;
}
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 = IDM_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 IDM_COLOR_BLACK:
case IDM_COLOR_RED:
case IDM_COLOR_GREEN:
case IDM_COLOR_BLUE:
case IDM_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 - IDM_COLOR_BLACK];
InvalidateRect(hwnd,NULL,FALSE);
}
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_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_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;
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);
return 0;
case WM_SIZE:
if(wParam != SIZE_MINIMIZED)
{
pRectData = (PRECTDATA)GetWindowLong(hwnd,0);
pRectData->cxClient = LOWORD(lParam);
pRectData->cyClient = HIWORD(lParam);
}
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_MDIACTIVATE:
if((LPARAM)hwnd == lParam)
{
SendMessage(hwndClient,WM_MDISETMENU,(WPARAM)hMenuRect,(LPARAM)hMenuRectWindow);
}
else
{
SendMessage(hwndClient,WM_MDISETMENU,(WPARAM)hMenuInit,(LPARAM)hMenuInitWindow);
}
DrawMenuBar(GetParent(hwndClient));
return 0;
case WM_PAINT:
InvalidateRect (hwnd, NULL, TRUE) ;
hdc = BeginPaint (hwnd, &ps) ;
EndPaint (hwnd, &ps) ;
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);
}