C++Builder Programming Forum
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
C++빌더 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
컴포넌트/라이브러리
메신저 프로젝트
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
볼랜드포럼 광고 모집

C++빌더 강좌/문서
C++Builder Programming Tutorial&Docments
[218] 같은 코드로 이중 폼 사용 기법.(폼 대치 기법)
김태선 [cppbuilder] 25267 읽음    2010-10-01 15:50
흔히 화면과 로직은 분리하라고 합니다.
VC에서는 주로 View와 Document를 분리하라는 말을 흔히 쓰고
또는 다른쪽에서는 Form과 엔진을 분리하라는 말을 쓰기도 합니다.

어쨌거나 화면에 보이는 비주얼한 부분과
실제 비지니스 로직이 구현되는 쪽 코드를 분리하는 것이 좋다는 것입니다.
이는 원칙이 그렇다는 것이지 코딩 상황에 따라 선택은 프로그래머가 하는 것입니다.

현실의 빌더나 델파이에서는 워낙 폼관련 서비스가 뛰어나기 때문에 분리 안하거나
가벼운 분리 정도만 하는 경우가 많고,
저 경우는 비UI 부분은 그런대로 명확히 분리하는 것을 선호합니다.
빌더나 델파이가 워낙 화면 디자인의 편의성에 초점을 맞춰 나온 개발툴이라
웬만한 프로그램 크기가 되어도 폼에 관련 코드를 웬만큼 채워 넣어도 문제가 없습니다.

가벼운 분리라는 말씀을 드렸는데, 가벼운 코딩은 귀잖은 관계로 폼에 그냥 하고
클래스로 빼야 할 것이나 무거운 코딩은 따로 순수 유닛을 구성해서 한다는 뜻으로 표현한 말입니다.

원칙에 맞춰 완전히 화면쪽은 코딩을 극히 안하고 모두 따로 비지니스 로직 유닛을 빼서 구성하는 것은
좀 성가신 면이 있고 그렇게 한다고 해서 프로그램이 좋아지는 것도 유지 보수가 유리해지는 것도 아니기 때문입니다.
너무 폼에 몰아 넣지만 않으면 된다는 것이죠.



그런데 프로그램을 만들다 보면 코드는 똑 같은데 화면 모습만 달라, 이중 폼이 필요한 경우가 있습니다.

어떤 프로그램을 만들었는데 화면에 컨트롤이 수백 수천개 되는 아주 복잡한 화면이라고 합시다.
이미 구현은 다 되어 있습니다.
그런데 이를 사이즈가 다른 모니터에서 모든 컨트롤이 이상없이 나와야 한다는 요청이 들어왔다면 어떻게 해야 할까요?
가령 1920x1080화면에 다 맞춰진 폼을, 1280x1024화면 버전을 릴리즈 해야 하는 생긴다면.

이 경우 그 모니터에 맞게 컨트롤을 모두 수동이던지 자동이던지 조절하면 되겠죠.
수동 조절은 빌더의 폼 디자이너에서 조절하는 것이고, 자동 조절은 코딩으로 자동으로 줄어들게 만드는 것인데
사실상 화면에 올려진 컨트롤이 수백 수천개를 넘어가면 이건 거의 힘든 작업이 됩니다.
할수 있는 것은 그 화면에 맞게 컨트롤을 재배치하는 작업 뿐입니다.

그런데 문제는 큰 모니터용도 유지보수해야 하고 작은 모니터용도 유지보수를 해야 한다면
양쪽을 다 관리해야 합니다. 그런데 기능의 변경이 생기면 양쪽 코드를 다 고치려면 아주 신경쓰이는 일이 됩니다.
양쪽을 위한 프로젝트도 따로 관리해야 하고, 양쪽 소스도 동기화 해야 때문입니다.
이렇게 되면 유지보수에 예기치 못한 실수에 따른 부담을 개발자가 지게 됩니다.

이럴때 이 이중 폼을 가장 힘 안들이고 유지관리하는 방법은 뭘까요?
폼의 유닛 부분은 한가지 소스로만 하고, 폼만 양쪽 중에 하나를 써서 빌드하도록 하려면?
특히 쓰지 않는 폼은 프로젝트에 포함시키지 않고 빌드하는 방법은?




아래는 Unit1 소스의 구현부 Unit1.cpp 중에 본래 Unit1.dfm을 사용하도록 된 부분인데,

#pragma resource "*.dfm"

를 아래와 같은 식으로 고쳤습니다.

#if __USE_UNIT1
    #pragma resource "Unit1.dfm"
#else
    #pragma resource "Unit2.dfm"
#endif

이는 무조건 폼파일 *.dfm 즉 Unit1.dfm을 사용하게 되어있는 것을,
조건에 따라 Unit1.dfm 또는 Unit2.dfm 을 링크할때 사용하라는 의미입니다.
디자인시에는 Unit1.cpp 에 딸린 Unit1.dfm을 무조건 사용하지요.

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
//#pragma resource "*.dfm"
#if 0
    #pragma resource "*.dfm"
