|
문서번호 |
2015 - BIT – S1 |
보존기간 |
영구 보존 |
보고일자 |
2015-03-19 |
MDI
BIT 29기
이정훈
목 차
◈ MDI(Multiple Document Interface)란?
◈ MDI의 구성(프레임, 클라이언트, 차일드)
◈ 시스템 MDI 지원
◈ MDI 메시지 처리
◈ MDI Exam(MDI 예제)
▣ MDI(Multiple Document Interface)란?
MDI란 Multiple Document Interface의 함축된 말로 여러 개의 문서를 인터페이스 즉 동시에 여러 개의 문서를 열 수 있는 프로그램 형태를 의미합니다. MDI는 동시에 여러 개의 윈도우를 열어놓고 작업할 수 있는 프로그램이며 대표적으로 ex)MS워드, 엑셀 등을 들 수 있으며 우리가 쓰고 있는 visual studio 6.0도 MDI 프로그램이다.
주요 시스템 파일을 한꺼번에 편집할 수 있는 Sysedit (시스템 구성편집기) 프로그램이 전형적인 MDI 프로그램이다.
▣ MDI의 구성(프레임, 클라이언트, 차일드)
SDI는 하나의 메인 윈도우만을 만들 수 있는데 MDI 프로그램은 세 가지 종류의 윈도우로 구성이 되어 있다. 이름만 봐서는 무엇인지 잘 모를 것이다. 이제 자세히 알아보자
1. 우선 제일 바깥쪽에 있는 프레임 윈도우(=메인 애플리케이션 윈도우)라고 하며 대체로 타이틀 바, 메뉴, 크기 조정 테두리, 시스템 메뉴 아이콘, 최소/최대/종료 버튼 등이 있다.
2. 클라이언트 윈도우는 종종 ‘워크스페이스(workspace)’라 하며, 일반 윈도우 애플리케이션과는 달리 프로그램의 출력을 표시하는 데 직접 사용되지 않는다.
3. 마지막으로 차일드 윈도우(=자식 윈도우)라고 하며 윈도우의 내부에 배치되어 있는 창이 차일드 윈도우고 실제로 사용자로부터 입력을 받아 문서 작업을 하는 주체이다.
프레임 윈도우가 메인 윈도우이며 그 차일드로 클라이언트 윈도우가 있고 나머지 차일드 윈도우는 모두 클라이언트 윈도우의 자식들이다. MDI 프로그램을 모두 만들기 위해서는 이 세가지 요소들을 모두 만들어야 한다.
Sysedit파일을 실행시켜서 이런 구조임을 확인할 수 있다.
▣ 시스템 MDI 지원
운영체제의 지원 중 가장 중요한 부분은 클라이언트 윈도우가 미리 만들어져 있다는 것이다. MDICLIENT라는 윈도우 클래스로 윈도우를 만들기만 하면 클라이언트 윈도우 기능이 지원이 된다. Sysedit을 실행하면 다섯 개의 차일드 윈도우가 열리는데 차일드들은 모두 클라이언트 윈도우 안에만 위치할 수 있으며 클라이언트 바깥쪽으로 나가면 잘린다.
MDI는 최소 세 개의 윈도우로 구성되며 이중 클라이언트 윈도우는 운영체제에 포함된 윈도우이며 운영체제에서 메시지를 처리하므로 윈도우 프로시저를 만들 필요가 없다. 나머지 2개의 윈도우인 프레임 차일드 윈도우는 프로시저를 만들어야 한다.
또한 MDI 프로그램의 키보드 단축키도 공짜로 지원된다.
Ex) Alt+- ->차일드 윈도우의 시스템 메뉴를 열어준다.
Alt+F4 ->차일드 윈도우를 닫는다.
Alt+F6 ->차일드 윈도우로 포커스를 옮긴다.
이 단축키들은 TranslateMDISysAccel함수에 의해 제공된다.
▣ MDI 메시지 처리
이와 같은 단축키들은 MDI 프로그래밍에 사용되는 메시지이며 클라이언트 윈도우로 전달되는
메시지들이다.
DefFrameProc과 DefMDIChildProc의 작업
출처 : 윈도우 API 정복(한빛 미디어)
▣ MDI Exam(MDI 예제)
<Program.cpp>
#include <Windows.h>
#include "MDIWndProc1.h"
#include "MDIChildProc1.h"
#include "resource.h"
LRESULT CALLBACK MDIWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK MDIChildProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);
HINSTANCE g_hInst; //인스턴스 핸들
HWND g_hFrameWnd; //메인 프레임 윈도우 핸들
HWND g_hMDIClient; //클라이언트 윈도우 핸들
LPWSTR lpszClass = TEXT("MDIExam"); //메인 윈도우의 윈도우 클래스
int ChildNum = 1;
INT APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR cmd, INT nCmdShow)
{
HWND hWnd;
MSG Message;
WNDCLASS WndClass;
g_hInst=hInstance;
//MDI Frame 윈도우 클래스 등록
WNDCLASS wndClass = {0, };
wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); //배경에 사용할 Brush(면) = 실행했을때 배경
wndClass.hCursor = LoadCursor(0,IDC_ARROW); // 0을 주면 운영체제에서 등록한 걸 defalt cursor(커서) 쓰겠다.
wndClass.hIcon = LoadIcon(0,IDI_APPLICATION);
wndClass.hInstance = hInstance;
wndClass.lpfnWndProc = MDIWndProc;
wndClass.lpszClassName = lpszClass;
wndClass.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
wndClass.style = 0; //
RegisterClass(&wndClass);
//위에는 클래스 스타일
hWnd=CreateWindow(
lpszClass,
lpszClass,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,(HMENU)NULL,hInstance,NULL);
//MDI Frame 윈도우 만듬
ShowWindow(hWnd,nCmdShow); // 윈도우 창 화면 출력
//위는 윈도우 스타일
g_hFrameWnd=hWnd;
//MDI Child 윈도우 등록
WndClass.lpszClassName = TEXT("MDIExamChild");
WndClass.lpfnWndProc = MDIChildProc;
WndClass.hIcon = LoadIcon(NULL,IDI_ASTERISK);
WndClass.lpszMenuName = NULL;
WndClass.cbWndExtra = sizeof(DWORD);
RegisterClass(&wndClass);
//메시지 루프
while(GetMessage(&Message,0,0,0)) //첫번째 인자를 가져오기위한 주소값
{
if(!TranslateMDISysAccel(g_hMDIClient,&Message))
{
TranslateMessage(&Message);
DispatchMessage(&Message); //CallBack 프로시져라고 함.
}
}
return (int)Message.wParam;
}
//프레임 윈도우의 메시지 프로시저
<MDIChildProc1.cpp>
#include "MDIChildProc1.h"
#include "resource.h"
//차일드 윈도우의 메시지 프로시저
CLIENTCREATESTRUCT ccs;
MDICREATESTRUCT mcs;
HINSTANCE g_hInst; //인스턴스 핸들
HWND g_hFrameWnd; //메인 프레임 윈도우 핸들
HWND g_hMDIClient; //클라이언트 윈도우 핸들
LPWSTR lpszClass = TEXT("MDIExam"); //메인 윈도우의 윈도우 클래스
int ChildNum = 1;
PAINTSTRUCT ps;
HDC hdc;
TCHAR str[128];
void OnPaint(HWND hWnd)
{
hdc = BeginPaint(hWnd,&ps);
wsprintf(str,TEXT("This is a MDI %dth Child window"), GetWindowLong(hWnd,0));
TextOut(hdc,0,0,str,lstrlen(str));
EndPaint(hWnd,&ps);
// WM_DESTROY 메시지를 처리하지 않아도 된다.
}
void OnCreate(HWND hWnd,WORD cid,WORD cmsg,HWND cWnd)
{
wsprintf(str,TEXT("Child %d"), ChildNum);
SetWindowLong(hWnd,0,ChildNum);
ChildNum++;
SetWindowText(hWnd,str);
}
LRESULT CALLBACK MDIChildProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
switch(iMessage)
{
case WM_CREATE:OnCreate(hWnd,LOWORD(wParam),HIWORD(wParam),(HWND)lParam); return TRUE;
case WM_PAINT: OnPaint(hWnd); return TRUE;
}
return(DefMDIChildProc(hWnd,iMessage,wParam,lParam));
}
<MDIWndProc1.cpp>
#include "MDIWndProc1.h"
#include "resource.h"
CLIENTCREATESTRUCT ccs;
MDICREATESTRUCT mcs;
HINSTANCE g_hInst; //인스턴스 핸들
HWND g_hFrameWnd; //메인 프레임 윈도우 핸들
HWND g_hMDIClient; //클라이언트 윈도우 핸들
LPWSTR lpszClass = TEXT("MDIExam"); //메인 윈도우의 윈도우 클래스
int ChildNum = 1;
void OnCreate(HWND hWnd,WORD cid,WORD cmsg,HWND cWnd)
{
//윈도우 만듬
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,(LPWSTR)&ccs);
ShowWindow(g_hMDIClient,SW_SHOW);
}
void OnDestroy(HWND hWnd)
{
PostQuitMessage(0);
}
void OnCommand(HWND hWnd,WORD cid,WORD cmsg,HWND cWnd)
{
switch(cid)
{
//새로운 차일드 윈도우를 만든다.
case ID_FILENEW:
mcs.szClass = TEXT("MDIExamChild");
mcs.szTitle = TEXT("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;
}
}
LRESULT CALLBACK MDIWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
//L = Long이라는 뜻
{
switch(iMessage)
{
case WM_CREATE: OnCreate(hWnd,LOWORD(wParam),HIWORD(wParam),(HWND)lParam); return TRUE;
case WM_COMMAND: OnCommand(hWnd,LOWORD(wParam),HIWORD(wParam),(HWND)lParam); return TRUE;
case WM_DESTROY: OnDestroy(hWnd); return TRUE;
}
return(DefFrameProc(hWnd,g_hMDIClient,iMessage,wParam,lParam));
//디폴트 프로시져
}
|