저작권에 대한 공지
이 문서에 포함된 모든 글과 소스 코드는 작자인 저 김성동의 지적 재산이지만 무단으로 배포하거나 책이나 강의 교재등을 위한 복제 등 저작권을 저해하는 행위를 제외하고는 자유롭게 사용할 수 있습니다.
또한 이 페이지에 대한 링크는 자유롭게 할 수 있으나 전문을 전제하는 일은 허용하지 않습니다.


1.3. 프로퍼티

1.3.1. 개요
어플리케이션 개발자의 입장에서 독자들은 프로퍼티라는 것이 무엇인지 알고 있을 것이다. 델파이에서 폼에 콤포넌트를 하나 올려 놓고 오브젝트 인스펙터를 보면 콤포넌트의 색상, 글꼴, 기능 등을 설정할 수 있도록 되어 있다. 어플리케이션 개발자들이 폼 디자이너와 오브젝트 인스펙터를 이용해서 콤포넌트의 속성들을 설정하면 그 피드백을 바로 바로 확인해가면서 어플리케이션을 설계할 수 있다. 델파이에서 제공하는 TeeChart 콤포넌트 등을 보면 속성이 너무 많아서 머리가 아플 정도다. 그렇다면 콤포넌트 개발자에게 프로퍼티는 어떤 의미가 있을까?
프로퍼티는 데이터 필드와 사용자 사이를 연결해 주는 접근 통로라고 할 수 있다. 사용자가 프로퍼티를 조작한다는 것은 실제로 데이터 필드의 값을 변경하거나 참조하는 경우가 대부분이다. 다만 그것을 직접 조작하지 않고 프로퍼티라는 통로를 통해서 한 단계 거쳐서 조작할 뿐이다. 프로퍼티는 마치 클래스의 데이터 필드처럼 사용하지만 데이터 필드와 달리 실행 시는 물론이고 설계 시에도 사용할 수 있기 때문에 어플리케이션을 실행하지 않고서도 콤포넌트의 여러 가지 기능을 설정할 수 있다. 또한 데이터 필드처럼 단순히 값을 대입하는 것 뿐만 아니라 값을 읽거나 쓸 때 특정 메소드를 사용할 수 있기 때문에 이 메소드내에서 데이터 필드가 가질 수 있는 값의 범위를 검사할 수도 있고 내부 데이터를 암호화해 놓고 사용자에게는 간단하게 보여 줄 수도 있고 변경된 데이터 필드의 값에 따라 콤포넌트의 모양을 즉시 변경해 준다든지 하는 다양하고 복잡한 작업을 할 수 있다.

리스트 1.7 TVehicle 클래스
type
  TVehicle = class(TComponent)
  private
    FWidth : integer;
    FHeight : integer;
    FLength : integer;

    procedure SetWidth(const Value : integer);
    function  GetHeight : integer;
    procedure SetHeight(const Value : integer);

    function  GetVolume : integer;
  public
    constructor Create(AOwner : TComponent); override;

    property Width : integer read FWidth write SetWidth default 1700;
    property Height : integer read GetHeight write SetHeight default 4500;
    property Length : integer read FLength write FLength default 1800;
    property Volume : integer read GetVolume;
  end;

TCar = class(TVehicle)
private
    FVolume : integer;
function  GetVolume : integer;
procedure SetVolume(const Value : integer);
  published
    property Width : integer ;
    property Height : integer nodefault;
    property Length : integer;
    property Volume : integer read GetVolume write SetVolume;
  end;

...

implementation

...

constructor TVehicle.Create(AOwner : TComponent);
begin
  inherited Create(AOwner);
  FWidth := 1700;
  FHeight := 4500;
  FLength := 1800;
end;

function TVehicle.GetHeight: integer;
begin
  Result := FHeight;
end;

function TVehicle.GetVolume: integer;
begin
  Result := FWidth * FHeight * FLength;
end;

