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


3.4. 콤포넌트 에디터(Component Editor)

3.4.1. 개요
프로퍼티 에디터는 오브젝트 인스펙터에서 사용되지만 콤포넌트 에디터는 폼 디자이너에서 폼을 설계할 때 사용한다. 콤포넌트 에디터는 폼을 설계할 때 콤포넌트를 더블 클릭 했을 때 어떤 동작을 할 것인지 마우스 오른 쪽 버튼으로 콘텍스트 메뉴를 동작시켰을 때 어떤 작업을 할 수 있도록 할 것인지를 결정한다. 모든 콤포넌트 에디터는 TComponentEditor에서 상속 받아 만들어진다.
TComponentEditor는 dsgnintf.pas 유닛에 아래와 같이 선언되어 있다.

TComponentEditor = class(TInterfacedObject, IComponentEditor)
  private
    FComponent: TComponent;
    FDesigner: IFormDesigner;
  public
    constructor Create(AComponent: TComponent; ADesigner: IFormDesigner); virtual;
    procedure Edit; virtual;
    procedure ExecuteVerb(Index: Integer); virtual;
    function GetIComponent: IComponent;
    function GetDesigner: IFormDesigner;
    function GetVerb(Index: Integer): string; virtual;
    function GetVerbCount: Integer; virtual;
    function IsInInlined: Boolean;
    procedure PrepareItem(Index: Integer; const AItem: TMenuItem); virtual;
    procedure Copy; virtual;
    property Component: TComponent read FComponent;
    property Designer: IFormDesigner read GetDesigner;
  end;



콤포넌트 에디터도 프로퍼티 에디터와 마찬가지로 TComponentEditor에서 상속 받아서 새 클래스를 만들고 필요한 메소드를 오버라이드 해서 원하는 작업을 해 주도록 하면 된다. 폼 디자이너에서 콤포넌트가 선택되면 등록된 콤포넌트 에디터가 생성된다. 별도록 등록된 콤포넌트 에디터가 없으면 델파이는 TDefaultEditor 라는 콤포넌트 에디터를 생성한다. TDefaultEditor는 콘텍스트 메뉴에 새로운 메뉴를 추가하지는 않고 콤포넌트를 더블 클릭했을 때 콤포넌트의 이벤트 중에서 OnCreate, OnChange, OnClick 이벤트를 순서대로 찾아 봐서 있으면 그 이벤트에 대한 이벤트 핸들러를 만들어 주고 없으면 그 콤포넌트의 첫번째 이벤트에 대한 이벤트 핸들러를 만들어 준다.
콤포넌트를 더블 클릭했을 때 원하는 작업을 수행하고 싶으면 Edit 메소드를 오버라이드한다. Edit 메소드는 기본적으로 GetVerbCount 의 반환값이 0보다 크면 첫번째 콘텍스트 메뉴 아이템을 동작시키도록 되어 있다. 콤포넌트의 콘텍스트 메뉴에 아이템을 추가하고 싶으면 GetVerbCount와 GetVerb 메소드, 그리고 ExecuteVerb 메소드를 오버라이드 한다. GetVerbCount 메소드는 추가할 메뉴 아이템의 개수를 반환하도록 해 준다. GetVerb 메소드는 추가될 메뉴의 캡션을 반환해야 한다. 폼 설계시에 추가된 콘텍스트 메뉴 아이템이 선택되면 ExecuteVerb 메소드가 호출된다.
델파이에서는 폼에서 콤포넌트를 선택하고 Ctrl+C 키를 눌러서 콤포넌트를 클립보드로 복사(Copy)하고 이렇게 복사된 콤포넌트를 붙이기(Paste) 할 수 있다. 델파이는 자체적으로 클립보드 형식을 만들어서 사용하기 때문에 이런 작업이 가능한데 클립 보드 형식을 변경하고 싶을 때는 Copy 메소드를 이용한다.
현재 콤포넌트 에디터가 편집 중인 콤포넌트는 Component 프로퍼티로 참조하며 폼 디자이너는 Designer 프로퍼티를 이용한다.

콤포넌트 에디터는 RegisterComponentEditor 프로시저를 이용해서 델파이 IDE에 등록한다. 이 프로시저의 원형은 아래와 같다.

procedure RegisterComponentEditor(ComponentClass: TComponentClass;
  ComponentEditor: TComponentEditorClass);



첫번째 인자는 등록할 콤포넌트 에디터를 사용할 콤포넌트 클래스이며 두번째 인자는 등록할 콤포넌트 에디터의 클래스이다.
3.4.2. TdpbLabel 콤포넌트 에디터
콤포넌트 에디터를 만들기 위한 기본적인 지식을 살펴 봤으므로 이전 장에서 개발한 TdpbLabel 콤포넌트의 여러가지 스타일을 대화 상자에서 한번에 편집할 수 있도록 콤포넌트 에디터를 하나 만들어 보자.
먼저 콤포넌트를 편집할 대화 상자를 아래 그림처럼 설계한다.

그림 3-8 콤포넌트 편집 대화 상자


폼의 유닛 파일에 아래와 같이 TdpbLabelEditor 클래스를 선언한다.

type
  TdpbLabelEditor = class(TComponentEditor)
  public
    procedure SynchronizeLabel(Dest, Source : TdpbLabel);
    procedure ExecuteVerb(Index: Integer); override;
    function GetVerb(Index: Integer): string; override;
    function GetVerbCount: Integer; override;
  end;


