달력

102011  이전 다음

'2011/10/10'에 해당되는 글 1건

  1. 2011.10.10 [펌] MFC 메시지 리플렉션
메시지 리플렉션이란 무엇인가?

윈도우즈 콘트롤들은 자신들의 부모윈도우들에게 자주 통지메시지를 보냅니다.
예를들어 많은 콘트롤들은 색상 통지 메시지(WM_COLOR 또는 이 메시지의 변형)을
부모에게 보내 부모가 컨트롤의 바탕색을 칠할수 있게 합니다.

윈도우즈와 MFC 버전 4.0 이전에서는 부모윈도우(일반적으로 다이알로그)에 이러한
메시지들을 처리할 책임이 있습니다. 이것은 메시지를 처리하는 코드가 부모윈도우
의 클래스에 있어야 하고 이 메시지를 처리하는 모든 클래스에 중복되어야 한다는
것을 의미합니다. 위의 경우에서 커스텀 백그라운드를 가지는 콘트롤을 원하는 모든 
다이알로그는 색상 통지 메시지를 처리해야합니다. 만약 콘트롤 클래스가 자신의 바탕
색을 다룰수 있게 작성이 된다면 코드를 재사용하기가 쉬워질 것입니다.

MFC 4.0 에서는 이 부모 윈도우가 통지메시지를 처리한다는 이 오래된 구조도 계속
사용할수 있습니다.
추가로 MFC 4.0은 통지메시지를 자식윈도우나 부모윈도우 또는 두개의 윈도우 
모두에서 처리할 수 있는 "메시지 리플렉션"이라고 부르는 기능으로 편리하게 코드를
재사용 할 수 있게 해줍니다.
컨트롤 바탕색 문제에서 부모윈도우에 의존하지않고 리플렉트된 WM_CTLCOLOR 메시지를 
처리해줌으로서 자신의 바탕색을 지정하는 콘트롤 클래스를 작성할 수 있습니다.
( 메시지 리플렉션은 윈도우즈에 의해서 구현되지 않고 MFC에 의해서 구현된 것이기 
때문에 메시지 리플렉션이 작동하기 위해서는 부모윈도우 클래스가 반드시 CWnd 또는
CWnd에서 계승된 클래스를 상속받아야 합니다.)

이전의 MFC에서도 owner-drawn 리스트박스의 WM_DRAWITEM 메시지를 처리하는 부분
과 같은 몇개의 특정 메시지에 대해 가상함수로 메시지 리플렉션과 유사한 기능을
제공했었으나 이 새로운 메시지 리플렉션 구조는 일반적이면서 견고합니다.

메시지 리플렉션은 4.0 이전 버전으로 작성된 코드에 대해서 하위 호환성을 갖습니다.

//

만일 당신의 부모 윈도우 클래스에 지정된 메세지나 메세지 범위를 위한 핸들을 
지정한다면 그것은 자신의 핸들러안에서 베이스 클래스 핸들러함수를 부르지 않으로써 
제공된 동일한 메세지를 위한 리플렉트된 메세지 핸들을 오버라이드 하는 것입니다.
예를 들면, 만일 다이얼로그 박스 클래스에 WM_CTLCOLOR 핸들을 지정한다면, 당신의 
핸들링은 어떤 리플래스된 메세지 핸들들을 오버라이드 하는 것입니다.

만일 부모 윈도우 클래스에서 당신의 WM_NOTIFY 메세지들 범위나 WM_NOFIFY 메세지를 
지정 한다면 그러한 메세지(WM_NOTIFY)를 보내는 자식 컨트롤이 ON_NOTIFY_REFLECT() 
를 통한 리플렉트 메세지를 가지지 않는다면 당신의 핸들러가 처리될 겁니다.
만일 메세지 맵에 ON_NOTIFY_REFLECT_EX() 을 사용한다면 메세지 핸들러는 부모 윈도우가
메세지를 다룰수 있게도 않게도 할수 있습니다.
즉, 핸들러가 TRUE 을 리턴하면, 메세지는 부모에 의해서 다뤄지지만, FALSE 을 
리턴한다면 다뤄지지 않습니다. 주목할것은 리플렉트 메세지는 notification 메세지 전에 
다뤄집니다.

WM_NOTIFY 메세지를 보낼때 컨트롤은 그것을 다룰 첫번째 기회를 제공 받습니다.
만일 다른 리플렉트 메세지가 보내진다면 부모 윈도우가 그것을 다룰 첫번째 기회를 
받습니다. 그리고 나서 컨트롤의 리플렉트된 메세지를 받습니다. 
그렇게 하기 위해서 그것은 컨트롤의 클래스 멥에 핸들러 함수나 적절한 엔트리가 
필요합니다.