procedure TVehicle.SetHeight(const Value: integer);
begin
  if FHeight <> Value then
  begin
    FHeight := Value;
    Update;
  end;
end;

procedure TVehicle.SetWidth(const Value: integer);
begin
  if FWidth <> Value then
  begin
    if FWidth < 3000 then
    begin
      FWidth := Value;
      Update;
    end;
  end;
end;

function TCar.GetVolume: integer;
begin
  Result := inherited Volume;
Result := Result + 1;
end;

procedure TCar.SetVolume(const Value: integer);
begin
inherited Volumen := Value;
end;


프로퍼티를 선언하기 위해서는 property라는 키워드와 프로퍼티 이름, 프로퍼티의 자료 형, 그리고 프로퍼티를 읽고 쓸 때 사용할 메소드를 지정해 준다. 예제의 Volume 프로퍼티처럼 쓰기 메소드를 지정하지 않으면 프로퍼티는 읽기 전용이 된다. 마찬가지로 프로퍼티를 읽는 방법을 정의해 주지 않으면 쓰기 전용 프로퍼티가 되는데 쓰기 전용 프로퍼티는 거의 사용하지 않는다. 프로퍼티를 클래스의 Published 영역에 선언하면 오브젝트 인스펙터에 나타나며 폼 파일(*.dfm)로 저장도 된다. 읽기 전용 프로퍼티는 Published 영역에 선언되어도 오브젝트 인스펙터에 나타나지 않으며 프로퍼티를 Public 영역에 선언하면 그 프로퍼티는 실행 시에만 읽고 쓸 수 있게 된다.
예제 코드에서 Width 프로퍼티는 직접 데이터 필드 FWidth로부터 값을 읽어 오고 쓸 때는 SetWidth 메소드를 이용하도록 선언되었다. 프로퍼티의 실제 값을 저장할 데이터 필드는 보통 Priavte 영역에 선언한다. 그리고 데이터 필드의 이름은 예제 코드에서 Width 프로퍼티의 값을 저장할 변수로 FWidth를 선언한 것처럼 보통 알파벳 F를 접두어로 붙여서 정의한다. 당연한 얘기지만 일반적으로 프로퍼티의 실제 값을 저장할 데이터 필드는 Private 영역에 선언하기 때문에 파생 클래스들은 직접 이 데이터 필드를 조작할 수 없고 부모 클래스에서 상속받은 프로퍼티를 이용해야 한다. Volume 프로퍼티처럼 프로퍼티가 반드시 대응하는 데이터 필드를 가질 필요는 없다.
1.3.2. 프로퍼티 읽고 쓰기
프로퍼티를 읽고 쓰는 방법을 정의할 때 read 지시자와 write 지시자를 사용한다. 읽을 때는 read 지시자를 사용하고 쓸 때는 write 지시자를 사용한다. 프로퍼티를 읽고 쓰는 가장 간단한 방법은 직접 데이터 필드를 조작하는 것이다. 이 방법은 데이터 필드를 조작할 때 특별한 처리가 필요 없는 경우에 사용한다. 예제에서 Width 프로퍼티나 Length 프로퍼티의 경우 직접 데이터 필드를 조작하고 있다.
만약 읽기 메소드를 사용하려 한다면 GetHeight, GetVolume처럼 함수로 만들어야 하며 기본적으로 인자를 가질 수 없다. 그리고 함수의 반환 값은 프로퍼티의 데이터 형과 같아야 한다. 쓰기 메소드는 SetWidth, SetHeight처럼 프로시저로 선언해야 하며 프로퍼티의 데이터 형과 같은 형의 인자를 하나 가지도록 해야 한다. 읽기 메소드의 이름은 관습적으로 프로퍼티 이름 앞에 Get을 붙이고 쓰기 메소드는 Set을 붙인다.
1.3.3. 프로퍼티 종류
Published 영역에 선언한 프로퍼티들은 설계 시에 오브젝트 인스펙터에 나타나는데 프로퍼티의 종류에 따라 오브젝트 인스펙터가 취급하는 방법이 다르다. 개발자가 오브젝트 인스펙터에서 프로퍼티를 조작할 때 프로퍼티 에디터가 동작하게 되는데 델파이는 기본적으로 대부분의 데이터 형에 대해 프로퍼티 에디터를 가지고 있다. 많은 프로퍼티 에디터들을 네 가지 정도로 구분해 볼 수 있는데 그 종류는 아래 표 6.1과 같다

