본문 바로가기

work/delphi

최소화시 애니메션 되게하는법!

왜? 델파이로 만든 프로그램은 최소화 애니메이션이 안되는가?

한번은 심심해서 이 문제를 가지고 VCL소스를 보면서 연구를 해 보았죠
그래서 제가 나름대로 지은 결론입니다. 그리고 해결방법도 제시해 보
았습니다.

먼저 왜 안되는가를 따지기 전에 윈95는 언제 애니메이션이 발생하게
하는가를 따져볼 필요가 있습니다.

★ 윈도95는 핸들이 있는 윈도가 Minimize, Maxmize 혹은 Restore(보통상태)
될때 애니메이션이 발생하게 한다.

별로 특별한 내용이 아닌것 같지만 의미가 있습니다. 애니메이션이 되는 것은
폼이 아니라 핸들이 있는 윈도라는 것입니다. 즉 폼에 Panel을 하나 놓고 
버튼도 하나 놓고 버튼의 OnClick에 이렇게 코딩해 보세요

ShowWindow( Panel1.Handle, SW_MINIMIZE );

그러면 Panel1이 폼안에서 최소화가 되면서 애니메이션이 보여집니다. 다시
말해서 애니메이션이 발생하는것은 폼만이 아니라는 얘기입니다.

★ 윈도95의 Explorer은 각 프로그램의 메인 핸들이 최소화 될때 그것을 가
로채어 자신이 버튼형식으로 표현한다.

문제는 여기에 있습니다. 최대화 될때는 Explorer가 관리를 하지 않지만
최소화 될때는 Explorer가 감시를 합니다. 그래서 그것이 프로그램의 메인
핸들이라면 가로채지요 즉 메인핸들은 놔두고 폼만 최소화가 되면 그 폼은 
Explorer안으로 들어가는것이 아니라 그냥 바탕화면에 밑에 있습니다. 그
런데 비주얼C++이나 비주얼베이직으로 프로그램을 만들면 메인폼의 핸들이
메인핸들이 되는데 우리의 델파이는 그렇지가 않습니다. 메일 폼이 생성되
기전에 Application이 처음으로 생성됩니다. 프로젝트 소스를 보면 알수
있지요 그래서 우리의 델파이로 만든 프로그램의 메인핸들은
Application.Handle이 메인 핸들입니다.TForm의 소스를 보면 실제로 폼이 
최소화 메세지를 받으면 
현재 폼이 Application의 MainForm인가를 확인해서 아니면 그냥 최소화를 
하고 맞으면 폼이 최소화 되는것이 아니라 Application이 최소화 되게 되어
있습니다. 그럼 소스를 다시 폼에서 Application쪽으로 옮겨서 봐야겠군요
왜냐 Application이 최소화 될때 애니메이션이 발생하지 않으니깐요 그랬더
니 거기에 기가 막힌 문장이 있더라고요 Application.Minimize에 
ShowWinNoAnimate라는 것이 있더라구요 이름만 봐도 알겠죠? 그래서 이거
를 빼버렸더니 되기는 되는데 메인폼의 위치에서 밑으로 내려 가는것이 아니
라 화면의 중앙에서 한개의 점으로 있다가 밑으로 오히려 커지면서 내려가
더라고요 폼이 어디에 있건간에.. 그래서 곰곰히 생각을 해보았는데 아마 
Application은 어차피 눈에 보이는것이 아니니깐 그냥 무조건 화면의 중앙
에 가로세로크기가 0인 체로 존재하고 있는것이라고 결론을 짓고 
Application.Minimize안에 Application의 위치와 크기를 메인폼과 꼭같게
한다음 최소화가 되도록 했더니 쩝~ 되기는 되는데 뭐 이상하게 되더라구요
요는 Application의 윈도 스타일이 적합하지 않았나봐요 그래서 Application
이 생성될때 스타일을 적절히 조절했더니 거의 완벽하게 됐습니다.