리플랙트 메세지를 위한 메세지 맵은 일반적인 notifications 과 조금 다릅니다.
그것은 _REFLECT 라는 추가적인 이름의 덪붙여 집니다. 예를 들어 부모에서 WM_NOTIFY 
메세지를 처리 하기 위해서 당신은 부모의 메세지 맵에 ON_NOTIFY 매크로를 사용합니다.
그리고 자식 콘트롤의 리플랙트 메세지를 다룰려면 ON_NOTIFY_REFLECT 매크로를 자식 
콘트롤의 메세지 맵에 사용해야 합니다. 양쪽모두 파라미터들은 틀립니다. 
주목할 점은 클래스 위자드가 대개 당신을 위해서 메세지 맵 엔트리와 올바른 
파라미터들을 구현한 뼈대가 되는 함수들을 추가해 줍니다.

새로운 WM_NOTIFY 메세지를 위한 정보를 위해서 TN061: ON NOTIFY and WM_NOTIFY 
Messages 을 보세요.

- Message-Map Entries and Handler Function Prototypes for Reflected Messages

리플렉트된 콘트롤 Notification 메세지를 다루기 위해서 테이블 밑에 열거 되어 있는 
메세지 맵 매크로와 함수 형들을 사용 하십시요.

클래스위자드는 대개 당신을 위해서 그러한 메세지 맵 엔트리들을 추가해 줍니다. 

메세지 이름에서 리플랙트 매크로 이름으로 변환하기 위해서 앞에는 ON_ 을 뒤에는 
_REFLECT 을 추가해 주십시요. 예를 들면 WM_CTLCOLOR 는 ON_WM_CTLCOLOR_REFLECT 가 됩니다.

다음과 같은 3가지 규칙을 벗어나는 경우가 있습니다.
. WM_COMMAND notifications 을 위한 매크로는 ON_CONTROL_REFLECT 가 있습니다.
. WM_NOTIFY 리플렉션들은 ON_NOTIFY_REFLECT 입니다.
. ON_UPDATE_COMMAND_UI 리플랙션들은 ON_UPDATE_COMMAND_UI_REFLECT 입니다.

위의 각경우에 당신은 핸들러 멤버 함수의 이름을 반드시 지정해야 합니다. 
다른 경우에는 핸들러 함수의 표준이름을 반드시 사용해야 합니다.

함수의 파라미터와 리턴값에 대한 의미는 On 으로 시작하는 함수와 일반함수 밑에 설명 
되어 있습니다.

예를 들면 CtlColor 를 OnCtlColor 에 설명 되어 있습니다.
여러개의 리플랙트된 메세지 핸들러들은 부모윈도우에 있는 동일한 핸들러보다 적은 
파라미터들의 필요합니다.

문서에 공식적인 파라미터 이름들과 함수이름들이 있습니다.

Map entry                                    Function prototype 
ON_CONTROL_REFLECT( wNotifyCode, memberFxn ) afx_msg void memberFxn ( ); 
ON_NOTIFY_REFLECT( wNotifyCode, memberFxn )  afx_msg void memberFxn ( NMHDR * pNotifyStruct, LRESULT* result ); 
ON_UPDATE_COMMAND_UI_REFLECT( memberFxn )    afx_msg void memberFxn ( CCmdUI* pCmdUI ); 
ON_WM_CTLCOLOR_REFLECT( )                    afx_msg HBRUSH CtlColor ( CDC* pDC, UINT nCtlColor ); 
ON_WM_DRAWITEM_REFLECT( )                    afx_msg void DrawItem ( LPDRAWITEMSTRUCT lpDrawItemStruct ); 
ON_WM_MEASUREITEM_REFLECT( )                 afx_msg void MeasureItem ( LPMEASUREITEMSTRUCT lpMeasureItemStruct ); 
ON_WM_DELETEITEM_REFLECT( )                  afx_msg void DeleteItem ( LPDELETEITEMSTRUCT lpDeleteItemStruct ); 
ON_WM_COMPAREITEM_REFLECT( )                 afx_msg int CompareItem ( LPCOMPAREITEMSTRUCT lpCompareItemStruct ); 
ON_WM_CHARTOITEM_REFLECT( )                  afx_msg int CharToItem ( UINT nKey, UINT nIndex ); 
ON_WM_VKEYTOITEM_REFLECT( )                  afx_msg int VKeyToItem ( UINT nKey, UINT nIndex ); 
ON_WM_HSCROLL_REFLECT( )                     afx_msg void HScroll ( UINT nSBCode, UINT nPos ); 
ON_WM_VSCROLL_REFLECT( )                     afx_msg void VScroll ( UINT nSBCode, UINT nPos ); 
ON_WM_PARENTNOTIFY_REFLECT( )                afx_msg void ParentNotify ( UINT message, LPARAM lParam ); 

