볼랜드포럼에 올린글을 블로그에 재게시 합니다.
http://cbuilder.borlandforum.com/impboard/impboard.dll?action=read&db=bcb_tutorial&no=135
http://cbuilder.borlandforum.com/impboard/impboard.dll?action=read&db=bcb_tutorial&no=135
Object 형 property 라는 것은 말 그대로 property 가 object 형태로 된 것입니다.
여러가지의 자료형이 혼합되어 있고, 각각의 자료형에 맞는 처리를 해주어야 할 때 , 그리고
이것들이 유사한 것들이어서 통합해서 관리하고 싶을때 object 형으로 property 를 만듭니다.
object 형 property 를 사용하는 쪽에서는 생성자에서 new 로 생성하고 파괴자에서 delete 로
파괴해야 합니다.
TFont3D* FFont3D; //Font3D property 포인터
__fastcall TApLabel::TApLabel(TComponent* Owner)
: TCustomLabel(Owner)
{
FFont3D = new TFont3D(); //TFont3D 의 object 생성 (object 형 property 이므로)
}
__fastcall TApLabel::~TApLabel(void)
{
delete FFont3D; //FFont3D 파괴
}
__fastcall TApLabel::TApLabel(TComponent* Owner)
: TCustomLabel(Owner)
{
FFont3D = new TFont3D(); //TFont3D 의 object 생성 (object 형 property 이므로)
}
__fastcall TApLabel::~TApLabel(void)
{
delete FFont3D; //FFont3D 파괴
}
전체적인 모습을 살펴봅시다.
class TFont3D : public TPersistent //Font3D property를 위한 class
{
private:
TTextStyle FTextStyle;
TShadowDirection FShadowDirection;
TColor FShadowColor;
int FShadowDepth;
TNotifyEvent FOnChange;
void __fastcall DoOnChange(void);
void __fastcall SetShadowDepth(const int Value);
void __fastcall SetShadowDirection(const TShadowDirection Value);
void __fastcall SetTextStyle(const TTextStyle Value);
void __fastcall SetShadowColor(const TColor Value);
public:
__property TNotifyEvent OnChange = {read = FOnChange, write = FOnChange};
void __fastcall Assign(TPersistent* Source);
__published:
__property TColor ShadowColor = {read = FShadowColor, write = SetShadowColor, default = DefShadowColor};
__property TShadowDirection ShadowDirection = {read = FShadowDirection, write = SetShadowDirection, default = DefShadowDirection};
__property int ShadowDepth = {read = FShadowDepth, write = SetShadowDepth, default = DefShadowDepth};
__property TTextStyle TextStyle = {read = FTextStyle, write = SetTextStyle, default = DefTextStyle};
};
{
private:
TTextStyle FTextStyle;
TShadowDirection FShadowDirection;
TColor FShadowColor;
int FShadowDepth;
TNotifyEvent FOnChange;
void __fastcall DoOnChange(void);
void __fastcall SetShadowDepth(const int Value);
void __fastcall SetShadowDirection(const TShadowDirection Value);
void __fastcall SetTextStyle(const TTextStyle Value);
void __fastcall SetShadowColor(const TColor Value);
public:
__property TNotifyEvent OnChange = {read = FOnChange, write = FOnChange};
void __fastcall Assign(TPersistent* Source);
__published:
__property TColor ShadowColor = {read = FShadowColor, write = SetShadowColor, default = DefShadowColor};
__property TShadowDirection ShadowDirection = {read = FShadowDirection, write = SetShadowDirection, default = DefShadowDirection};
__property int ShadowDepth = {read = FShadowDepth, write = SetShadowDepth, default = DefShadowDepth};
__property TTextStyle TextStyle = {read = FTextStyle, write = SetTextStyle, default = DefTextStyle};
};
TPersistent 로부터 상속받았다는 것을 알 수 있습니다. TPersistent 는 Stream 이 가능한 class 들의 제일 위 조상입니다.
Stream 이 가능하다는 것은 속성의 변화된 값을 파일에 저장할 수 있다는 뜻입니다.
즉, dfm 파일에 저장할 수 있다는 뜻입니다. property 들은 최소한 TPersistent 로부터 상속받아야 합니다.
물론, TPersistent 로부터 상속받은 class 로부터 상속받아도 됩니다. VCL class 계층도를 보면 TPersistent는
TObject 로부터 상속받는다는 것을 알 수 있습니다. 그리고 모든 컴포넌트의 조상인 TComponent 는 TPersistent 의 자손입니다.
TObject
|
TPersistent
|
TComponent
|
TPersistent
|
TComponent
여기서 좀 묘한 문제가 발생합니다. Object 형 property 인 TFont3D는 ShadowColor, TextStyle 등을 변화시킬 수 있습니다.
그런데 TFont3D 는 TPersistent 로부터 상속받은 Object 형 property 이지 Component 가 아닙니다. 따라서 TFont3D의
property 들인 TextStyle , ShadowColor 등을 변화시킨다고 해서 Component 를 직접 변화시킬 수가 없습니다.
변화를 시킨다는 것은 화면을 다시 그려준다는 이야기인데 TPersistent 로부터 직접 상속받은 class 는 그런 기능이 없습니다.
따라서 TFont3D 를 Object 로 생성해서 사용하는 다른 Component 에서 대신 해줄 필요가 있습니다. 화면을 다시 그려주는
Invalidate() 함수를 호출하는 함수를 TFont3D의 property 와 연결시켜주는 이벤트 디스패치 함수가 있어야 됩니다. 그 역할을
하는 것이 DoOnChange() 함수입니다.
void __fastcall TFont3D::DoOnChange(void)
{
if(FOnChange) FOnChange(this); //TFont3D 와 이벤트 핸들러 연결
}
{
if(FOnChange) FOnChange(this); //TFont3D 와 이벤트 핸들러 연결
}
FOnChange 변수는 TNotifyEvent 형입니다. 저번의 메시지 핸들러 부분에서도 보았듯이 TNotifyEvent 형은
이쪽 class 의 함수와 저쪽 class 의 함수를 연결시켜줍니다. 실제로는 다른 class 의 함수가 하는 일을 마치
TFont3D class 내부의 함수가 하는 것처럼 만드는 것입니다.
(1) 값이 변화되었으면 DoOnChange() 호출
void __fastcall TFont3D::SetTextStyle(const TTextStyle Value)
{
if(FTextStyle != Value)
{
FTextStyle = Value;
DoOnChange();
}
}
(2) TFont3D를 object 로 생성하여 사용하는 component 의 이벤트 핸들러와 연결
void __fastcall TFont3D::DoOnChange(void)
{
if(FOnChange) FOnChange(this); //TFont3D 와 이벤트 핸들러 연결
}
(3) TApLabel 생성자에서 연결
FFont3D->OnChange = Changed;
(4) TApLabel 의 이벤트 핸들러 Changed 에서 넘겨받아서 처리
void __fastcall TApLabel::Changed(TObject *Sender) //이벤트 핸들러
{
Invalidate(); //컨트롤을 새로 그린다.
}
void __fastcall TFont3D::SetTextStyle(const TTextStyle Value)
{
if(FTextStyle != Value)
{
FTextStyle = Value;
DoOnChange();
}
}
(2) TFont3D를 object 로 생성하여 사용하는 component 의 이벤트 핸들러와 연결
void __fastcall TFont3D::DoOnChange(void)
{
if(FOnChange) FOnChange(this); //TFont3D 와 이벤트 핸들러 연결
}
(3) TApLabel 생성자에서 연결
FFont3D->OnChange = Changed;
(4) TApLabel 의 이벤트 핸들러 Changed 에서 넘겨받아서 처리
void __fastcall TApLabel::Changed(TObject *Sender) //이벤트 핸들러
{
Invalidate(); //컨트롤을 새로 그린다.
}
(비교)
void __fastcall TApLabel::SetEdges(const TEdges Value)
{
if(FEdges != Value) //변경하려는 값이 기존의 값과 다르면
{
FEdges = Value; //새로운 값으로 변경
Invalidate(); //컨트롤을 새로 그린다.
}
}
void __fastcall TApLabel::SetEdges(const TEdges Value)
{
if(FEdges != Value) //변경하려는 값이 기존의 값과 다르면
{
FEdges = Value; //새로운 값으로 변경
Invalidate(); //컨트롤을 새로 그린다.
}
}
(1), (2), (3), (4) 로 표시된 부분과 (비교) 부분을 비교해 보세요 TFont3D 에서는 값이 변화되었을때의
처리만 해줄 뿐, 화면처리등의 구체적인 내용은 TApLabel 에서 하게 됩니다.
또 한가지 특이한 것은 Assign 함수입니다.
TFont3D* FFont3D; //Font3D property 포인터
위와 같이 포인터를 만들고
__property TFont3D* Font3D = {read = FFont3D, write = SetFont3D};
void __fastcall TApLabel::SetFont3D(TFont3D* Value)
{
FFont3D->Assign(Value); //Object 형 property 의 값 설정
}
void __fastcall TApLabel::SetFont3D(TFont3D* Value)
{
FFont3D->Assign(Value); //Object 형 property 의 값 설정
}
위와 같이 값을 넣을 때 Assign 함수를 사용해야 합니다. 만약 다음과 같이 한다면
void __fastcall TApLabel::SetFont3D(TFont3D* Value)
{
FFont3D = Value;
}
{
FFont3D = Value;
}
에러가 나게 됩니다. FFont3D 는 포인터일 뿐이므로 Value 를 넣는다는 것은 주소만 바꾼다는
의미입니다. 원래의 객체는 해제되지 않은채로 잃어버리게 됩니다. 따라서 Assign 함수를 통해서
멤버들의 값을 일일히 대입해야 합니다.
void __fastcall TFont3D::Assign(TPersistent* Source)
{
TFont3D* LS = dynamic_cast<TFont3D*>(Source);
if(Source && dynamic_cast<TFont3D*>(Source)) //Source 가 NULL 이 아니고 안전하게 타입캐스팅 되었다면
{
ShadowDirection = LS->ShadowDirection; //속성을 복사한다.
ShadowDepth = LS->ShadowDepth;
ShadowColor = LS->ShadowColor;
TextStyle = LS->TextStyle;
}
}
{
TFont3D* LS = dynamic_cast<TFont3D*>
if(Source && dynamic_cast<TFont3D*>
{
ShadowDirection = LS->ShadowDirection; //속성을 복사한다.
ShadowDepth = LS->ShadowDepth;
ShadowColor = LS->ShadowColor;
TextStyle = LS->TextStyle;
}
}
'기술탐구' 카테고리의 다른 글
[C++Builder] File not found: 'Designintf.dcu' 에러 대처법 (0) | 2008.07.26 |
---|---|
[C++Builder] TApLabel 만들기 (3) Run Time 과 Design Time 의 분리 (0) | 2008.07.23 |
[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 |