그래서 결론은 윈도95의 Explorer는 프로그램의 메인핸들이 최소화 될때
그것을 가로채어 자신이 가지고 있는데 델파이의 메인폼은 메인핸들이 아니
다. 그래서 델파이의 메인폼이 최소화 될때는 Application이 최소화 되도록
해 놓았다. 그런데 Application은 폼이 아니기때문데 형태를 갖추고 있지
않다. 그래서 Application이 최소화될때는 아예 애니메이션이 안되도록 
해 놓았다 그리하야 그거를 되게 하려면 Application의 윈도스타일을 최소화
되는 순간에만 눈에 보이는 스타일로 바꾸고 일단 최소화가 되면 눈에 안보
이게 해야한다. 왜? 다시 메인폼이 활성화되면 그옆에 Application도 보이
게 되니깐..(Application도 윈도라는점..) 그리고 Application.Minimize에
ShowWinNoAnimate를 제거한다. 그러면 제법 비슷하게 됩니다. 애니메이션이
되게 하는 콤포넌트들도 몇개 나와있는데 그것들보다도 더 완벽하게 되는것
같습니다. 어찌되었건 결론은 Forms.pas소스 한개를 고쳐서 Lib디렉토리에 
넣어두면 된다는거였습니다. 아래소스는 바로그 Forms.pas를 위에 설명한데
로 몇줄 고친부분입니다. 그러니 이걸 다운받아서 Source/Vcl디렉토리에
있는 Forms.pas의 같은 부분을 찾아 아래소스의 내용으로 바꾸고 그걸
Lib디렉토리에 Forms.pas라는 이름으로 넣어두면 됩니다. 
참고로 소스중 주석표시중에 /// (슬러시 세개)는 제가 삭제한 부분이구요 
{} 이표시는 그 줄은 새로 만들어 넣은 줄이라는겁니다. 
평안하시길~



procedure TApplication.CreateHandle;
var
TempClass: TWndClass;
SysMenu: HMenu;
begin
if not FHandleCreated then
begin
FObjectInstance := MakeObjectInstance(WndProc);
if not GetClassInfo(HInstance, WindowClass.lpszClassName, TempClass) then
begin
WindowClass.hInstance := HInstance;
if Windows.RegisterClass(WindowClass) = 0 then
raise EOutOfResources.CreateRes(SWindowClass);
end;
FHandle := CreateWindow(WindowClass.lpszClassName, PChar(FTitle),
WS_POPUP or WS_CAPTION or WS_VISIBLE or WS_CLIPSIBLINGS or
{} WS_SYSMENU or WS_MINIMIZEBOX or WS_CHILD , 
{WS_CHILD를 추가하여 TApplication이 화면에 보이게..}
GetSystemMetrics(SM_CXSCREEN) div 2,
GetSystemMetrics(SM_CYSCREEN) div 2,
0, 0, 0, 0, HInstance, nil);
FTitle := '';
FHandleCreated := True;
ShowWinNoAnimate(FHandle, SW_RESTORE);
SetWindowLong(FHandle, GWL_WNDPROC, Longint(FObjectInstance));
if NewStyleControls then
SendMessage(FHandle, WM_SETICON, 1, GetIconHandle);
SysMenu := GetSystemMenu(FHandle, False);
DeleteMenu(SysMenu, SC_MAXIMIZE, MF_BYCOMMAND);
DeleteMenu(SysMenu, SC_SIZE, MF_BYCOMMAND);
if NewStyleControls then DeleteMenu(SysMenu, SC_MOVE, MF_BYCOMMAND);
end;
end;


procedure TApplication.Minimize;
begin
if not IsIconic(FHandle) then
begin
NormalizeTopMosts;
SetActiveWindow(FHandle);
/// ShowWinNoAnimate(FHandle,SW_MINIMIZE);{애니메션을 못하게 하는것을 삭제}
{} if MainForm <> nil then {TApplication의 위치,크기를 메인폼과 같게}
{} with MainForm do SetWindowPos(FHandle,0,Left,Top,Width,Height,0);
ShowWindow(FHandle, SW_MINIMIZE);
if Assigned(FOnMinimize) then FOnMinimize(Self);
end;
end;

procedure TApplication.Restore;
begin
if IsIconic(FHandle) then
begin
SetActiveWindow(FHandle);
/// ShowWinNoAnimate(FHandle, SW_RESTORE);{애니메션을 못하게 하는것을 삭제}
ShowWindow(FHandle,SW_RESTORE);
{} SetWindowPos(FHandle, 0,0,0,0,0,0);{최소화된후 TApplication을 안보이게}
RestoreTopMosts;
if Screen.ActiveControl <> nil then
Windows.SetFocus(Screen.ActiveControl.Handle);
if Assigned(FOnRestore) then FOnRestore(Self);
end;
end;