[Win32 - 김상형윈도우즈API정복-정리노트]MDI Win32
2008/09/02 09:59 http://blog.naver.com/blue7red/100054479397 |
1.MDI
-
(1)정의
-동시에 여러 개의 문서를 열 수 있는 프로그램 형태를 의미한다.
-요즘은 프로그램을 MDI로 작성하는 것은 별로 권장되지는 않는다.
(2)MDI프로그램 구조
-프레임윈도우 : 메인윈도우
-클라이언트 윈도우
-차일드윈도우 :클라이언트윈도우의 자식윈도우다.
(3)시스템의 MDI지원
-일단 운영체제의 지원 중 가장 중요한 부분은 클라이언트 윈도우가 미리 만들어져 있다.
2.MDI기본예제
(1)예제
-
// 프레임 윈도우의 메시지 프로시저
LRESULT CALLBACK MDIWndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
CLIENTCREATESTRUCT ccs;
MDICREATESTRUCT mcs;
switch (iMessage) {
case WM_CREATE:
// MDI Client 윈도우 만듬
ccs.hWindowMenu=GetSubMenu(GetMenu(hWnd), 1);
ccs.idFirstChild=IDM_WINDOWCHILD;
g_hMDIClient=CreateWindow("MDICLIENT", NULL, WS_CHILD | WS_VSCROLL |
WS_HSCROLL | WS_CLIPCHILDREN,
0,0,0,0,hWnd,(HMENU)NULL, g_hInst, (LPSTR)&ccs);
ShowWindow(g_hMDIClient, SW_SHOW);
return 0;
case WM_COMMAND:
switch (LOWORD(wParam)) {
// 새로운 차일드 윈도우를 만든다.
case ID_FILENEW:
mcs.szClass="MDIExamChild";
mcs.szTitle="Child";
mcs.hOwner=g_hInst;
mcs.x=mcs.y=CW_USEDEFAULT;
mcs.cx=mcs.cy=CW_USEDEFAULT;
mcs.style=MDIS_ALLCHILDSTYLES;
SendMessage(g_hMDIClient, WM_MDICREATE, 0,
(LPARAM)(LPMDICREATESTRUCT)&mcs);
break;
// 바둑판식 정렬
case ID_WIN_TILE:
SendMessage(g_hMDIClient, WM_MDITILE,
(WPARAM)MDITILE_HORIZONTAL, 0);
break;
// 계단식 정렬
case ID_WIN_CASCADE:
SendMessage(g_hMDIClient, WM_MDICASCADE,
(WPARAM)MDITILE_SKIPDISABLED, 0);
break;
// 아이콘 정렬
case ID_WIN_ARRANGE:
SendMessage(g_hMDIClient, WM_MDIICONARRANGE, 0, 0);
break;
}
break; // 여기서 "return 0"하면 안된다. 반드시 break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return(DefFrameProc(hWnd,g_hMDIClient,iMessage,wParam,lParam));
}
// 차일드 윈도우의 메시지 프로시저
LRESULT CALLBACK MDIChildProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
TCHAR str[128];
switch (iMessage) {
case WM_CREATE:
wsprintf(str, "Child %d", ChildNum);
SetWindowLong(hWnd, 0, ChildNum);
ChildNum++;
SetWindowText(hWnd, str);
return 0;
case WM_PAINT:
hdc=BeginPaint(hWnd, &ps);
wsprintf(str,"This is a MDI %dth Child window", GetWindowLong(hWnd, 0));
TextOut(hdc,0,0,str,lstrlen(str));
EndPaint(hWnd, &ps);
return 0;
// WM_DESTROY 메시지를 처리하지 않아도 된다.
}
return(DefMDIChildProc(hWnd,iMessage,wParam,lParam));
}
(2)구성요소 만들기
-윈도우 클래스가 MDICLIENT로 고정되어 있다
-클라이언트 윈도우는 운영체제에 미리정의되어 있으므로 윈도우클래스를 등록할 필요가 없다.
-클라이언트 윈도우에는 특별한 스타일이 필요하다.
-WS_CLIPCHIDREN스타일을 반드시 지정해야한다.
그렇지 않으면 클라이언트 윈도우가 다시 그려질 때 차일드까지 같이 그려져야 하므로
효율상 , 미관상 좋지 않다.
-MDI의 차일드윈도우는 메뉴를 가질 수 없으므로 lpzsMenuName은 반드시 null이어야한다.
-윈도우 클래스의 여분의 메모리는 차일드 윈도우의 고유정보를 기록하는데 사용할 수 있다.
-차일드 윈도우를 만들 때 클라이언트 윈도우로 WM_MDICREATE메시지를 보내면 된다.
다른방법으로는 CreateMDIWindow함수를 사용해서 생성할 수도 있다.
(3)MDI의 메시지 처리
-WM_MDIACTIVE:차일드 윈도우 활성화
-WM_MDICASCADE:계단식 정렬
-WM_MDICREATE:차일드윈도우 생성
-WM_MDIDESTROY:차일드윈도우 파괴
-WM_MDIGETACTIVE:현재 활성화된 차일드의 핸들을 리턴한다.
-WM_MDIICONARRANGE:최소화된 아이콘 정렬
-WM_MDIMAXIMIZE:최대화
-WM_MDINEXT:지정한 차일드 윈도우의 앞 또는 뒤쪽 차일드 윈도우를 활성화시킨다.
-WM_MDIREFRESHMENU:Window메뉴를 리프레시시킨다. 이메시지 이후 DrawMenuBar함수를 호출하여 메뉴를 갱신해야된다.
-WM_MDIRESTORE:원래 크기로 복원
-WM_MDISETMENU:메뉴를 변경한다.
-WM_MDITITLE: 바둑판식 재정렬
TranslateMDISysAccel : WM_KEYDOWN메시지를 WM_SYSCOMMAND 메시지로 변경하여 MDI차일드 윈도우로 보내는 역할을 한다.
DefFrameProc : MDIWindowProc에서 처리하지 않은 메시지를 DefWindowProc이 아닌 DefFrameProc으로 전달한다.
=>WM_COMMAND,WM_MENUCHAR,WM_SETFOCUS,WM_SIZE메시지를 반드시 처리해야 한다.
설사 MDIWndProc에서 이 메시지 중 한 가지를 처리했다고 하더라도 반드시 DefFrameProc으로 전달하는 것이 좋다.
차일드 윈도우의 메시지 처리함수도 프레임 윈도우의 메시지도 DefMDIChildProc으로 전달되어야 한다.
(4)차일드 윈도우의 정렬
-MDITITLE_HORIZONTAL:수평으로 정렬
-MDITITLE_SKIPDISABLED: 사용금지된 차일드윈도우는 정렬에서 제외된다.
-MDITITLE_VERTICAL: 수직으로 정렬
(5)여분의 메모리
-각 차일드 윈도우는 일반적으로 같은 윈도우 프로시저를 공유하기 때문에 동작이 동일할 수 밖에 없다.
-차일드마다 다른 고유값은 차일드윈도우 클래스의 여분 메모리에 저장된다.
3.MDI고급
(1)MDI프레임의 작업영역
-
// 프레임 윈도우의 메시지 프로시저
LRESULT CALLBACK MDIWndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
CLIENTCREATESTRUCT ccs;
MDICREATESTRUCT mcs;
switch (iMessage) {
case WM_CREATE:
// MDI Client 윈도우 만듬
ccs.hWindowMenu=GetSubMenu(GetMenu(hWnd), 1);
ccs.idFirstChild=IDM_WINDOWCHILD;
g_hMDIClient=CreateWindow("MDICLIENT", NULL, WS_CHILD | WS_VSCROLL |
WS_HSCROLL | WS_CLIPCHILDREN,
0,0,0,0,hWnd,(HMENU)NULL, g_hInst, (LPSTR)&ccs);
ShowWindow(g_hMDIClient, SW_SHOW);
hList=CreateWindow("listbox",NULL,WS_CHILD | WS_VISIBLE | WS_BORDER |
LBS_NOINTEGRALHEIGHT,0,0,0,0,hWnd,(HMENU)1,g_hInst,NULL);
hEdit=CreateWindow("edit",NULL,WS_CHILD | WS_VISIBLE | WS_BORDER |
ES_MULTILINE,10,10,200,25,hWnd,(HMENU)2,g_hInst,NULL);
SendMessage(hList,LB_ADDSTRING,0,(LPARAM)"리스트 박스입니다.");
SetWindowText(hEdit,"에디트 박스입니다.");
return 0;
case WM_SIZE:
if (wParam != SIZE_MINIMIZED) {
MoveWindow(hList,0,0,200,HIWORD(lParam),TRUE);
MoveWindow(hEdit,200,HIWORD(lParam)-100,LOWORD(lParam)-200,100,TRUE);
MoveWindow(g_hMDIClient,200,0,LOWORD(lParam)-200,HIWORD(lParam)-100,TRUE);
}
return 0;
case WM_COMMAND:
switch (LOWORD(wParam)) {
// 새로운 차일드 윈도우를 만든다.
case ID_FILENEW:
mcs.szClass="MDIExamChild";
mcs.szTitle="Child";
mcs.hOwner=g_hInst;
mcs.x=mcs.y=CW_USEDEFAULT;
mcs.cx=mcs.cy=CW_USEDEFAULT;
mcs.style=MDIS_ALLCHILDSTYLES;
SendMessage(g_hMDIClient, WM_MDICREATE, 0,
(LPARAM)(LPMDICREATESTRUCT)&mcs);
break;
// 바둑판식 정렬
case ID_WIN_TILE:
SendMessage(g_hMDIClient, WM_MDITILE,
(WPARAM)MDITILE_HORIZONTAL, 0);
break;
// 계단식 정렬
case ID_WIN_CASCADE:
SendMessage(g_hMDIClient, WM_MDICASCADE,
(WPARAM)MDITILE_SKIPDISABLED, 0);
break;
// 아이콘 정렬
case ID_WIN_ARRANGE:
SendMessage(g_hMDIClient, WM_MDIICONARRANGE, 0, 0);
break;
}
break; // 여기서 "return 0"하면 안된다. 반드시 break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return(DefFrameProc(hWnd,g_hMDIClient,iMessage,wParam,lParam));
}
// 차일드 윈도우의 메시지 프로시저
LRESULT CALLBACK MDIChildProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
TCHAR str[128];
switch (iMessage) {
case WM_CREATE:
wsprintf(str, "Child %d", ChildNum);
SetWindowLong(hWnd, 0, ChildNum);
ChildNum++;
SetWindowText(hWnd, str);
return 0;
case WM_PAINT:
hdc=BeginPaint(hWnd, &ps);
wsprintf(str,"This is a MDI %dth Child window", GetWindowLong(hWnd, 0));
TextOut(hdc,0,0,str,lstrlen(str));
EndPaint(hWnd, &ps);
return 0;
// WM_DESTROY 메시지를 처리하지 않아도 된다.
}
return(DefMDIChildProc(hWnd,iMessage,wParam,lParam));
}
(2)복수 개의 차일드
-
#include <windows.h>
#include "resource.h"
// 프레임 윈도우와 차일드의 윈도우 프로시저
LRESULT CALLBACK MDIWndProc(HWND,UINT,WPARAM,LPARAM);
LRESULT CALLBACK MDIDrawProc(HWND,UINT,WPARAM,LPARAM);
LRESULT CALLBACK MDIEditProc(HWND,UINT,WPARAM,LPARAM);
// 전역 변수들
LPSTR lpszClass=TEXT("MultiMDI");
HINSTANCE g_hInst;
HWND g_hFrameWnd;
HWND g_hMDIClient;
HMENU hMenu1, hMenu2, hMenu3;
HMENU hMenu1W,hMenu2W,hMenu3W;
int EditNum=1;
int DrawNum=1;
// 텍스트 에디터 차일드의 개별 정보 구조체
struct tagEditData {
int Num;
HWND hEdit;
};
int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance
,LPSTR lpszCmdParam,int nCmdShow)
{
HWND hWnd;
MSG Message;
WNDCLASS WndClass;
g_hInst=hInstance;
// 메인 윈도우(프레임 윈도우) 클래스 등록
WndClass.cbClsExtra=0;
WndClass.cbWndExtra=0;
WndClass.hbrBackground=(HBRUSH)COLOR_APPWORKSPACE+1;
WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
WndClass.hInstance=hInstance;
WndClass.lpfnWndProc=MDIWndProc;
WndClass.lpszClassName=lpszClass;
WndClass.lpszMenuName=NULL;
WndClass.style=0;
RegisterClass(&WndClass);
// 프레임 윈도우 만듬
hWnd=CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
NULL,(HMENU)NULL,hInstance,NULL);
ShowWindow(hWnd,nCmdShow);
g_hFrameWnd=hWnd;
// 텍스트 에디터 차일드 윈도우 클래스 등록
WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
WndClass.lpszClassName=TEXT("MDIExamEdit");
WndClass.lpfnWndProc=MDIEditProc;
WndClass.hIcon=LoadIcon(NULL,IDI_ASTERISK);
WndClass.lpszMenuName=NULL;
WndClass.cbWndExtra=sizeof(DWORD_PTR);
RegisterClass(&WndClass);
// 그래픽 에디터 차일드 윈도우 클래스 등록
WndClass.lpszClassName=TEXT("MDIExamDraw");
WndClass.lpfnWndProc=MDIDrawProc;
WndClass.hIcon=LoadIcon(NULL,IDI_ERROR);
WndClass.lpszMenuName=NULL;
WndClass.cbWndExtra=sizeof(DWORD);
WndClass.style=CS_DBLCLKS;
RegisterClass(&WndClass);
while (GetMessage(&Message,NULL,0,0)) {
if (!TranslateMDISysAccel(g_hMDIClient, &Message)) {
TranslateMessage(&Message);
DispatchMessage(&Message);
}
}
return Message.wParam;
}
LRESULT CALLBACK MDIWndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
CLIENTCREATESTRUCT ccs;
MDICREATESTRUCT mcs;
switch (iMessage) {
case WM_CREATE:
// 각 차일드의 메뉴 핸들과 윈도우 메뉴를 미리 구해 놓는다.
hMenu1=LoadMenu(g_hInst,MAKEINTRESOURCE(IDR_MENU1));
hMenu2=LoadMenu(g_hInst,MAKEINTRESOURCE(IDR_MENU2));
hMenu3=LoadMenu(g_hInst,MAKEINTRESOURCE(IDR_MENU3));
hMenu1W=GetSubMenu(hMenu1,0);
hMenu2W=GetSubMenu(hMenu2,1);
hMenu3W=GetSubMenu(hMenu3,1);
// 차일드가 없을 때는 hMenu1을 사용한다.
SetMenu(hWnd,hMenu1);
// 프레임 윈도우 만듬
ccs.hWindowMenu=hMenu1W;
ccs.idFirstChild=IDM_WINDOWCHILD;
g_hMDIClient=CreateWindow(TEXT("MDICLIENT"), NULL, WS_CHILD | WS_VSCROLL |
WS_HSCROLL | WS_CLIPCHILDREN,
0,0,0,0,hWnd,(HMENU)NULL, g_hInst, (LPSTR)&ccs);
ShowWindow(g_hMDIClient, SW_SHOW);
return 0;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case ID_FILE_NEWEDIT:
mcs.szClass=TEXT("MDIExamEdit");
mcs.szTitle=TEXT("Edit");
mcs.hOwner=g_hInst;
mcs.x=mcs.y=CW_USEDEFAULT;
mcs.cx=mcs.cy=CW_USEDEFAULT;
mcs.style=MDIS_ALLCHILDSTYLES;
SendMessage(g_hMDIClient, WM_MDICREATE, 0,
(LPARAM)(LPMDICREATESTRUCT)&mcs);
break;
case ID_FILE_NEWDRAW:
mcs.szClass=TEXT("MDIExamDraw");
mcs.szTitle=TEXT("Draw");
mcs.hOwner=g_hInst;
mcs.x=mcs.y=CW_USEDEFAULT;
mcs.cx=mcs.cy=CW_USEDEFAULT;
mcs.style=MDIS_ALLCHILDSTYLES;
SendMessage(g_hMDIClient, WM_MDICREATE, 0,
(LPARAM)(LPMDICREATESTRUCT)&mcs);
break;
case ID_WIN_TILE:
SendMessage(g_hMDIClient, WM_MDITILE,
(WPARAM)MDITILE_HORIZONTAL, 0);
break;
case ID_WIN_CASCADE:
SendMessage(g_hMDIClient, WM_MDICASCADE,
(WPARAM)MDITILE_SKIPDISABLED, 0);
break;
case ID_WIN_ARRANGE:
SendMessage(g_hMDIClient, WM_MDIICONARRANGE, 0, 0);
break;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return(DefFrameProc(hWnd,g_hMDIClient,iMessage,wParam,lParam));
}
// 텍스트 에디터의 윈도우 프로시저
LRESULT CALLBACK MDIEditProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
TCHAR str[128];
HWND hEdit;
tagEditData *pED;
switch (iMessage) {
case WM_CREATE:
// 캡션에 차일드 번호를 출력한다.
wsprintf(str, TEXT("Edit %d"), EditNum);
SetWindowText(hWnd, str);
// 클라이언트 영역에 에디트 컨트롤을 배치한다.
hEdit=CreateWindow(TEXT("edit"),NULL,WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL
| ES_MULTILINE | ES_AUTOVSCROLL | WS_HSCROLL | WS_VSCROLL,
0,0,0,0,hWnd,(HMENU)100,g_hInst,NULL);
// 에디트 컨트롤의 핸들과 차일드 번호를 구조체에 작성한 후
// 여분 메모리에 이 포인터를 저장한다.
pED=(tagEditData *)malloc(sizeof(tagEditData));
pED->hEdit=hEdit;
pED->Num=EditNum;
SetWindowLongPtr(hWnd,0,(LONG)pED);
EditNum++;
return 0;
case WM_MDIACTIVATE:
// 활성화될 때 자신의 메뉴를 프레임에 부착시킨다.
if (lParam==(LPARAM)hWnd)
SendMessage(g_hMDIClient,WM_MDISETMENU,
(WPARAM)hMenu3,(LPARAM)hMenu3W);
else
SendMessage(g_hMDIClient,WM_MDISETMENU,
(WPARAM)hMenu1,(LPARAM)hMenu1W);
DrawMenuBar(g_hFrameWnd);
return 0;
//구조체를 할당한 후 그 포인터를 저장하는 것이 여분 메모리를 활용하는 가장 일반적인 방법이다.
case WM_SIZE:
pED=(tagEditData *)GetWindowLongPtr(hWnd,0);
MoveWindow(pED->hEdit, 0, 0, LOWORD(lParam), HIWORD(lParam),TRUE);
return 0;
case WM_SETFOCUS:
pED=(tagEditData *)GetWindowLongPtr(hWnd,0);
SetFocus(pED->hEdit);
return 0;
case WM_DESTROY:
pED=(tagEditData *)GetWindowLongPtr(hWnd,0);
free(pED);
break;
}
return(DefMDIChildProc(hWnd,iMessage,wParam,lParam));
}
// 드로우의 윈도우 프로시저
LRESULT CALLBACK MDIDrawProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
HDC hdc;
TCHAR str[128];
static int x;
static int y;
static BOOL bNowDraw=FALSE;
switch (iMessage) {
case WM_CREATE:
wsprintf(str, TEXT("Draw %d"), DrawNum);
SetWindowLong(hWnd, 0, DrawNum);
DrawNum++;
SetWindowText(hWnd, str);
return 0;
case WM_MDIACTIVATE: //메뉴를 변경할 시점이다.: 차일드 윈도우 내에서 포커스가 이동할 때이기 때문이다.
if (lParam==(LPARAM)hWnd)
SendMessage(g_hMDIClient,WM_MDISETMENU,
(WPARAM)hMenu2,(LPARAM)hMenu2W);
else
SendMessage(g_hMDIClient,WM_MDISETMENU,
(WPARAM)hMenu1,(LPARAM)hMenu1W);
DrawMenuBar(g_hFrameWnd);
return 0;
case WM_LBUTTONDOWN:
x=LOWORD(lParam);
y=HIWORD(lParam);
bNowDraw=TRUE;
return 0;
case WM_MOUSEMOVE:
if (bNowDraw==TRUE) {
hdc=GetDC(hWnd);
MoveToEx(hdc,x,y,NULL);
x=LOWORD(lParam);
y=HIWORD(lParam);
LineTo(hdc,x,y);
ReleaseDC(hWnd,hdc);
}
return 0;
case WM_LBUTTONUP:
bNowDraw=FALSE;
return 0;
case WM_LBUTTONDBLCLK:
InvalidateRect(hWnd, NULL, TRUE);
return 0;
}
return(DefMDIChildProc(hWnd,iMessage,wParam,lParam));
}
'API' 카테고리의 다른 글
MAKEINTRESOURCE 매크로 분석 (1) | 2008.09.08 |
---|---|
win32/DynamicLinkLibrary(dll) - Good (0) | 2008.09.05 |
큰 사이즈 그래픽(그림, 이미지) 화일 처리시 팁 (1) | 2008.09.05 |
Dll 재배치, 로딩, 구현 (0) | 2008.09.03 |
윈도우 메시지 종류 및 큐잉, 비큐잉 메시지 (0) | 2008.09.01 |
WM_PAINT의 비밀...? - 메시지 큐잉 관련 좋은 글....비큐 메시지/큐 메시지 정확한 구분 (0) | 2008.08.31 |
WM_PAINT 메시지의 특별한 동작 (0) | 2008.08.31 |