표 1-1 프로퍼티 종류
프로퍼티 종류프로퍼티 에디터
기본형숫자, 문자, 문자열등의 단순한 데이터 형을 조작할 수 있으며 오브젝트 인스펙터의 편집 창에서 직접 조작한다.
열거형문자열을 입력하는 것처럼 직접 편집 창에서 입력하거나 드롭 다운 콤보 박스에서 값을 선택할 수 있으며 편집 창에서 더블 클릭하면 순환하면서 다음 값들을 보여 준다.
집합형프로퍼티를 더블 클릭하거나 프로퍼티 이름 왼쪽에 있는 + 표시를 누르면 설정 가능한 플래그들이 아래로 나타나며 각 플래그는 Boolean형으로 설정할 수 있다.
객체형프로퍼티를 더블 클릭하거나 프로퍼티 이름 왼쪽에 있는 + 표시를 누르면 객체의 Published 프로퍼티 리스트가 나타나며 이 값들은 다른 프로퍼티 종류와 같이 설정하며 별도의 프로퍼티 에디터를 제공할 수도 있다.
배열형배열형은 기본적으로 제공하는 프로퍼티 에디터가 없고 Published로 사용하려면 반드시 콤포넌트 개발자가 별도로 프로퍼티 에디터를 만들어 등록해 주어야 한다.




그림 1-4 기본형 프로퍼티 에디터




그림 1-5 열거형 프로퍼티 에디터




그림 1-6 집합형 프로퍼티 에디터




그림 1-7 객체형 프로퍼티 에디터


비록 대부분의 데이터 형이 Publised 영역에 선언될 수 있지만 모든 데이터 형이 Published 프로퍼티가 될 수 있는 건 아니다. 대표적으로 배열형이나 레코드형은 Published 영역에 선언될 수 없다. 표 6.2는 Published 영역에 선언될 수 있는 프로퍼티 형을 나타낸다. 그러면 표6.2에 없는 프로퍼티 형을 Published 영역에 선언하려면 어떻게 해야 할까? TRect 레코드를 Published 영역에 선언하려면 어떻게 해야 할까? TRect형 프로퍼티를 Published 영역에 선언한다고 해서 컴파일러가 에러를 발생시키지는 않는다. 하지만 폼을 설계할 때 폼에 콤포넌트를 올려 놓으면 아마도 델파이가 무사하지 못할거다. 왜냐하면 오브젝트 인스펙터는 RTTI(Run Time Type Information)를 이용해서 해당 콤포넌트의 Published 영역에 선언된 프로퍼티 리스트를 알아내고 이들을 차례대로 오브젝트 인스펙터에 표시하게 되는데 순서대로 표시하다가 TRect 형 프로퍼티를 만나서 TRect 레코드형에 대한 프로퍼티 에디터를 찾아 보지만 등록된 프로퍼티 에디터가 없기 때문에 Access Violation을 발생시키게 된다. 이 문제에 대한 해결책은 콤포넌트 개발자가 별도의 프로퍼티 에디터를 만들어 주던가 아니면 레코드를 클래스로 만들어 주면 된다. 프로퍼티 에디터를 만드는 방법은 나중에 자세하게 다루기로 하겠다.