TComponentEditor로부터 상속 받고 ExecuteVerb, GetVerb, GetVerbCount 함수를 오버라이드한다. GetVerbCount의 반환값이 0보다 크면 첫번째 메뉴 아이템을 실행시키도록 되어 있으므로 Edit 메소드는 굳이 오버라이드하지 않아도 될것이다.
다음 TdpbLabelEditor의 가상메소드들을 아래와 같이 구현한다.

{ TdpbLabelEditor }

function TdpbLabelEditor.GetVerb(Index: Integer): string;
begin
  case Index of
    0 : Result := '모양 편집...';
    1 : Result := 'TdpbLabel에 대하여...';
  end;
end;

function TdpbLabelEditor.GetVerbCount: Integer;
begin
  Result := 2;
end;

{ 편집 폼에 있는 레이블과 편집하는 레이블사이의 프로퍼티 동기화 }
procedure TdpbLabelEditor.SynchronizeLabel(Dest, Source: TdpbLabel);
begin
  Dest.Caption := Source.Caption;
  Dest.Font := Source.Font;
  Dest.Alignment := Source.Alignment;
  Dest.Layout := Source.Layout;
  Dest.TextStyle := Source.TextStyle;
  Dest.ShadowColor := Source.ShadowColor;
  Dest.ShadowDepth := Source.ShadowDepth;
  Dest.ShadowDirection := Source.ShadowDirection;
  Dest.EdgeStyle := Source.EdgeStyle;
  Dest.Edges := Source.Edges;
end;

{ 콘텍스트 메뉴 실행 }
procedure TdpbLabelEditor.ExecuteVerb(Index: Integer);
var
  DLG : TfrmLabelEdit;
begin
  case Index of
    { 첫번째 메뉴 }
    { 콤포넌트를 더블 클릭해도 실행된다. }
    0 :
    begin
      DLG := TfrmLabelEdit.Create(nil);
      try
        SynchronizeLabel(DLG.lblPreview, TdpbLabel(Component));
        DLG.Caption := Component.Owner.Name + '.' + Component.Name + ' 편집';
        if DLG.ShowModal = mrOk then
        begin
          SynchronizeLabel(TdpbLabel(Component), DLG.lblPreview);
          { 콤포넌트를 변경했으면 반드시 호출해 주어야 한다. }
          Designer.Modified;
        end
      finally
        DLG.Free;
      end;
    end;
    { 두번째 메뉴 }
    1 :
    begin
      ShowMessage('델파이 프로그래밍 바이블 예제 콤포넌트');
    end;
  end;
end;


콘텍스트 메뉴에 두개의 아이템을 추가하도록 GetVerbCount의 반환값을 2로 해주고 GetVerb에서 각 메뉴 아이템에 나타낼 캡션을 반환해 준다.
ExecuteVerb에서는 첫번째 메뉴에 대해 실제로 편집 대화 상자를 띄우고 TdpbLabel의 여러 프로퍼티를 편집하게 만들고 두번째 메뉴는 단순한 메시지 박스만 하나 띄우도록 한다.
첫번째 메뉴가 선택되면 TfrmLabelEdit 폼을 생성하고 lblPreview 콤포넌트의 프로퍼티를 콤포넌트 에디터의 Component 프로퍼티 값과 일치되도록 동기화를 해주고 대화 상자를 보여 준다.
콤포넌트 에디터에서 콤포넌트의 프로퍼티를 변경했으면 반드시 Designer 프로퍼티의 Modified 메소드를 호출해 주어야 한다. 이렇게 하지 않으면 변경된 프로퍼티가 편집 중인 콤포넌트에 정상적으로 적용되지 않으며 폼 디자이너가 폼 파일이 변경되었다는 사실을 모르게 된다. 따라서 변경된 내용을 저장할 수 없게 된다.
TdpbLabelEditor는 Edit 메소드를 오버라이드하지 않았기 때문에 사용자가 콤포넌트를 더블 클릭하면 첫번재 메뉴가 선택된것처럼 동작한다.
마지막으로 콤포넌트 에디터를 등록하기 위해 Register 프로시저를 만들고 이 프로시저 내에서 RegisterComponentEditor 프로시저를 아래와 같이 호출해 준다.

procedure Register;
begin
  { 콤포넌트 에디터 등록 }
  RegisterComponentEditor(TdpbLabel, TdpbLabelEditor);
end;


콤포넌트 패키지에 지금 만든 유닛을 추가해서 다시 컴파일하면 아래와 같이 콤포넌트 에디터가 동작할 것이다. 여기서 주의할 것이 하나 있는데 패키지에 콤포넌트와 콤포넌트 에디터를 추가할 때 반드시 콤포넌트를 먼저 추가해 주어야 한다. 왜냐하면 TdpbLabel 콤포넌트를 편집하는 콤포넌트 에디터의 폼이 TdpbLabel를 사용하고 있기 때문이다. 반드시 TdpbLabel이 먼저 등록되고나서 콤포넌트 에디터가 등록되어야 한다. 그렇지 않으면 콤포넌트 에디터가 등록될 때 TdpbLabel 콤포넌트를 찾지 못한다는 에러가 발생할 것이다.

그림 3-9 TdpbLabelEditor 콤포넌트 에디터의 콘텍스트 메뉴




그림 3-10 TdpbLabelEditor 콤포넌트 에디터의 첫번째 메뉴 동작 화면




그림 3-11 TdpbLabelEditor 콤포넌트 에디터의 두번째 메뉴 동작 화면





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