#else
    #pragma resource "Unit2.dfm"
#endif
TForm2 *Form2;
//---------------------------------------------------------------------------
__fastcall TForm2::TForm2(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm2::Button1Click(TObject *Sender)
{
    Memo1->Lines->Add("하하 스머프");
}
//---------------------------------------------------------------------------



//---------------------------------------------------------------------------

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
//---------------------------------------------------------------------------
class TForm2 : public TForm
{
__published:    // IDE-managed Components
    TButton *Button1;
    TMemo *Memo1;
    void __fastcall Button1Click(TObject *Sender);
private:    // User declarations
public:        // User declarations
    __fastcall TForm2(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm2 *Form2;
//---------------------------------------------------------------------------
#endif


다만 폼이름은 TForm2로 해놨습니다.

이렇게 하면 링크시 원래의 폼파일 dfm을 링크되게 할수도 있고
다른 사이즈로 다르게 배치된 폼파일 dfm을 링크되게도 할수 있습니다.

그러면 TForm2의 동일한 폼 클래스 명칭을 가진 다른 폼 유닛 소스가 있어야 겠지요.
이는 기존 Unit1.cpp Unit1.h Unit1.dfm을 복사해
Unit2.cpp Unit2.h Unit2.dfm로 만들어 주면 됩니다.
Unit2.cpp에 실지 코드가 있던 없던 관계는 없습니다.

Unit2의 폼내의 컨트롤 조정 작업은 다른 더미 프로젝트를 하나 생성해서
여기에 대치할 폼파일 소스를 Add 시킨후 열어서 조절해주면 될 것입니다.

단, 폼에 컨트롤이 추가 또는 삭제 되었을때 또는 이벤트 핸들러 명칭이 변했을때는
unit1.dfm에 맞춰서 unit2.dfm도 변경을 시켜 주면 됩니다.

이를 더미 프로젝트를 같이 열어 놓고 작업하면 쉬울 것입니다.

이렇게 하면 코딩은 unit1.cpp 에만 하면 되고, unit2.cpp 쪽은 코딩을 하지 않고 더미로 핸들러만 두어도 됩니다.
어차피 링크될때 필요한 것은 unit2.dfm 뿐이기 때문입니다.



첨부 파일에는 실험한 소스가 들어 있습니다.
빌더6로 된 것입니다.


올리고 다시 읽어보면서 보충하려고 했는데, 사이트가 다운되는 바람에
한참 있다가 다시 들어와 보니 생각의 리듬이 깨졌네요.

아무튼, 현재의 폼이 아니라 다른 폼을 현재의 폼에 대치하는 방법은

- 같은 폼 파일을 복사 또는 생성한다.
- 당연히 폼 클래스 명이 같아야 한다. 파일명은 물론 다를 것이다.
- 화면에 컨트롤을 각각의 목적에 맞게 배치한다.
- 컨트롤의 이름과 프로퍼티와 이벤트 핸들러가 원래의 폼과 같아야 한다. 또는 목적에 따라 약간 바꾸어도 무방하나, 양 폼 파일의 내용을 같이 맞추고, 그 외의 바뀐 내용은 코딩으로 처리하는게 유지 보수에 유리하다.
- 폼의 구현부 소스쪽에 조건 컴파일로 대치할 폼 파일을 지정한다.
    #pragma resource "대치할폼.dfm"

이 정도로 정리될 수 있겠네요.

그리고, 나중에 다시 프로젝트를 열면 이중폼을 쓰는 폼 소스를 열면
디자인 화면이 안나오는 경우는 이는 #pragma resource "*.dfm" 이 문장이 없기 때문입니다.
이는 아래처럼 #pragma resource "*.dfm" 가 default 로 지정될수 있게 조건문을 수정한 뒤
다시 폼을 열면 됩니다.

#if 1
    #pragma resource "*.dfm"  // 이건 원래의 폼
#else
    #pragma resource "Unit2.dfm" // 대치할 폼.
#endif


#if 1로 원래의 폼으로 실행했을 경우


#if 0로 대치 폼으로 실행했을 경우


이렇게 화면은 바뀌지만, 실행해보면 같은 코드를 실행합니다.


그럼.
Mins [heroms01]   2010-12-06 18:46 X
우와! 정말 유용한 기술이네요. 좋은 정보 감사합니다.
Gaeseong Park [rotjdsla]   2017-05-30 10:52 X
유용하기도 하고 필요한 기술이라 검색을 해서 잘 봤습니다. 위의 방법이 컴파일 타임에 폼을 결정하는 것 같은데 혹시 런타임에 위와 같은 식으로 같은 코드의 다른 폼을 사용할 수 있을까요?

+ -

관련 글 리스트
218 같은 코드로 이중 폼 사용 기법.(폼 대치 기법) 김태선 25267 2010/10/01
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.