표 1-2 Published 영역에 선언될 수 있는 프로퍼티 형
프로퍼티 데이터 형
서수형Integer, Char, 열거형
실수형Single, Double, Extended, Comp, Currency
문자열String, ShortString
집합TAnchors, TBorderIcons, TDBGridOptions
클래스TSizeConstraints, TFont
메소드 포인터TNotifyEvent, TCloseEvent, TCloseQueryEvent


1.3.4. 프로퍼티 초기값
콤포넌트를 폼 위에 올려 놓고 오브젝트 인스펙터를 보면 대부분의 프로퍼티에는 이미 값이 설정되어 있는 것을 볼 수 있다. 어플리케이션 개발자는 필요한 프로퍼티에 대해서만 설정하고 나머지는 초기값을 그냥 사용하면 편리하기 때문에 콤포넌트 개발자는 가능하면 모든 프로퍼티에 대해 초기값을 설정해 주는 것이 좋다. 프로퍼티의 초기값을 설정해 주기 위해서는 위의 예제 코드처럼 프로퍼티를 선언할 때 write 절과 세미콜론 사이에 default 지시자와 초기값을 써 주면 된다. 프로퍼티의 초기값은 또한 프로퍼티를 폼 파일에 저장할것인지를 결정하는 기본적인 역할을 한다. 폼 디자이너는 프로퍼티의 현재 설정 값과 초기값이 같으면 폼 파일에 프로퍼티를 저장하지 않는다. 그렇지 않고 초기값을 설정해 주지 않았거나 값이 다르면 무조건 저장한다.

프로퍼티를 선언할 때 초기값을 지정해 준다고 해서 대응하는 데이터 필드의 값이 그 초기값으로 설정되는 건 아니다. 단지 오브젝트 인스펙터에서의 초기값을 지정하는 것 뿐이기 때문에 TVehicle 예제 처럼 반드시 클래스의 생성자에서 데이터 필드를 초기값으로 설정해 주어야 한다.


1.3.5. 프로퍼티 상속(Property Inheritance)
부모 클래스에서 Public 영역이나 Protected 영역에 선언한 프로퍼티를 자손 클래스에서 오브젝트 인스펙터에 나타나도록 하려면 자손 클래스의 Published 영역에 프로퍼티 이름과 데이터 형을 다시 선언해 주면 된다. 예제 코드에서 TCar 클래스는 TVehicle 클래스의 Public 프로퍼티인 Width, Height, Length를 오브젝트 인스펙터에 나타날 수 있도록 했다. 그러나 TCar 클래스의 Volume 프로퍼티는 TVehicle 클래스의 Volume 프로퍼티를 무시하고 새로운 프로퍼티를 정의(Redefining)하는 것임을 주의해야 한다. 이런 경우에 부모 클래스의 프로퍼티는 inherited 지시자를 이용해서 사용할 수 있다. 부모 클래스에서 선언한 초기값을 파생 클래스에서 무시하고 싶을 경우에는 Height 프로퍼티처럼 nodefault 지시자를 사용한다.
1.3.6. 배열 프로퍼티(Array Properties)
메모 콤포넌트에는 TStrings 클래스 형의 Lines라는 프로퍼티가 있다. 이 프로퍼티를 오브젝트 인스펙터에서 더블 클릭하면 TStrings 프로퍼티 에디터가 동작해서 메모에 들어갈 내용을 입력할 수가 있다. 그런데 Lines 프로퍼티를 실행 시에 프로그램에서 사용할 때 Memo1.Lines.Strings[1] := '아리랑 1호'; 이런 식으로 배열의 형태로 사용할 수가 있다. 이런 프로퍼티를 배열 프로퍼티라고 한다. 배열 프로퍼티는 하나의 프로퍼티를 가지고 다수의 값을 조작할 수 있도록 해 주며 각 값들은 인덱스를 통해서 참조하게 된다. Classes.pas에 선언되어 있는 TStrings 클래스와 TStringList 클래스의 일부분을 예로 들어 설명하겠다.

