승적이익강 (勝敵而益强)
델파이 MDI 기초 본문
출처 : DelphiKorea - 일반강좌 - MDI 로 구현하는 MIS 프로젝트
=============================
MDI 로 구현하는 MIS 프로젝트
=============================
==================목차======================
1. 들어가며...
2. 간단한 MDI 프로그램 만들기
3. 메인폼을 다루기
1) 클라이언트 영역의 조절
2)메인폼에 그림넣기
3) 메인폼에 메시지 넣기
4)메인폼의 스크롤바 없애기
4. 차일드 폼 다루기
1)클래스 이름으로 차일드 생성하기
2)차일드가 보여지는 위치 고정하기
3)차일드가 한번만 실행되도록 하기
5. 메뉴 다루기
1) 창메뉴에 실행중인 차일드 리스트 보여주기
2) 메인폼의 메뉴를 차일드에서 실행하게 하기
6. 마치면서
============================================
1. 들어가며...
안녕하세요...뫼빛 이만준입다...
델파이로 MDI 프로그램을 만드는 작업이 그리 어려운 것은 아니지만...
자료가 여기저기 흩어져 있는 관계로 어려움을 좀 격는 것 같습니다.
그래서 정리한다는 생각으로 별로 아는 것은 없지만 글을 올립니다..
제목을 'MDI 로 구현하는 MIS 프로젝트' 로 정한 이유는
폼의 갯수가 몇백개 되는 MIS 프로젝트의 경우에 MDI 를 선호하기도 하고
일반적인 MDI 의 경우 차일드가 모두 같은 로직으로 구성되므로 별어려움이 없지만
MIS 의 경우는 모두 다른 로직을 가지게 되지요..
이런 차이점도 있구요....
2. 간단한 MDI 프로그램 만들기
1) File => New Application 을 하여 새 프로그램을 만든다.
2) 폼이 가지고 있는 프로퍼티 중에 FormStyle 이란게 있지요..
부모폼으로 사용하려면 이 속성을 fsMDIForm
자식폼으로 사용하려면 이 속성을 fsMDIChild 로 합니다.
지금은 부모폼이니까...fsMDIForm 로 해야겠죠...
3) TMainMenu 컴포넌트를 폼에 가져다 놓고 메뉴를 만든다
뭐 간단하게 '파일-차일드 생성' 으로 만들죠..
4) File => New 하여 새로운 폼을 추가한다.
자식폼으로 쓸거니까....
FormStyle 속성을 fsMDIChild 로 합니다.
OnClose 이벤트에서 Action := caFree; 라고 코딩을 하구요
Project => Options => Forms 에서 Form2 를 Available Forms 로 보냅니다.
5)메인폼의 메뉴 '차일드만들기' 에서 Form2 := TForm2.Create(Self); 라고 써줍니다.
6)이제 Run 을 하면 메시지 박스가 하나 뜨는데 'Yes' 하고요(uses 에 포함시키지 않아서 자동으로 포함시키는거니까)
다시 Run 을 하면 프로그램이 실행됩다....
여기까지 하면 간단하게 MDI 프로그램이 만들어 졌는데요...
-------------------------------------------------------------------
특이한 걸 한 번 보면
첫째로 차일드의 생성은 동적으로 해야 한다는 것이고요,
둘째로 Show 나 ShowModal 함수를 호출하지 않아도 보여진다는 것입니다...
소스를 뒤져봐야 하는데...하지 못했네여...관심있으면 한번 찾아보세요...
-------------------------------------------------------------------
3. 메인폼을 다루기
1) 클라이언트 영역의 조절
메인폼에다 panel 하나를 가져다 놓고 Align 을 Top 으로 하고 프로그램을 실행해보세요..
그리고 차일드를 생성시키면 panel 아래쪽에 보여질 겁니다...
즉, 판넬을 놓고 클라이언트 영역을 조절할 수 있다는 것이죠..
2)메인폼에 그림넣기
이 자료는 많이 있죠...그중 하나를 옮깁니다.
---------------------------------------------------------------------------------------
┌───────────────────────────────────┐
│ ▶ 번 호 : 31/38 ▶ 등록일 : 1998년 12월 02일 16:57 │
│ ▶ 제 목 : MDI 메인폼에 그림넣기
└───────────────────────────────────┘
[질문]
MDI를 이용하여 Application을 작성중입니다.
Main화면(Parent)에 이미지를 넣고 싶은데, Child화면에서와 같이 이미지 삽입이 되지 않는군요.
구체적인 방법을 가르쳐 주세요...
[답변] 민성기
unit Unit1;
interface
uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
FOldDefWndProc : Pointer;
FNewDefWndProc : Pointer;
procedure ClientWndProc ( var Msg: TMessage );
public
{ Public declarations }
end;
var Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.ClientWndProc;
var
ClientDC : HDC;
MemDC : TBitmap;
MemDCX : Integer;
MemDCY : Integer;
begin
case Msg.Msg of
WM_PAINT : begin
ClientDC := GetDC ( ClientHandle );
MemDC := TBitmap.Create;
MemDC.LoadFromFile ( 'C:ATHENA.BMP' );
MemDCX := 0;
MemDCY := 0;
repeat
repeat
BitBlt ( ClientDC, MemDCX, MemDCY, MemDC.Width,
MemDC.Height, MemDC.Canvas.Handle, 0, 0, SRCCOPY );
Inc ( MemDCX, MemDC.Width );
until MemDCX >= ClientWidth;
MemDCX := 0;
Inc ( MemDCY, MemDC.Height );
until MemDCY >= ClientHeight;
ReleaseDC ( ClientHandle, ClientDC );
MemDC.Free;
end;
end;
with Msg do
Result := CallWindowProc ( FOldDefWndProc, ClientHandle, Msg, wParam, lParam );
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
FNewDefWndProc := MakeObjectInstance ( ClientWndProc );
FOldDefWndProc := Pointer(SetWindowLong(ClientHandle,
GWL_WNDPROC,
LongInt ( FNewDefWndProc ) ) );
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
SetWindowLong (ClientHandle, GWL_WNDPROC, LongInt FOldDefWndProc));
FreeObjectInstance ( FNewDefWndProc );
end;
end.
--------------------------------------------------------------------------------------------------
3) 메인폼에 메시지 넣기
위의 그림 넣기 소스에서 아래 처럼 사용합니다..
var slSysBtn : DWord;
ClientDC : HDC;
R : TRect;
........
WM_PAINT : begin
ClientDC := GetDc(ClientHandle); //dc 를 얻고
SelectObject(ClientDc, Font.Handle); //Form의 폰트를 선택하고
clSysBtn := GetSysColor(COLOR_BTNFACE); //시스템 컬러(버튼색)을 얻고
//배경색을 지정하하고
SetBkColor(ClientDC, RGB(GetRValue(clSysBtn), GetGValue(clSysBtn), GetBValue(clSysBtn)));
R := ClientRect;
//쓰고싶은걸 쓴다.
DrawText(ClientDC, PChar('안녕하세유~'), Length('안녕하세유~'), R, DT_VCENTER);
ReleaseDC(ClientHandle, ClientDC ); //dc 를 놓아준다.
end;
__________________________________________________________________________________________
윈도 프로그램에서 문자를 출력하는 방법인데...
델파이에서는 VCL 로 감추고 있어서 별로 익숙하게 느껴지지 않지요...
내부적으로는 이런 함수들을 사용하지만요..
우리가 개발할때 프로그램 개발상황을 사용자들에게 보여주는데 사용했었지요..
_________________________________________________________________________________________
4)메인폼의 스크롤바 없애기
위의 소스에서 계속됩니다...
차일드가 메인폼에서 벋어나면 자동으로 스크롤바가 생깁니다.
이게 귀찮게 느껴질때가 있죠....
procedure TForm1.ClientWndProc(var Message: TMessage);
var B : Boolean;
begin
B := True;
case Message.Msg of
$3F : B := False;
end;
if B then with Message
do Result := CallWindowProc(FOldDefWndProc, ClientHandle, Msg, wParam, lParam);
end;
______________________________________________________________________________
스크롤바가 생기기 전에 메시지 값이 $3F 인 메시지가 발생합니다...
이 메시지를 받아서 델파이 MDI 메인 폼이 스크롤바를 만들기때문에
이 메시지를 삼켜 버리는거죠. 그럼 스크롤바가 생기지 않게 되죠..
______________________________________________________________________________
4. 차일드 폼 다루기
1)클래스 이름으로 차일드 생성하기
차일드 클래스의 이름만으로 차일드를 생성시키는 방법이 있습니다.
델파이가 비주얼 툴리기 때문에 이런 기능이 필요했겠지요..
(1) 차일드 유닛의 맨 아래에 다음라인을 추가한다.
initialization RegisterClasses([TForm2]);
컴포넌트에서 볼 수 있는 소스지요...클래스를 등록하면
'TForm2' 라는 클래스 이름으로 클래스를 찾을 수 가 있습니다.
(2) 이제 생성을 해보지요
function CreateChild(sClass : string):Boolean;
var MyFormClass : TFormClass;
WorkForm : TForm;
H : THandle;
begin
Result := True;
MyFormClass := TFormClass(GetClass(sClass)); //클래스를 찾아온다
if MyFormClass <> nil then begin
WorkForm := MyFormClass.Create(Application.MainForm);
end else Result := False;
end;
..........
메인 폼에서 Form2 := TForm2.Create(Self); 라고 생성했던 루틴을 지우고
CreateChild('TForm2');
라고 써보지요...역쉬...차일드가 생성될 겁니다..
_____________________________________________________________________________
이 기능은 MIS 프로젝트시 유용하게 이용할 수 있지요..
데이타베이스에다 클래스명을 넣어두고
사용자가 어떤 차일드를 실행하려고 하면 읽어와서
바로 생성해주면 되니까요...
매번 차일드를 생성할때마다 소스를 고칠 필요가 없어집니다..
-----------------------------------------------------------------------------
2)차일드가 보여지는 위치 고정하기
MDI 에서는 차일드가 생성될때 매번 역사선 모양으로 생성위치를 변경합니다..
하지만 항상 원하는 위치에서 시작하도록 할 수있습니다.
차일드의 OnShow 이벤트에서 Left := 0; Top := 0; 이라고 써넣으면
항상 클라이언트 영역의 (0, 0) 에 보여지게 됩니다...간단하네요...
3)차일드가 한번만 실행되도록 하기
위의 CreateChild 함수에서 차일드가 이미 생성되어 있는지 찾아야 하겠지요..
function CreateChild(sClass : string):Boolean;
var MyFormClass : TFormClass;
WorkForm : TForm;
H : THandle;
I : Integer;
begin
Result := True; H := 0;
//이미 생성되어있는지 찾는다.
for I := 0 to Screen.CustomFormCount - 1 do begin
if UpperCase('T' + Screen.CustomForms[I].Name) = UpperCase(sClass) then begin
H := Screen.CustomForms[I].Handle;
Break;
end;
end;
if H = 0 then begin //생성되어 있지 않으면 생성한다.
MyFormClass := TFormClass(GetClass(sClass));
if MyFormClass <> nil then begin
WorkForm := MyFormClass.Create(Application.MainForm);
end else Result := False;
end else begin //생성되어있으면 최상위 윈도우로 만든다.
if IsIconic(H) then ShowWindow(H, SW_SHOWNORMAL)
else BringWindowToTop(H);
end;
end;
5. 메뉴 다루기
1) 창메뉴에 실행중인 차일드 리스트 보여주기
MDI 에는 창 메뉴가 있지요...
차일드가 여러개이까...원하는 차일드를 쉽게 찾게 하고 차일드 정렬을 쉽게 하려구요..
실행되어 있는 차일드의 리스트를 보여주는 기능을 만들어보지요..
(1) 메뉴에다 '창' 메뉴를 추가하지요..
(2) '창' 의 OnClick 이벤트에서 아래처럼 코딩합니다.
procedure TFMdiMain.WinMenuClick(Sender: TObject);
var AddWnd : TMenuItem;
I : Integer;
begin
//추가된 것 삭제
for I := WinMenu.Count -1 downto 0 do //WinMenu 는 창메뉴입니다.
if WinMenu.Items[I].Tag <> 0 then WinMenu.Delete(I); //디자인시 창메뉴에 추가해 준 메뉴는
//Tag 값을 0 으로 준다.
//차일드 추가
if MDIChildCount > 0 then begin
for I := 0 to MDIChildCount - 1 do begin
AddWnd := TMenuItem.Create(Self);
AddWnd.Caption := MDIChildren[I].Caption;
AddWnd.Tag := MDIChildren[I].Handle; //차일드의 핸들을 Tag에 넣어준다.
//현재 활성화 되어 있는 차일드의 메뉴에 체크표시
if MDIChildren[I] = ActiveMDIChild then AddWnd.Checked := True;
AddWnd.OnClick := AddedWndClick; //차일드 메뉴를 클릭하면 그 차일드가 최상위로 와야 하니까
WinMenu.Add(AddWnd);
end;
end;
end;
________________________________________________________________________________________
어디서 이 작업을 해야 할까 ...고민을 했었는데....창메뉴를 클릭하는 순간 해주는게
가장 좋을 것 같더군요...
창메뉴 클릭시 메뉴에 추가 되어 있는 차일드를 모두 지우고
다시 생성되어 있는 차일드를 추가하는 것이지요
창 메뉴에 달려있는 계단식 배열 이나 바둑판식 배열같은 메뉴는
Tag 값을 0 으로 주고
삭제 할때 Tag 가 0 이 아닌 메뉴만 삭제하는 방식입니다..
차일드윈도를 나타내는 메뉴의 Tag에는 그 차일드의 Tag를 넣어두었다가
사용자가 그 메뉴 선택시 사용을 합니다..
------------------------------------------------------------------------------------------
차일드메뉴 클릭시 최상위로 이동시키는 루틴을 살펴보죠..
procedure TForm1.AddedWndClick(Sender : TObject);
var I : Integer;
begin//추가된 윈도우 메뉴 클릭시
BringWindowToTop(TMenuItem(Sender).Tag); //위에서 Tag에 핸들을 넣어둔 핸들을 사용한다.
end;
2) 메인폼의 메뉴를 차일드에서 실행하게 하기
각 차일드 폼마다 신규, 저장, 삭제, 조회 등의 공통된 버튼을 넣는 일은 이제 그만 합시다..
메인폼에 그런 버튼은 하나만 만들어 놓고 공통으로 사용하도록 해보지요..
폼 디자인 할때 작업도 줄어들고...
시스템 자원도 절약하고...프로그램 사이즈도 줄이고...
여러가지로 좋네요...하하...
사용자 정의 메시지를 이용해서 한번 해보지요..
차일드에다 아래처럼 선언해주고 구현해줍니다..
procedure WmCmdButtonClick(var MSG : TMessage);message WM_USER + 100;
procedure NewCommand; //신규버튼을 응답
procedure SearchCommand;//조회버튼 응답
procedure DeleteCommand; //삭제버튼 응답
procedure SaveCommand; //저장버튼 응답
.....
procedure TForm2.WmCmdButtonClick(var MSG : TMessage);
begin
case MSG.WParam of
11 : NewCommand;
12 : SearchCommand;
13 : DeleteCommand;
14 : SaveCommand;
end;
end;
NewCommand; SearchCommand; DeleteCommand; SaveCommand; 에다가
필요한 작업을 하는 루틴을 만들어야 겠지요..
procedure TForm2.NewCommand;
begin
ShowMessage('신규작업입니다.');
end;
procedure TForm2.SearchCommand;
begin
ShowMessage('조회작업입니다.');
end;
procedure TForm2.DeleteCommand;
begin
ShowMessage('삭제작업입니다.');
end;
procedure TForm2.SaveCommand;
begin
ShowMessage('저장작업입니다.');
end;
자, 이제 메인 폼에서 호출을 해야지요...
메뉴를 만듭니다..신규, 조회, 삭제, 저장 메뉴를
그리고 Tag 값을 신규는 11, 조회는 12, 삭제는 13, 저장은 14 로 합니다.
OnClick 이벤트에서 아래처럼 처리합니다..
if ActiveMDIChild <> nil
then SendMessage(ActiveMDIChild.Handle, WM_USER + 100, TMenuItem(Sender).Tag, 0);
여기까지 하면 끝입니다....좀 길었네요...
'Delphi' 카테고리의 다른 글
[델파이] 초간단 Log 파일 (0) | 2016.05.04 |
---|---|
[델파이]MDIChild Client Rect 변경 (0) | 2016.05.04 |
[altibase] 델파이 ODBC, OLEDB 연결 (0) | 2016.05.04 |