ON_NOTIFY_REFLECT 와 ON_CONTROL_REFLECT 매크로들은 주어진 메세지를 다루는 
하나의 Object 보다 더 많은 것을 할수 있는 다양함을 가집니다.

Map entry                                       Function prototype 
ON_NOTIFY_REFLECT_EX( wNotifyCode, memberFxn )  afx_msg BOOL memberFxn ( NMHDR * pNotifyStruct, LRESULT* result ); 
ON_CONTROL_REFLECT_EX( wNotifyCode, memberFxn ) afx_msg BOOL memberFxn ( ); 

- Handling Reflected Messages: An Example of a Reusable control
이 예제는 CYellowEdit 라 불리는 재사용을 위한 컨트롤을 만든다.
컨트롤은 단지 노란색 바탕에 검정 글씨라는 점만 제외하고 보통의 에디터 컨트롤과 
하는 것은 같습니다.
CYellowEidt 가 다른 색깔로 나타나게 하므로 멤버함수를 추가하는 것이 쉽게 됩니다.

이 샘플을 실험하기 위해서 다음 단계를 하십시요.
1. 기존의 애플리케이션에 새로운 다이얼로그 박스를 생성한다. 
   당신은 틀림없이 reusable 컨트롤 개발을 위한 애플리케이션을 가져야 한다. 만일 없다면 
   AppWizard 로 생성한다.

2. VC++에 로드된 프로젝트가 있다면 CEdit를 베이스로한 CYellowEdit라 불리우는 새로운 
클래스를 ClassWizard 로 생성한다. 리고 Add to Component Gallery 는 체크된 상태로 나둔다.

3. CYellowEdit 에 3개의 멤버 변수를 추가한다. 
첫번째와 두번째는 테스트와 백그라운드 색깔을 저장하기 위한 것이고 세번째는 백그라운드
페인팅을 위한 브러쉬 저장 공간이다. 
CBrush object 는 한번 생볕홱? 단지 후에 그것을 참조 하고 그리고 컨트롤의 파괴될때 
자동으로 없어진다.

4. 생성자에서 다음 멤버값을 초기화 한다.
CYellowEdit::CYellowEdit()
{
 m_clrText = RGB( 0, 0, 0 );
 m_clrBkgnd = RGB( 255, 255, 0 );
 m_brBkgnd.CreateSolidBrush( m_clrBkgnd );
}

5. ClassWizard 를 사용해서 CYellowEdit Class 에 WM_CTLCOLOR 리플렉트 메세지를 추가한다. 
 ClassWizard 다음과 같은 메세지 맵과 구조를 추가해 줄 것이다.

 ON_WM_CTLCOLOR_REFLECT()
 // Note: other code will be in between....
 HBRUSH CYellowEdit::CtlColor(CDC* pDC, UINT nCtlColor) 
 {
 // TODO: Change any attributes of the DC here
 // TODO: Return a non-NULL brush if the
 //    parent's handler should not be called
 return NULL;
 }


6. 다음 코드로 함수를 수정해라. 다음 코드는 텍스트 색깔과 텍스트 백그라운드 색깔, 그리고 
컨트롤 리셋을 위한  백그라운드 색깔을 지정한다.

pDC->SetTextColor( m_clrText );    // text
pDC->SetBkColor( m_clrBkgnd );    // text bkgnd
return m_brBkgnd;                // ctl bkgnd


7. 다이얼로그 박스에 에디터 컨트롤을 만들어라. 그리고 나서 컨트롤 키를 누른 상태에서 
에디터 컨트롤을 더블 클릭하므로써 멤버 변수를 추가해라. 다이얼로그 박스의 멤버 추가 
박스에서 멤버변수를 끝내고 category 를 Control를
선택하고 Vaiable Type을 "CYellowEidt"로 선택하라. 
잊지말것은 다이얼로그 박스에서 tab 순서를 선택하는 것이다. 
또한, 다이얼로그 박스의 헤더 화일에 CYellowEdit 컨트롤을 위한 헤더화일을 포함해라.

8. 애플리케이션을 Build 하고 실행하라 . 에디터 박스는 노란색 바탕을 가질 것이다.

9. 당신은 다른 프로젝트들에서 CYellowEdit Control CLass 을 추가하기위해서 
Component Gallery 을 지금 사용해라.
Posted by 촌돌애비
|