리스트 1.8 배열 프로퍼티 예제
unit Classes.pas

  { 생략 }
  
  { TStrings class }

  TStrings = class(TPersistent)
  private
    { 생략 }
    function GetValue(const Name: string): string;
    procedure SetValue(const Name, Value: string);
  protected
    { 생략 }
    function Get(Index: Integer): string; virtual; abstract;
    function GetObject(Index: Integer): TObject; virtual;
    procedure Put(Index: Integer; const S: string); virtual;
    procedure PutObject(Index: Integer; AObject: TObject); virtual;
  public
    { 생략 }
    property Objects[Index: Integer]: TObject read GetObject write PutObject;
    property Values[const Name: string]: string read GetValue write SetValue;
    property Strings[Index: Integer]: string read Get write Put; default;
  end;

{ 생략 }

{ TStringList class }

  TStringList = class(TStrings)
  private
    { 생략 }
  protected
    { 생략 }
    function Get(Index: Integer): string; override;
    function GetObject(Index: Integer): TObject; override;
    procedure Put(Index: Integer; const S: string); override;
    procedure PutObject(Index: Integer; AObject: TObject); override;
  public
    { 생략 }
  end;
  
{ 생략 }

implementation

{ 생략 }

{ TStrings }

function TStrings.GetObject(Index: Integer): TObject;
begin
  Result := nil;
end;

function TStrings.GetValue(const Name: string): string;
var
  I: Integer;
begin
  I := IndexOfName(Name);
  if I >= 0 then
    Result := Copy(Get(I), Length(Name) + 2, MaxInt) else
    Result := '';
end;
procedure TStrings.Put(Index: Integer; const S: string);
var
  TempObject: TObject;
begin
  TempObject := GetObject(Index);
  Delete(Index);
  InsertObject(Index, S, TempObject);
end;

procedure TStrings.PutObject(Index: Integer; AObject: TObject);
begin
end;

procedure TStrings.SetValue(const Name, Value: string);
var
  I: Integer;
begin
  I := IndexOfName(Name);
  if Value <> '' then
  begin
    if I < 0 then I := Add('');
    Put(I, Name + '=' + Value);
  end else
  begin
    if I >= 0 then Delete(I);
  end;
end;

{ TStringList }

function TStringList.Get(Index: Integer): string;
begin
  if (Index < 0) or (Index >= FCount) then Error(@SListIndexError, Index);
  Result := FList^[Index].FString;
end;

function TStringList.GetObject(Index: Integer): TObject;
begin
  if (Index < 0) or (Index >= FCount) then Error(@SListIndexError, Index);
  Result := FList^[Index].FObject;
end;

procedure TStringList.Put(Index: Integer; const S: string);
begin
  if Sorted then Error(@SSortedListError, 0);
  if (Index < 0) or (Index >= FCount) then Error(@SListIndexError, Index);
  Changing;
  FList^[Index].FString := S;
  Changed;
end;

procedure TStringList.PutObject(Index: Integer; AObject: TObject);
begin
  if (Index < 0) or (Index >= FCount) then Error(@SListIndexError, Index);
  Changing;
  FList^[Index].FObject := AObject;
  Changed;
end;


