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

C++빌더 팁&트릭
C++Builder Programming Tip&Tricks
[586] C++빌더에서도 화면디자인에 내 컨트롤을 적용하는 트릭을 써보자.
김태선 [jsdkts] 8750 읽음    2006-04-01 14:12
델마당에 가보니 변태적 상속이란 제목하에 양병규님이
동일한 명칭으로 클래스를 재정의하는 기법으로,
컴포넌트의 기본 기능을 수정한 새 컴포넌트를 기존의 디자인한 컴포넌트에 그대로 적용하여
화면 디자인은 이전처럼하고 기능은 새로 정의된 클래스로 동작하게 하는 방법에 관한
팁 기사가 있어 이를 C++에서도 함 해보자는 생각에 간단히 긁적이다가 미니 샘플을 올립니다.

C++에서는 델파이처럼 동일 명칭의 클래스를 재정의하는 것을 허용하지 않습니다.
그러므로 델파이처럼 똑 같이는 할수 없으나 namespace를 활용하는 약간의 수고만 하면
동일한 결과를 얻을 수 있습니다.

먼저 양병규님 기사를 보시죠.
//---------------------------------------------------------------------------
변태적인 상속 
//---------------------------------------------------------------------------

음....

이것 역시 옆에 사람이 물어봐서 답변할 겸 셈플로 만들어 본겁니다. (전 이렇게 작업하지는 않는데... 나름대로 급할때 사용될수 있을것같아서...)


TPageControl에서 페이지를 추가하면 TTabSheet가 생깁니다.

TTabSheet를 상속해서 TMyTabSheet로 만들어서 Test라는 메소드를 추가합니다.

그리고 이것을 동적으로 생성해서 TPageControl에 넣어서 사용합니다.

TPageControl에 ActivePage라는 프로퍼티가 있습니다.

그런데 이넘이 TTabSheet형이므로

TMyTabSheet( PageControl1.ActivePage ).Test;

이렇게 타입캐스팅해서 사용해야합니다.

그런데....

이게 싫답니다. 타입캐스팅 안하고 바로 ActivePage프로퍼티가 TMyTabSheet형으로 리턴되게서 PageControl1.ActivePage.Test; 이렇게 쓰고 싶답니다.

그래서....

그러면 TPageControl을 상속해서 새로운 콤포넌트로 만들라고 했드니 그것도 싫답니다.

새로운 콤포넌트 안만들고 지금 이대로 그냥 하고 싶답니다.

쩝....

그래서 이 방법을 소개했습니다.

^^;

머... 다소 변태적이긴하지만 머... 문법적으로 문제는 없으므로 일단 아쉬운대로 사용할수는 있습니다만... 좀 혼란스러울수도 있으므로 별로 좋은 예제는 아닙니다.

그냥 한번씩 구경해 보시고.....(알아서들...^^ )


자..... 간략한 설명 들어갑니다. ^^

새 유닛을 만들어서 다음과 같이 새로운 탭시트를 정의합니다.

uses
ComCtrls;

type
TMyTabSheet = class(TTabSheet)
public
   procedure Test(const S: String);
end;

그리고.... PageControl도 이렇게 새로 정의합니다.

TPageControl = class(ComCtrls.TPageControl)
private
   function GetActivePage: TMyTabSheet;
public
   property ActivePage: TMyTabSheet read GetActivePage;
end;

function TPageControl.GetActivePage: TMyTabSheet;
begin
Result := TMyTabSheet( inherited ActivePage );
end;


그리고...

폼 소스에 uses에 이 유닛을 추가하고(ComCtrls보다 뒤에 있어야함)

폼에 탭이 없이 그냥 빈 TPageControl을 올려놓고....

TMyTabSheet를 동적생성해서 PageControl에 페이지를 추가합니다.

그리고

소스코드에디터에서 PageControl1. 을 쓰고 기다려보면 멤버목록창이 뜨는데 거기에 ActivePage: TMyTabSheet로 나오는걸 볼수 있을겁니다. 물론 그렇게 사용할수도 있습니다.

이 방법의 핵심은...

TPageControl = class(ComCtrls.TPageControl)

여기가 핵심입니다.

같은 이름으로 상속을 할수 있다는 점을 이용한건데....(유닛이 달라야..)

이렇게하면 폼디자이너에서는 기존의 콤포넌트로 디자인하고 그 콤포넌트의 기능을 새로 추가하거나 변경해서 적용할수있습니다.

주의할점이 많이 있습니다.

PageControl1 이 새로운 유닛에 있는 TPageControl인지 ComCtrls에 있는 TPageControl인지를 잘 구분해야합니다.

