볼랜드포럼에 올렸던 글을 블로그에 재게시 합니다.
http://cbuilder.borlandforum.com/impboard/impboard.dll?action=read&db=bcb_tutorial&no=127
http://cbuilder.borlandforum.com/impboard/impboard.dll?action=read&db=bcb_tutorial&no=127
여러개의 컴포넌트를 조합하여 새로운 컴포넌트를 만들고 싶을 때가 있습니다. 델파이는 다중상속을 지원하지 않으므로 다른 컴포넌트를 포함하는 것으로 다른 컴포넌트의 기능을 이용할 수 있습니다. 델파이 예제는 많지만 C++ Builder 예제는 찾아보기 어렵더군요. C++Builder 로 제작된 컴포넌트가 거의 없다보니 컴포넌트 만들기가 막막합니다. 그래서 간단한 예제로 Label 에 Lines property 를 추가하여 멀티라인이 입력되는 Label 컴포넌트를 만들어 보겠습니다.
Borland C++Builder 6.0 을 사용합니다.
그럼 시작합니다.
일단, 컴포넌트 제작을 위한 템플릿이 필요합니다. 컴포넌트를 등록해주는 함수가 있고 기본적인 틀이 나와있는 것입니다. 그 다음에는 만들어진 틀 내부를 채워 나가면 되는 것이죠.
File -> New -> Other... -> Component 를 선택합니다.
New Component 에서 Ancestor type: 을 TLabel 을 선택합니다.
Pallette Page: 는 컴포넌트가 설치될 팔레트입니다. Samples 팔레트에 설치하는 것으로 합니다. 나중에 소스를 수정하면 원하는 팔레트를 만들어서 설치할 수 있습니다.
Unit file name: 은 저장될 디렉토리와 이름을 지정하는 것입니다.
Search path: 는 필요한 컴포넌트가 있는 경로입니다.
컴포넌트를 컴파일하면 bpl 은 Projects\bpl 에 저장되고 lib 는 Projects\lib 에 저장됩니다.
OK 버튼을 누릅니다.
이제 MyLabel.cpp 와 MyLabel.h 가 생겼습니다.
MyLabel.cpp 편집화면이 나옵니다. 헤더파일을 편집하기 위해서 에디터 아래의 MyLabel.h 탭을 선택합니다.
class PACKAGE TMyLabel : public TLabel
{
private:
protected:
public:
__fastcall TMyLabel(TComponent* Owner);
__publshed:
};
{
private:
protected:
public:
__fastcall TMyLabel(TComponent* Owner);
__publshed:
};
현재 나와있는 것이라고는 객체 생성시 초기화를 담당하는 생성자만 있으므로 나머지를 채워넣어야 합니다.
필요한 것은 4개 입니다.
(1) 객체가 삭제될 때 , 생성자에서 만든 객체를 파괴할 파괴자. ( public )
(2) 멀티라인 입력속성을 결정하는 Lines property ( __published )
(3) Lines property 의 read 방법 ( private )
(4) Lines property 의 write 방법 ( private )
(1)파괴자는 외부에서 호출해야 하므로 public 영역에 넣습니다.
(2)TMyLabel 에 추가할 Lines 속성은 Object Inspector 에 나타나야 하므로 __published: 섹션에 넣습니다.
(3), (4) Lines 의 read , write 는 외부에서 직접 접근하는 것을 막아야 하므로 private 에 넣습니다.
class PACKAGE TMyLabel : public TLabel
{
private:
TStringList* FLines; //입력할 내용을 담은 객체의 포인터
void __fastcall SetLines(TStringList* Lines); //Lines 의 write 방법
protected:
public:
__fastcall TMyLabel(TComponent* Owner); //생성자
virtual __fastcall ~TMyLabel(); //파괴자
__published:
__property TStringList* Lines = { read = FLines, write = SetLines }; //접근 방법을 지정하는 property
};
{
private:
TStringList* FLines; //입력할 내용을 담은 객체의 포인터
void __fastcall SetLines(TStringList* Lines); //Lines 의 write 방법
protected:
public:
__fastcall TMyLabel(TComponent* Owner); //생성자
virtual __fastcall ~TMyLabel(); //파괴자
__published:
__property TStringList* Lines = { read = FLines, write = SetLines }; //접근 방법을 지정하는 property
};
-여기서 잠깐!-
VCL 컴포넌트는 고유의 명명법이 있습니다. TForm 에서 T는 Type 을 나타내는 것입니다. class 도 Type 이라는 말이
있지요. MFC 에서는 Class 라는 뜻으로 C 를 접두어로 사용하지만 VCL 은 T를 사용합니다. 마찬가지로 class 내부
변수는 Field 를 뜻하는 F를 접두어로 사용합니다. property read 를 위한 함수는 Get 으로 시작합니다. write 를 위한
함수는 Set 으로 시작합니다. 즉, 기본구조는 다음과 같습니다.
VCL 컴포넌트는 고유의 명명법이 있습니다. TForm 에서 T는 Type 을 나타내는 것입니다. class 도 Type 이라는 말이
있지요. MFC 에서는 Class 라는 뜻으로 C 를 접두어로 사용하지만 VCL 은 T를 사용합니다. 마찬가지로 class 내부
변수는 Field 를 뜻하는 F를 접두어로 사용합니다. property read 를 위한 함수는 Get 으로 시작합니다. write 를 위한
함수는 Set 으로 시작합니다. 즉, 기본구조는 다음과 같습니다.
class PACKAGE TDerived : public TBase
{
private:
int FValue; //값을 저장할 변수
int __fastcall GetValue(); //read 방법
void __fastcall SetValue(int AValue); //write 방법
protected:
public:
__fastcall TDerived(TComponent* Owner);
virtual __fastcall ~TDerived();
__published:
__property int Value = { read = GetValue, write = SetValue };
};
{
private:
int FValue; //값을 저장할 변수
int __fastcall GetValue(); //read 방법
void __fastcall SetValue(int AValue); //write 방법
protected:
public:
__fastcall TDerived(TComponent* Owner);
virtual __fastcall ~TDerived();
__published:
__property int Value = { read = GetValue, write = SetValue };
};
기본적인 아이디어는 TMyLabel 의 객체가 생성될 때 멀티라인의 데이터를 저장할 FLines 객체를 생성하고 TMyLabel 객체가 파괴 될때 FLines 도 같이 파괴하는 것입니다. 객체가 객체를 포함하는 것입니다.
TStringList 등의 T로 시작하는 객체는 new 연산자를 통해서 heap 에서만 생성할 수 있습니다.
생성자에서 new 를 통해서 생성하고 , 파괴자에서 delete 로 파괴한다는 밑그림이 그려집니다.
어떤 객체들은 고유의 property editor 가 있습니다. TPicture 는 그림을 불러오는 Picture Editor 가 있습니다.
이제 구체적인 구현으로 들어갑니다. MyLabel.cpp 를 선택하여 입력합니다.
__fastcall TMyLabel::TMyLabel(TComponent* Owner)
: TLabel(Owner)
{
FLines = new TStringList(); //객체를 생성
}
__fastcall TMyLabel::~TMyLabel()
{
delete FLines; //만들었던 객체를 파괴
}
void __fastcall TMyLabel::SetLines(TStringList* Lines)
{
FLines->Assign(Lines); //String List Editor 에서 입력받은 내용을 FLines 에 저장
this->Caption = FLines->Text; //TMyLabel 의 Caption 에 FLines 에 저장된 내용을 표시
}
: TLabel(Owner)
{
FLines = new TStringList(); //객체를 생성
}
__fastcall TMyLabel::~TMyLabel()
{
delete FLines; //만들었던 객체를 파괴
}
void __fastcall TMyLabel::SetLines(TStringList* Lines)
{
FLines->Assign(Lines); //String List Editor 에서 입력받은 내용을 FLines 에 저장
this->Caption = FLines->Text; //TMyLabel 의 Caption 에 FLines 에 저장된 내용을 표시
}
자, 이제 컴포넌트를 컴포넌트 팔레트에 등록시키기 위해서 패키지 파일을 만들어야 합니다.
패키지 파일에는 컴파일에 필요한 설정이 들어가 있습니다.
-잠깐!-
작업을 새로 시작하기 전에 Close All 을 선택하여 모두 닫는 것이 좋습니다. 다른 폼이 열려있는 상태에서 엉뚱하게
저장되었다거나 저장한 줄 알았더니 저장이 안되는 경우가 생깁니다. 확실하게 하기 위해서 새로 시작하기 전에
Close All 을 해줍니다.
작업을 새로 시작하기 전에 Close All 을 선택하여 모두 닫는 것이 좋습니다. 다른 폼이 열려있는 상태에서 엉뚱하게
저장되었다거나 저장한 줄 알았더니 저장이 안되는 경우가 생깁니다. 확실하게 하기 위해서 새로 시작하기 전에
Close All 을 해줍니다.
File -> New -> Other... -> Package 를 선택합니다.
파일이름을 MyLabel_bcb6.bpk 로 저장합니다. 그러면 자동으로 res , cpp 파일이 추가됩니다.
res 파일은 리소스 파일로 아이콘이나 여러가지 이미지를 넣을 수 있습니다.
cpp파일은 엔트리입니다. 일종의 main 함수입니다.
-또 잠깐!-
MyLabel.bpk 로 하면 절대 안됩니다! bpk 파일을 만들면 cpp , res 가 같이 생깁니다. MyLabel.bpk 로 저장하면
자동으로 MyLabel.cpp 를 만들기 때문에 앞에서 만들었던 파일을 덮어쓰기 합니다. 때문에 컴포넌트의
엔트리라는 것을 명확히 하기 위해서 식별하기 쉬운 이름을 정해줍니다. C++Builder 6 용이면 _bcb6 정도의 접미사
정도가 적당할 것 같습니다.
MyLabel.bpk 로 하면 절대 안됩니다! bpk 파일을 만들면 cpp , res 가 같이 생깁니다. MyLabel.bpk 로 저장하면
자동으로 MyLabel.cpp 를 만들기 때문에 앞에서 만들었던 파일을 덮어쓰기 합니다. 때문에 컴포넌트의
엔트리라는 것을 명확히 하기 위해서 식별하기 쉬운 이름을 정해줍니다. C++Builder 6 용이면 _bcb6 정도의 접미사
정도가 적당할 것 같습니다.
이제 , Add 버튼을 눌러서 앞에서 만들었던 MyLabel.cpp 를 추가해야 합니다. 그럼 총 3개의 파일이 포함되는 것을
알 수 있습니다.
MyLabel.cpp
MyLabel_bcb6.cpp
MyLabel_bcb6.res
Include Path 에 MyLabel.h 가 있는 곳을 추가합니다.
저장한후 Install 합니다.
아래와 같은 화면이 나와야 합니다. Registered 되었다는 메시지가 나와야 컴포넌트 팔레트에 나타납니다.
Samples 탭에 TMyLabel 컴포넌트가 생겼습니다. 폼에 drop 한후 Object Inspector 의 properties 를 보면 TLabel 에는 없던 Lines 속성이 생긴것을 알 수 있습니다. 클릭하면 String List Editor 가 나타납니다. 여러줄을 입력해 봅니다.
다음과 같이 멀티라인이 입력되는 Label 이 생겼습니다.
'기술탐구' 카테고리의 다른 글
[C++Builder] TApLabel 만들기 (1) 준비 (2) | 2008.07.23 |
---|---|
[C++Builder] Message Handler를 이용하여 Event 추가하기 (0) | 2008.07.23 |
[C++Builder] About Property 를 추가한 TMyComponent (0) | 2008.07.23 |
[C++Builder] IDE 확장 - 나만의 Component Editor 를 추가해보자 (0) | 2008.07.23 |
[C++Builder] Component Editor 가 추가된 TMyLabel (0) | 2008.07.23 |