TStrings 클래스에서 Objects, Values, Strings 프로퍼티는 모두 배열 프로퍼티로 선언되어 있다. 배열 프로퍼티는 일반 프로퍼티와 여러 가지 다른 특징을 가지고 있는데 첫째는 배열 프로퍼티를 선언할 때에는 Strings[Index : Integer]처럼 프로퍼티 이름 다음에 일반 배열처럼 괄호로 둘러 싸인 인덱스 인자 리스트를 선언해야 하며 인자는 일반 프로시저나 함수에서처럼 선언하면 된다. 두번째로 읽기 메소드나 쓰기 메소드를 제공하는 경우 데이터 필드를 직접 조작하면 안 되고 반드시 Get, Put처럼 메소드로 선언해 주어야 한다. 세번째로 인덱스 인자는 일반 배열과 달리 반드시 서수형일 필요는 없다. Values 프로퍼티처럼 문자열을 사용할 수도 있다. 네번째로 배열 프로퍼티는 일반 배열처럼 포인터를 통해 배열 전체를 참조할 수는 없고 단순히 각 아이템만 참조할 수 있다.
배열 프로퍼티의 읽기 메소드는 Get, GetObject, GetValue처럼 프로퍼티 선언 시 사용한 인덱스 인자 리스트와 같은 인자를 가져야 하며 쓰기 메소드는 Put, SetObject, SetValue처럼 인덱스 인자와 프로퍼티의 데이터 형과 같은 형의 인자를 하나 더 가지도록 선언해야 한다.
예제에 있는 프로퍼티는 모두 단일 인덱스를 가지는 일차원 배열 프로퍼티지만 아래와 같이 다차원 배열 프로퍼티도 만들 수 있다.

type
  TMatrix = class
  private
    { 생략 }
    function GetEntry(ACol, ARow : Integer) :Real;
    procedure SetEntry(ACol, ARow : Integer, Value : Real);
  public
    property Entry[ACol, ARow : Integer] : Real 
      read GetEntry 
      write SetEntry
  end;


1.3.7. 기본 배열 프로퍼티(Default Array Properties)
TStrings 클래스의 Strings 프로퍼티를 보면 default 라는 지시자가 보일 것이다. 프로퍼티 초기값에서 설명한 default 지시자와 같은데 이상하게도 지시자 다음에 초기값이 보이지 않는다. default 지시자는 두 가지 용도로 사용된다. 하나는 위에서 설명한 프로퍼티 초기값을 설정할 때 사용하고 나머지 하나는 기본 배열 프로퍼티를 선언할 때 사용한다. 배열 프로퍼티를 기본 배열 프로퍼티로 선언하면 프로퍼티를 참조할 때 프로퍼티 이름과 배열 인자를 사용해서 참조할 수도 있지만 객체 이름만으로도 사용할 수 있도록 해준다. Memo1.Lines.Strings[0] 을 이용해서 Lines 프로퍼티의 첫번째 문자열을 참조할 수도 있지만 Memo1.Lines[0]를 사용해도 된다는 말이다. 눈치채신 독자도 있겠지만 기본 배열 프로퍼티는 하나 밖에 선언할 수 없음을 유의해야 한다.
1.3.8. 인덱스 프로퍼티(Indexed Properties)
배열 프로퍼티는 하나의 프로퍼티 이름으로 다수의 값을 조작할 수 있도록 해주는데 반해 인덱스 프로퍼티는 하나의 읽기/쓰기 메소드를 통해 다수의 프로퍼티를 조작할 수 있도록 해준다. 아래 예제 코드를 한번 살펴보자. 예제 코드는 리스트 6.7에서 사용한 TVehicle 클래스를 인덱스 프로퍼티를 설명하기 좋게 약간 수정했다.
인덱스 프로퍼티를 선언할 때는 index 지시자와 인덱스 번호를 프로퍼티 데이터 형과 읽기 메소드 사이에 써 준다. 예제에서 보는 것처럼 TVehicle 클래스의 Width, Height, Length 프로퍼티는 모두 GetDimension, SetDimension 메소드를 사용해서 값을 조작하게 되며 인덱스를 통해서 각 프로퍼티를 구별하도록 되어 있다.

리스트 1.9 인덱스 프로퍼티 예제
type
  TVehicle = class(TComponent)
  private
    FDimensions : Array[1..3] of integer;

    function  GetDimension(Index : integer) : Integer;
    procedure SetDimension(Index : Integer; const Value : integer);

  public
    constructor Create(AOwner : TComponent); override;

    property Width : integer index 1 read GetDimension write SetDimension;
    property Height : integer index 2 read GetDimension write SetDimension;
    property Length : integer index 3 read GetDimension write SetDimension;
  end;