다소... 변태같은 방법이긴 하지만.... 새 콤포넌트를 만들지 않고 기존의 콤포넌트로 디자인하면서 그 콤포넌트의 새 기능을 정의할수 있다는 점이 가장 큰 장점인데....

머.... ^^;

이런 방법도 있다... 라는 정도로만 알고계시는게 좋을것 같습니다.




다음은 제 기사입니다.
//---------------------------------------------------------------------------
// 다음은 C++빌더에서 하는 비슷한 방법입니다.
//---------------------------------------------------------------------------
결과는 같아도 방법은 상당히 차이가 있습니다.

샘플용 프로그램으로 폼에 패널하나 올려 놓은 것입니다.
//---------------------------------------------------------------------------
// unit1.cpp

#include <vcl.h>
#pragma hdrstop

namespace ME // C++에서는 동일명칭으로 클래스를 만들수 없으므로 네임스페이스로 포장을 해 줍니다.
{
// 새로 패널 컴포넌트를 정의해서 만듭니다.
class TPanel : public Extctrls::TPanel
{
public:
    __fastcall TPanel(TComponent *Owner) : Extctrls::TPanel(Owner)
    {
        Color = clRed;  // 패널을 생성하면 기본 바탕이 붉은 색이 되게 합니다. 왜냐구요. 대~한민국~
    }
    void    color()  // 이렇게 유저 함수를 추가하는 것도 아무 문제 없습니다.
    {
        Color = clBlue;
    }
};
};

#include "Unit1.h"

namespace ME
{
TForm1 *Form1;
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"

//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------
// Your functions....


//---------------------------------------------------------------------------
}; // end namespace ME

// 마지막에 이 폼을 생성하는 함수를 하나 만들어 줍니다.  프로젝트 메인 cpp에서 호출할 것입니다.
// 이건 위의 namespace 바깥에 만들어야 합니다.
void    CreateForm1() 
{
    Application->CreateForm(__classid(ME::TForm1), &ME::Form1);
};



다음은 헤더이며 namespace로 TForm1 클래스를 감싸줍니다.
//---------------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ExtCtrls.hpp>
//---------------------------------------------------------------------------
namespace ME
{

class TForm1 : public TForm
{
__published:    // IDE-managed Components
    TPanel *Panel1;
private:    // User declarations
public:        // User declarations
    __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------

};
#endif
//---------------------------------------------------------------------------



다음은 프로젝트 메인 cpp 파일이며 여기에 약간 손을 봅니다.
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop
//---------------------------------------------------------------------------
USEFORM("Unit1.cpp", Form1);
//---------------------------------------------------------------------------
void    CreateForm1();

WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
    try
    {
        Application->Initialize();
        CreateForm1();  // namespce 참조로 폼을 생성하는게 여기서 안되므로 이렇게 함수를 대신 부릅니다.
        Application->Run();
    }
    catch (Exception &exception)
    {
        Application->ShowException(&exception);
    }
    catch (...)
    {
        try
        {
            throw Exception("");
        }
        catch (Exception &exception)
        {
            Application->ShowException(&exception);
        }
    }
    return 0;
}
//---------------------------------------------------------------------------


코드는 이상입니다.

이렇게 하면 unit1.cpp 에서 
TPanel 을 재정의할때 기본 배경 칼라를 붉은 색으로 한 패널이,
TForm1 폼을 생성하고 나서 아무런 코딩도 적용받지 않는데도
컴파일해서 실행해보면 붉은 색 패널이 나타나는 것을 볼수 있습니다.

다시말해, 디자인은 기존 컴포넌트로 하고,
적용은 내가 새로 제작한 컴포넌트로 되었다는 뜻이죠.

머.. 응용하면 아주 복잡한 기능의 클래스로도 대치도 가능하겠죠.
이때 쬐금 유의해야 할 것은 같은 류의 클래스를 상속받아 새 클래스를 만들어야 합니다.
물론 안그래도 되지만..... TPanel을  초코파이챠드로 둔갑시켜 놓는다던지 하면  좀 이상할 것 같아서... ㅡ,,ㅡ;
암튼 알아서 씁시다.

이렇게 하고 나서도 화면에 다른 컴포넌트를 놓는 등의 작업에는 지장을 주지 않습니다.

어차피 자주 쓰일 테크닉이 아니니 길게 안 쓰겠습니다.

그럼..

+ -

관련 글 리스트
586 C++빌더에서도 화면디자인에 내 컨트롤을 적용하는 트릭을 써보자. 김태선 8750 2006/04/01
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.