본문 바로가기
Delphi Tip/컴포넌트

델파이 컴포넌트(Component) 4편

by MonoSoft 2023. 7. 19.
728x90
반응형

델파이 컴포넌트(Component)

728x90

 

 

 

 

 

이벤트 추가

 

우선 이벤트를 받아오는 함수에 대해서 알아야 할 것입니다.

WM_MOUSEMOVE이벤트가 발생할때 반응하는

함수를 만들어 보도록 합시다.

 

우선 윈도우 메시지를 처리하는 함수를

다음은 Controls.pas의 함수의 선언입니다.

 

procedure WMMouseMove(var Message: TWMMouseMove); message WM_MOUSEMOVE;

 

지금까지 보아오던 함수와는 조금 다르죠?

뒤에 있는 'message WM_MOUSEMOVE'는 'WM_MOUSEMOVE'이벤트를

받아서 처리를 하겠다는 의미일테구요.

 

매개변수로 들어간 Message는 처리할 메시지의 값이 들어가겠지요?

그럼 이 함수의 실체를 보도록 합시다

 

 

procedure TControl.WMMouseMove(var Message: TWMMouseMove);

begin

  inherited;

 

  if not (csNoStdEvents in ControlStyle) then

    with Message do MouseMove(KeysToShiftState(Keys), XPos, YPos);

end;

 

복잡한건 집어치우고 Message를 받아서 MouseMove함수를 호출하고 있습니다.

 

자 어떻게 이루어졌는지 알았으니

우리의 컴포에서 WM_MOUSEMOVE메시지를 처리하도록 기능을 넣어 봅시다.

 

자, 선언부에는 다음과 같이 넣어주고요..

 

위의 예에서는 Message를 TWMMouseMove라는 형으로 정의를 했지만

우리는 좀더 일반적인 TMessage를 이용하도록 합시다.

 

protected

{ Protected declarations }

procedure WMMouseMove(var Message: TMessage); message WM_MOUSEMOVE;

 

위와 같이 함수를 선언했으면 이제 구현을 해야겠지요?

 

procedure TMyButton.WMMouseMove(var Message: TMessage);

begin

  inherited;

  with Message do

    Caption := Format( '%d, %d', [ LOWORD( lParam ), HIWORD( lParam ) ] );

end;

 

버튼의 캡션을 'x,y'값으로 바꿔주는 것입니다.

자, 우리의 컴포를 위와같이 바꿔주고 실행해 봅시다.

 

버튼위에 마우스를 갖다 대면 어떤일이 벌어지나요?

버튼의 캡션이 바뀌는 것을 확인하셨나요?

 

자 그런데 위의 함수에도 'inherited'가 있네요?

이녀석이 하는 역할은 또 무엇일까요?

 

이것을 알아보기 위해서 우리의 버튼의

OnMouseMove이벤트에 다음을 연결해 봅시다.

 

(몸으로 때워서 알아내는 것처럼 기억에 오래남는 것은 없겠지요?)

 

procedure TForm1.MyButton1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);

begin

  Caption := Format( '%d, %d', [ x, y ] );

end;

 

자, 준비가 되었으면 실행을 해서 확인을 해 봅시다.

폼의 캡션에도 마우스의 위치가 표시되는 것을 확인하셨나요?

 

그럼, 이제 'WMMouseMove'에 있는 'inherited'를 지우고 다시 확인을 해봅시다.

 

실행을 하면.. 짜잔~~ 어때요 아직도 폼의 캡션에 좌표가 나타나나요?

'WMMouseMove'에서 'inhedited'의 역할을 아셨죠?

 

그럼 이제 준비가 끝났으니 진짜루 우리만의 이벤트를 만들어 봅시다.

 

마우스가 컴포넌트위에 들어오면 어떤 메시지가 발생하는지 아시는지요?

또는 마우스가 컴포넌트위를 벗어나면 어떤 메시지가 발생할까요?

 

크크, 이미 알고 계시다구요?

 

 

 

CM_MOUSEENTER : 마우스가 컨트롤 위로 올라오면 발생하는 이벤트

CM_MOUSELEAVE : 마우스가 컨트롤 밖으로 나가면 발생하는 이벤트

 

이 두녀석을 가지고 마우스가 우리의 버튼위에 접근하거나 나가면

이벤트를 발생하도록 만들어 봅시다.

 

우선은 CM_MOUSEENTER와 CM_MOUSELEAVE가

제대로 작동을 하는지를 확인해야 속이 시원하겠죠?

 

 

procedure CmMouseEnter(var Message: TMessage); message CM_MOUSEENTER;

procedure CmMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;

 

 

 

위와 같이 선언을 하구 다음과 같이 구현을 합시다.

 

procedure TMyButton.CmMouseEnter(var Message: TMessage);

begin

  inherited;

  Caption := '들어왔네!';

end;

 

procedure TMyButton.CmMouseLeave(var Message: TMessage);

begin

  inherited;

  Caption := '나갔네!';

end;

 

우리의 컴포에 추가를 했으면, 마찬가지로 실행을 해 봅시다.

어때요?

마우스가 버튼위에 위치하면 버튼의 캡션이 바뀌나요?

 

크크.. 재미있는 이벤트를 하나 알았네요..

 

 