implementation

function TVehicle.GetDimension(Index : Integer) : Integer;
begin
  Result := FDimensions[Index];
end;

procedure TVehicle.SetDimension(Index : Integer; const Value : Integer);
begin
  if FDimensions[Index] <> Value then
FDimensions[Index] := Value;
end;


1.3.9. 프로퍼티 저장 여부 결정
어플리케이션 개발자가 오브젝트 인스펙터에서 프로퍼티 값을 변경하면 그 값들은 폼 파일(*.dfm)에 저장된다. 사실 폼 파일은 윈도 리소스 파일이기 때문에 컴파일할 때 실행 파일에 덧붙여지게 되며 어플리케이션이 실행될 때는 폼을 생성하고 초기값들로 설정된 각 프로퍼티의 값들을 이 리소스를 이용해서 최종 값(어플리케이션 개발자가 폼 설계 시 설정한 값)으로 설정하게 되어 있다. 폼 파일에는 폼 뿐만 아니라 폼이 가지고 있는 모든 콤포넌트의 Published 프로퍼티가 프로퍼티 이름과 값의 형태로 저장되는데 앞에서도 애기한 것처럼 초기값과 다를 경우에만 저장된다. 대부분의 경우 이런 일반적인 처리 방법을 사용하면 되지만 가끔씩 콤포넌트 개발자는 프로퍼티를 저장할지 말지를 특별히 지정해야 할 경우가 생긴다. 나중에 실전 예제에서 나오겠지만 About 프로퍼티 같은 경우 콤포넌트에 대한 일반적인 정보(개발자, 저작권 등)만을 나타낼 뿐 콤포넌트의 동작과는 전혀 무관한 프로퍼티이기 때문에 굳이 불필요한 프로퍼티를 폼 파일에 저장해서 실행 파일의 크기를 늘릴 필요는 없다.
프로퍼티 값을 저장할지 말지를 결정하는 방법은 앞에서 얘기한 default, nodefault 지시자와 함께 stored 지시자를 사용한다. stored 지시자는 프로퍼티를 선언할 때 write 절 다음에 사용하며 Boolean 형 상수나 Boolean 형을 가지는 데이터 필드 또는 인자가 없고 Boolean 형을 반환하는 함수를 바로 뒤에 써 준다. 아래 예제는 Controls.pas에서 발췌한 것으로 ClientHeight의 경우는 상수를 사용하였고 Color 프로퍼티는 함수를 사용하고 있다.

리스트 1.10 stored 지시자 사용 예제
unit Controls.pas
...
property ClientHeight: Integer read GetClientHeight write SetClientHeight stored False;
property Color: TColor read FColor write SetColor stored IsColorStored default clWindow;
...
function TControl.IsColorStored: Boolean;
begin
  Result := not ParentColor;
end;




콤포넌트는 어플리케이션 실행 시에 자신의 프로퍼티를 폼 파일에서 읽어 들인 후에 다른 초기화 작업을 수행할 수 있도록 Loaded 라는 가상 메소드를 호출하도록 되어 있다. 그래서 프로퍼티가 읽혀진 후에 어떤 작업을 하고 싶으면 Loaded 메소드를 오버라이드해서 하고 싶은 작업을 추가해주면 된다. Loaded 메소드는 콤포넌트가 화면에 보여지기 전에 호출되므로 화면이 깜박거리는 것에 대한 걱정은 하지 않아도 된다.





저작권에 대한 공지
이 문서에 포함된 모든 글과 소스 코드는 작자인 저 김성동의 지적 재산이지만 무단으로 배포하거나 책이나 강의 교재등을 위한 복제 등 저작권을 저해하는 행위를 제외하고는 자유롭게 사용할 수 있습니다.
또한 이 페이지에 대한 링크는 자유롭게 할 수 있으나 전문을 전제하는 일은 허용하지 않습니다.