실제로 TSpeedButton의 소스를 보면 이를 이용해서 마우스가

올라오면 표시를 하도록 되어 있습니다.

 

 

 

위의 두 이벤트가 잘 작동하는 것은 보았으니

이제는 원래 목적대로 우리의 이벤트를 만들어 봅시다.

 

 

 

먼저 강좌에서 TNotifyEvent라는 녀석을 보았죠?

 

간단하게 이녀석을 이용해서 작업을 해 봅시다.

우선 VCL에서는 어떻게 했는지를 봅시다.

 

property OnClick: TNotifyEvent read FOnClick write FOnClick;

역시 복잡한 설명은 나중으로 미루고

TNotifyEvent형의 OnClick을 프로퍼티로 선언했음을 알수 있습니다.

 

여기서 OnClick이라는 것은 Object Inspector에 나타나는

이름일 뿐이므로 실제로 함수포인터의 역할을 할 녀석이 필요합니다.

 

위의 예에서는 'FOnClick'이라는 녀석을 이용했네요..

그런 'FOnClick'의 정체는 무엇일까요?

 

FOnClick: TNotifyEvent; 프로퍼티인 OnClick의 실체는 바로 이녀석입니다.

바로 TNotifyEvent형의 함수포인터죠 어때요 감이 잡히나요?

 

그럼 위와 같은 형식으로 우리의 메시지를 만들어 봅시다.

 

우선 프로퍼티 이름은 OnMouseEnter와 OnMouseLeave라고 합시다.

 

published

{ Published declarations }

property OnMouseEnter : TNotifyEvent read fOnMouseEnter write fOnMouseEnter;

property OnMouseLeave : TNotifyEvent read fOnMouseLeave write fOnMouseLeave;

 

published 부분에다가 위와 같이 넣어 줍시다.

 

참고로 published에 위와 같이 넣어주면 Objector Inspector에 이름이 나타나게 됩니다.

그럼 물론 fOnMouseEnter와 fOnMouseLeave도 선언을 해야 겠지요?

 

private

{ Private declarations } fOnMouseEnter : TNotifyEvent; fOnMouseLeave : TNotifyEvent;

 

이와 같이 내부적으로만 쓰일 변수는 private에 넣어두는 것이 좋습니다.

싫으시다면 어디에 넣어도 상관은 없겠지만..

 

참 그리고 변수의 형(type)은 관례적으로 'T'를 접두어로 붙이지요?

 

마찬가지로 프로퍼티의 이름을 나타내는

변수로는 프로퍼티 이름앞에 'f'를 접두어로 붙입니다.

 

역시 관례적인 것이니깐 컴파일하는데에는 전혀 지장을 주지는 않지만

다른 사람이 코드를 읽기가 힘들어 집니다.

 

선언은 모두 끝났는데 이것을 어떻게 이용을 할까요?

역시 쉽습니다.

 

procedure TMyButton.CmMouseEnter(var Message: TMessage);

begin

  inherited;

  if Assigned( fOnMouseEnter ) then

    fOnMouseEnter( self );

end;

 

procedure TMyButton.CmMouseLeave(var Message: TMessage);

begin

  inherited;

  if Assigned( fOnMouseLeave ) then

    fOnMouseLeave( self );

end;

 

 

이해가 되시나요?

 

 

 

이해가 되신다면 함수포인터에 대해서 잘 알고 계신것이 됩니다.

 

계속 함수포인터 얘기를 했는데 실체는 이렇습니다.

 

아까도 말씀드렸듯이 OnMouseEnter라는 것은

단순히 Objector Inspector에 나타나는 이름일 뿐이고

실체는 fOnMouseEnter라고 했습니다.

 

그럼 우리가 폼을 디자인할때 이벤트가 어떻게 연결되는지를 알아봅시다.

 

Objector Inspector에서 OnClick을 더블클릭하면

이벤트 핸들러가 생기지요?

 

또는 Application.OnMessage := AppMessage;

와 같이 해주면 OnMessage의

 

함수포인터에는 AppMessage의 주소값이 들어가게 됩니다.

 

그러니까 fOnMouseEnter에 어떤 함수의 주소가 들어가게 된다는 것이지요.

 

그렇다면 내부에서는 어떻게 처리를 해야 할까요?

 

if Assigned( fOnMouseEnter ) then fOnMouseEnter( self );

 

같이 fOnMouseEnter에 어떤 함수가 연결이 되어 있는지를

검사를 한다음 함수가 연결되어 있다면 그 함수를 호출해 주면 되겠지요?

 

fOnMouseEnter는 함수 포인터이므로

fOnMouseEnter( self )라고 해주면 fOnMouseEnter에 연결된 함수를 호출하게 됩니다.

 

procedure TForm1.MyButton1Click(Sender: TObject);

begin

  ShowMessage( '폼에서 눌렸네' );

end;

 

과 같은 소스를 보면 매개변수로 Sender라는 값이 들어오는 것을

볼수 있습니다.

 

이때 Sender는 이벤트가 발생한 컴포넌트의

주소라는 것을 아시지요?

 

우리가 만든 컴포의 경우 fOnMouseEnter( self ) 와 같이

매개변수로 'self'값을 넘겨주기 때문에 이렇게 되는 것입니다.

 

5편 계속....

728x90
반응형

댓글