[MFC] 디자인 예쁜 다이얼로그, 탭/리스트 컨트롤

Posted by 빵빵빵
2016/08/23 15:11 전산(컴퓨터)/PC-Windows



출처 및 다운로드 : http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=278&MAEULNo=20&no=25575&ref=25575


1. SkinDialog.zip : 다이얼로그에 스킨을 적용한 프로젝트
설명 : http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=51&MAEULNo=20&no=8092&ref=8092

2. TabCtrlEx.zip : 기본 탭 컨트롤을 커스터마이징한 프로젝트
설명 : http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=51&MAEULNo=20&no=8095&ref=8095

3. SkinListCtrl.zip : CXListCtrl에 스킨 및 여러가지 기능이 추가된 프로젝트
설명 : http://www.devpia.com/Maeul/Contents/Detail.aspx?BoardID=51&MAEULNO=20&no=8096&page=24


==================================================================================================

갈무리 : 첨부된 이미지가 잘 보이면 원본 링크도 살아 있는 겁니다.

1. SkinDialog.zip : 다이얼로그에 스킨을 적용한 프로젝트
설명 : http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=51&MAEULNo=20&no=8092&ref=8092

리사이징이 되어야하는 프레임 윈도우나 다이얼로그 윈도우를 위한 스킨 기능입니다.

프로젝트를 진행하면서 구현했던 기능들을 정리하면서 간단한 예제 소스도 함께 만들어보았습니다.

초기에는 프레임 윈도우에만 구현하였다가 지금은 다이얼로그 윈도우에도 동일하게 적용했습니다.

 

프레임 윈도우에 적용하고 싶으신 분은 이 소스를 참조해서 동일하게 구현하셔도 되겠습니다.

 

개발 환경

- Window XP : Intel X86 32bit

- Visual Studio 2005 : MFC 8.0 Unmanaged C++

 

캡션과 보더 부분에 들어가는 비트맵은 총 8개 입니다.

- 캡션 왼쪽

- 캡션 가운데(width:1pixel)

- 캡션 오른쪽

- 보더 왼쪽(height:1pixel)

- 보더 왼쪽아래

- 보더 아래(width:1pixel)

- 보더 오른쪽(height:1pixel)

- 보더 오른쪽아래

 

시스템 박스에 들어가는 비트맵은 총 12개입니다.

- 최소화 박스 : out, over, on

- 최대화 박스 : out, over, on

- 종료 박스 : out, over, on

- 복귀 박스 : out, over, on

 

캡션에 들어가는 아이콘은 총 2개 입니다.

- active, inactive

 

모든 이미지, 아이콘 파일은 현재 파일로 노출되어 있고 실행시 로드해서 각 핸들을 글로벌 객체의 멤버로 가지고

있게 됩니다. 추후에는 XML로 인덱스를 관리한다거나 이미지 DB파일로 확장해서 사용해도 좋을 것 같습니다.

 

스킨 버튼은 공통 배경이 되는 Base 비트맵과 기능별 아이콘으로 구성됩니다. 스킨 버튼 설명은 다음 강좌에서 다른 스킨 컨트롤들과 함께 설명하겠습니다.(CListCtrl, CTabCtrl, CToolBar, CMenuBar,...)

 

참고로 스킨 리사이징이 완전한 다각형 윈도우까지 되는건 아닙니다. 현재는 라운드 형태의 스킨 윈도우만 가능합니다. 그 부분은 추후 기능을 보완하도록 하겠습니다.

 

다음은 스킨 관련 클래스입니다.

CGlobal : 비트맵, 아이콘, RGB, FONT 들의 정보를 저장하기 위한 글로벌 클래스입니다.

CSkinDialog : 실제 상속 받아서 사용하게 될 스킨(캡션/프레임) 다이얼로그입니다.

CMemDC : 각종 컨트롤에서 사용하는 더블 버퍼링 기능을 위한 DC 클래스 입니다.

CSkinButton : 버튼 스킨 클래스입니다.

 

다음은 CGlobal 클래스의 주요 함수별 기능 설명입니다.

1. void LoadBitmaps()

 : Bitmap 파일을 로드하여 핸들을 배열로 저장, 최초 호출 함

 

2. HBITMAP GetBitmap(UINT nBitmapID)

 : 키 인덱스로 해당 비트맵의 핸들을 얻음

2. SIZE GetBitmapSize(UINT nBitmapID)

 : 키 인덱스로 해당 비트맵의 사이즈를 얻음

 

3. void LoadIcons()

 : Icon 파일을 로드하여 핸들을 배열로 저장, 최초 호출함

 

4. HICON GetIcon(UINT nIconID, UINT nType)

 : 키 인덱스로 해당 아이콘의 핸들을 얻음

 

5. void SetRGB(UINT nRGBID, COLORREF cf)

: 키 인덱스에 해당하는 RGB를 설정함, 최초 설정함

 

6. COLORREF GetRGB(UINT nRGBID)

: 키 인덱스에 해당하는 RGB를 얻음

 

7. CFont * SetFont(UINT nFontID, CString sFaceName, UINT nFontSize, BOOL bBold=FALSE)

: 키 인덱스에 해당하는 폰트를 생성하여 배열로 저장함

 

8. CFont * GetFont(UINT nFontID)

: 키 인덱스에 해당하는 폰트를 얻음

 

다음은 CSkinDialog 클래스의 주요 함수별 기능 설명입니다.

1. void DrawFrame()

: 실제 GDI를 이용하여 캡션, 보더를 그림

 

2. virtual BOOL PreTranslateMessage(MSG* pMsg)

: 마우스 이벤트 발생 시 시스템 박스의 이미지 변경과 기능 처리를 위한 함수

 

3. int OnCreate(LPCREATESTRUCT lpCreateStruct)

: 비트맵, 아이콘 핸들 초기 설정

 

 4. void OnDestroy()

: GDI 오브젝트 소멸

 

5. void OnSize(UINT nType, int cx, int cy)

: 라운드 형태로 윈도우 영역 설정

 

6. void OnNcPaint()

: None Client 영역을 위한 Paint 함수로 DrawFrame 함수를 호출함

 

7. void OnPaint()

: 클라이언트 영역의 배경을 칠할 함수(OnEraseBackground에서 해도 되지만 특정 때 호출되지 않는 문제가 발생)

 

8. BOOL OnNcActivate(BOOL bActive)

: 윈도우가 액티브/인액티브 될 때 DrawFrame 함수를 호출함

 

9. void OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp)

: 스킨 캡션, 보더의 사이즈에 따른 클라이언트 영역을 재조정함

 

11. void OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)

: 윈도우 리사이징 시 최소/최대값을 설정함

 

12. LRESULT OnEnterSizeMove(WPARAM wParam, LPARAM lParam)

: 윈도우 이동 시 투명 처리함


 13. LRESULT OnExitSizeMove(WPARAM wParam, LPARAM lParam)

: 윈도우 이동 종료시 불투명 처리함

 

강좌라고 하기에는 내용이 너무 부실한거 같아 죄송합니다. 간단히 샘플 소스 공개 입니다. ㅎ ^^;;

다음에는 여러 컨트롤의 스킨 예제 소스도 올려 보겠습니다. 

기타 의문점이나 개선사항 있으면 리플 부탁드립니다. 많은 도움이 되겠습니다.

 

Update history 

-------------------------------------------------------------------------------------------

- 2008.04.11 - 16:09 : SetCapture/ReleaseCapture를 사용하여 시스템 박스 기능을 새로 구현하였습니다.

- 2008.04.11 - 18:00 : 최대화 상태에서 윈도우가 이동되는 문제점이 수정되었습니다. Restore 이미지가 변경되었습니다. 더블클릭 메시지처리가 추가되었습니다.

- 2008.04.12 - 05:35 : 최대화 관련 윈도우 표시 문제점 수정


2. TabCtrlEx.zip : 기본 탭 컨트롤을 커스터마이징한 프로젝트
설명 : http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=51&MAEULNo=20&no=8095&ref=8095


 

화면에서 보는 것과 같이 자유롭게 커스터마이징 가능하도록 Tab Control을 상속/구현하였습니다.

아직 탭/보더가 스킨 이미지로 구현된 컨트롤은 아닙니다.

현재는 GDI/GDI+ 를 이용한 직접 그리는 형태가 되겠습니다.

 

개발 환경

- Window XP : Intel X86 32bit

- Visual Studio 2005 : MFC 8.0 Unmanaged C++

 

현재 구현된 탭 스타일은 아래와 같습니다.

1. 버튼 스타일

- 가로/세로 스타일이 가능함

- 단, 세로 스타일일때는 멀티라인이어야함(이건 기본 컨트롤도 마찬가지임)

- GDI+ 이용한 그라데이션 효과를 줌

- 탭 위치 스타일이 현재 기본(Top)만 가능함. Bottom은 현재 미구현

 

2. 일반 스타일

- 가로/세로 스타일이 가능함

- 단, 세로 스타일일때는 멀티라인이어야함(이건 기본 컨트롤도 마찬가지임)

- 3D 스타일과 플랫 스타일로 변경하는 옵션

- 보더의 색을 지정하는 옵션

- 탭 위치 스타일이 현재 기본(Top)만 가능함. Bottom은 현재 미구현

 

다음은 CTabCtrlEx 클래스의 주요함수 설명입니다.

 

1. void SetColor(COLORREF crText, COLORREF crBorder, COLORREF crBackIn, COLORREF crBackOut, BOOL bRedraw=TRUE)

 : 텍스트, 배경안쪽, 배경바깥쪽, 보더 색 설정

 

2. void Set3dBorder(BOOL b3dBorder)

 : 보더 스타일 설정(TRUE=3D, FALSE=플랫)

 

3. void SetItemImage(int nItem, int nImage)

 : 해당 탭 아이템의 이미지 설정

 

4. virtual void TextOutVertical(CDC* pDC, CRect rect, CStringW sText)

 : 세로 스타일일 경우 텍스트를 한문자씩 세로로 출력

 

5. virtual void DrawMainBorder(LPDRAWITEMSTRUCT lpDrawItemStruct)

 : 메인 보더를 그림

    

6. virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)

 : 해당 탭 아이템의 텍스트를 출력

 

7. virtual void DrawItemBorder(LPDRAWITEMSTRUCT lpDrawItemStruct)

 : 해당 탭 아이템의 보더를 그림






3. SkinListCtrl.zip : CXListCtrl에 스킨 및 여러가지 기능이 추가된 프로젝트
설명 : http://www.devpia.com/Maeul/Contents/Detail.aspx?BoardID=51&MAEULNO=20&no=8096&page=24

 

 

1. Edit Control 이용한 아이템 수정 기능

 

 

2. Edit/Spin Control을 이용한 아이템 수정 기능

 

 

3. Combo Box Control을 이용한 아이템 수정 기능

 

 

4. Date Control을 이용한 아이템 수정기능

 

 

위 화면에서 보는 것과 같이 기존 CXListCtrl에 여러가지 기능을 수정하고 추가하였습니다.

 

개발 환경

- Window XP : Intel X86 32bit

- Visual Studio 2005 : MFC 8.0 Unmanaged C++

 

에디터, 콤보, 날짜 컨트롤은 소스를 보시면 알겠지만 컨트롤에 대한 오브젝트가 클래스의 Static 멤버변수로 되어있습니다.

그리고 기존 CXListCtrl에서는 GDI를 사용하여 컨트롤을 직접 그리는 형태였습니다.

기능 확장성을 위해 지금처럼 컨트롤을 올리는 형태로 쓰는게 좋을 것 같습니다.

(아직 CheckBox와 Progress는 GDI로 직접 그리는 형태입니다.)

 

그리고 헤더컨트롤에 스킨 이미지를 적용했습니다. 현재는 헤더의 shadow 이미지가 한개지만 추후에는 마우스 over/out/click에 따라 이미지를 세분화하는게 좋겠습니다.

 

다음은 간단하게 XListCtrl에 추가한 기능들을 나열해 보았습니다.

 

1. 헤더 컨트롤

 - 헤더 높이 설정, 헤더 컬러 설정, 헤더 폰트 설정, 헤더 스킨 적용

 

2. 리스트 컨트롤

 - 마우스 이동시 라인별 Animate 효과

 - Underline 표시, Underline 컬러 설정

 - 아이템 높이 설정, 폰트 설정

 - Combobox, Editbox, Datebox Contorl 추가 및 기능 구현

 - 소트 함수 추가

 - 탭키로 다음 아이템 활성화 기능(Combox, Editbox, Datebox)

 

CXListCtrl에서 사용하는 클래스 목록은 다음과 같습니다.

 - CXHearderCtrl

 - CXComboBox

 - CXEditBox

 - CXSpinBox

 - CXDateBox

 

다음은 CXListCtrl의 외부에서 호출가능한 주요 설정 함수입니다.

 

1. void EnableFocusRect(BOOL bFocusRect=TRUE)
 - 아이템 선택시 포커스 컬러를 적용 유무 선택

 

2. void EnableResize(BOOL bResize=TRUE)
 - 컬럼 리사이징 가능 유무 선택

 

3. void SetNoItemMsg(CString strNoItemMsg)
 - 아이템이 없을 때 표시할 스트링 지정

 

3. void SetStatusColumn(int nSubItem)
 - 상태 컬럼을 지정 : 상태컬럼의 Rect은 다르게 조정되어 표시됨

 

4. void SetBgColor(COLORREF crBg)
 - 리스트 배경색 지정

 

5. void SetBgColorProgress(COLORREF crBg)
 - Progress 배경색 지정

 

6. void SetUnderLine(BOOL bUnderLine=TRUE)
 - 언더라인 유무 지정

 

7. void SetColorUnderLine(COLORREF crUnderLine)
 - 언더라인 컬러 지정

 

7. BOOL GetProgressColor(int nItem, int nSubItem, COLORREF &cf)
 - Progress 컬러 획득

 

8. void GetDrawColors(int nItem, int nSubItem, COLORREF& colorText, COLORREF& colorBkgnd)
 - 해당 서브아이템의 텍스트, 배경 컬러 획득

 

9. BOOL DeleteAllItems()
 - 모든 아이템 삭제

 

10. BOOL DeleteItem(int nItem);
 - 해당 아이템 삭제

 

11. int GetCheckbox(int nItem, int nSubItem)
 - 해당 서브아이템의 체크 박스 상태 획득

 

12. int GetColumns()
 - 컬럼수 획득

 

13. int GetCurSel()
 - 현재 선택된 첫번째 아이템 획득

 

14. DWORD GetItemData(int nItem)
 - 해당 아이템의 Data 획득

 

15. BOOL GetSubItemRect(int iItem, int iSubItem, int nArea, CRect& rect)
 - 해당 서브아이템의 Rect 획득

 

16. int InsertItem(int nItem, LPCTSTR lpszItem)
 - 아이템 추가: 아이템 텍스트 설정


17. int InsertItem(int nItem, LPCTSTR lpszItem, COLORREF crText, COLORREF crBackground)
 - 아이템 추가: 아이템 텍스트, 텍스트 컬러, 배경 컬러 설정

 

18. int InsertItem(const LVITEM* pItem);
 - 아이템 추가: LVIITEM 구조체 이용

 

19. BOOL SetComboBox(int nItem, int nSubItem, BOOL bEnableCombo, CStringArray *psa=NULL)
 - 해당 서브아이템 콤보박스 유무 설정 : 콤보박스 유무, String Array 설정

 

20. BOOL SetEditBox(...)
 - 해당 서브아이템 에디트박스 유무 설정 : 에디트박스 유무, 숫자입력일 경우 최소/최대값 지정, 문자열 최대길이 지정, 에디트 Style지정

 

21. BOOL SetDateBox(...); 
 - 해당 서브아이템 Date박스 유무 설정 : Date박스 유무, Date Format, Date박스 Style 지정

 

22. BOOL SetProgress(int nItem, int nSubItem, BOOL bShowProgressText = TRUE, LPCTSTR lpszProgressText = NULL)
 - 해당 서브아이템을 Progress로 설정

 

23. BOOL SetCheckbox(int nItem, int nSubItem, int nCheckedState)
 - 해당 서브아이템 체크박스 상태 설정

 

24. BOOL SetItemData(int nItem, DWORD dwData)
 - 해당 아이템 Data 설정

 

25. BOOL SetItemImage(int nItem, int nSubItem, int nImage, BOOL bImageCenter=FALSE)
 - 해당 서브아이템 이미지 설정 : 이미지 인덱스, 이미지 센터 유무 설정

 

26. int GetItemImage(int nItem, int nSubItem)
 - 해당 서브아이템의 이미지 인덱스 획득

 

27. BOOL SetItemText(int nItem, int nSubItem, LPCTSTR lpszText)
 - 해당 서브아이템 텍스트 설정

 

28. BOOL SetItemText(int nItem, int nSubItem, LPCTSTR lpszText, COLORREF crText, COLORREF crBackground)
 - 해당 서브아이템 텍스트, 텍스트 컬러, 텍스트 배경 컬러 설정

 

29. BOOL SetItemTextColor(int nItem, int nSubItem, COLORREF crText, COLORREF crBackground)
 - 해당 서브아이템 텍스트 컬러, 텍스트 배경 컬러 설정

 

30. void UpdateDate(int nItem, int nSubItem, CTime time, COLORREF crText, COLORREF crBackground)
 - 해당 Date박스 업데이트 : 시간, 텍스트 컬러, 배경 컬러 설정

 

31. void UpdateProgress(int nItem, int nSubItem, int nPercent, COLORREF crText, COLORREF crBar, CString ProgressText=_T(""))
 - 해당 Progress 업데이트 : 퍼센트 값, 텍스트 컬러, Progress Bar 컬러 설정

 

32. virtual void Sort(int nSubItem, BOOL bSort)
 - 해당 서브아이템을 기준으로 정렬: bSort = TRUE:내림차순, FALSE:오름차순

 

33. void SetRowHeight(int nRowHeight)
 - 아이템 높이 설정

 

34. void SetTextFont(CFont *pTextFont)
 - 리스트 폰트 설정


다음은 CXListCtrl의 내부에서 호출되는 주요 함수 및 메시지입니다.

 

1. virtual BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
 - 컬럼 사이즈 조정 및 제어

 

2. void DrawProgress(...)
 - 해당 서브아이템의 Progress 그리기 : OnCustomDraw에서 호출됨
 - OnCustomDraw는 현재 OnPaint 함수속에 Default()를 통해서 호출됨, OnPaint 메시지를 핸들링 한다면 꼭 Default()함수를 호출 해야 함

 

3. void DrawCheckbox(...)
 - 해당 서브아이템의 체크 박스를 그림 : OnCustomDraw에서 호출됨
 - OnCustomDraw는 현재 OnPaint 함수속에 Default()를 통해서 호출됨, OnPaint 메시지를 핸들링 한다면 꼭 Default()함수를 호출 해야 함

 

4. void DrawText(...)
 - 해당 서브아이템의 텍스트 그리기 : OnCustomDraw에서 호출됨
 - OnCustomDraw는 현재 OnPaint 함수속에 Default()를 통해서 호출됨, OnPaint 메시지를 핸들링 한다면 꼭 Default()함수를 호출 해야 함

 

5. int DrawImage(int nItem, int nSubItem, CDC* pDC, COLORREF crText, COLORREF crBkgnd, CRect rect, XLISTCTRLDATA *pXLCD)
 - 해당 서브아이템의 이미지 그리기 : OnCustomDraw에서 호출됨
 - OnCustomDraw는 현재 OnPaint 함수속에 Default()를 통해서 호출됨, OnPaint 메시지를 핸들링 한다면 꼭 Default()함수를 호출 해야 함

 

6. void ShowComboBox(int nItem, int nSubItem)
 - 해당 서브아이템의 콤보박스 생성 및 표시 : OnLButtonDown에서 호출됨

 

7. void ShowEditBox(int nItem, int nSubItem)
 - 해당 서브아이템의 에디트박스 생성 및 표시 : OnLButtonDown에서 호출됨

 

8. void ShowDateBox(int nItem, int nSubItem)
 - 해당 서브아이템의 Date박스 생성 및 표시 : OnLButtonDown에서 호출됨
 
9. afx_msg void OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
 - 실제 아이템을 그려주는 함수 : 생성/표시되는 컨트롤들은 텍스트만 표시

 - 커스터마이징을 위한 메시지 함수

 

10. afx_msg void OnLButtonDown(UINT nFlags, CPoint point)
 - 실제 컨트롤을 생성/표시하는 함수

 

11. afx_msg void OnPaint()
 - 아이템이 없을 때 메시지를 표시

 - OnCustomDraw 메시지 핸들링을 위해 Default함수를 호출함
 - OnPaint 메시지를 추가하지 않았다면 OnCustonDraw 메시지는 내부적으로 호출됨

 

12. afx_msg BOOL OnEraseBkgnd(CDC* pDC)
 - 배경을 그림

 

13. afx_msg LRESULT OnMouseLeave(WPARAM wParam, LPARAM lParam)
 - 마우스 이동시 아이템 별 Animate 효과 처리를 위해 추가된 메시지

 

14. virtual BOOL PreTranslateMessage(MSG* pMsg);
 - 마우스 이동시 아이템 별 Animate 효과 처리를 위해 추가된 함수

 

15. afx_msg LRESULT OnEditChange( WPARAM wParam, LPARAM lParam )
 - 에디트 박스 종료 시 호출 됨 : 에디트 박스는 KillFocus될 때 종료됨
 - 변경된 값을 해당 서브아이템에 저장

 

16. afx_msg LRESULT OnDateChange( WPARAM wParam, LPARAM lParam )
 - Date 박스 종료 시 호출 됨 : Date 박스는 KillFocus될 때 종료됨
 - 변경된 값을 해당 서브아이템에 저장

 

17. afx_msg LRESULT OnComboChange( WPARAM wParam, LPARAM lParam )
 - Combo 박스 종료 시 호출 됨 : Combo 박스는 KillFocus될 때 종료됨
 - 변경된 값을 해당 서브아이템에 저장

 

18. void SetLButtonDown(int nStartItem, int nStartSubItem)
 - 특정 컨트롤에서 Tab키 이벤트에 의해 호출되는 함수로 다음 컨트롤로 활성화 시킴
 
19. static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
 - MFC 내부에서 호출되는 Sort를 위한 콜백 함수

 

 

 

MFC 클래스를 커스터마이징 하거나 분석하기 위해서는 C++의 가상함수와 각종 MFC 매크로에 대한 이해는

필수 입니다. MFC 프레임웍의 기본이 가상함수에 의한 설계이고 실제 그 설계자 본인들을 위해 CRuntimeClass 클래스 부터 각정 매크로를 선언, 정의하여 사용하도록 설계되었습니다.

 

우리가 이 클래스들을 커스터마이징하기 위해선 그 구조를 잘 이해하는 수 밖에 없습니다. 

물론 MFC 떠나 다른 프레임웍을 분석한다고 해도 똑같겠죠.

 

가상테이블과 런타임클래스의 무거움을 버리고 WTL/STL로만 개발할 수 있다면 좋겠습니다.;;

2016/08/23 15:11 2016/08/23 15:11

[MFC] 사용자 정의 메시지 처리기 사용하기

Posted by 빵빵빵
2016/07/29 17:31 전산(컴퓨터)/PC-Windows



펌... 윈도우 메시지 처리에 대해 아주 정확하고 자세한 설명이 되어 있어서.... 퍼놓습니다. 절친 설명, 찰진 설명


출처 : http://six605.tistory.com/222


사용자가 정의한 메시지를 수신할 수 있는 윈도우는 크게 2가지가 있다.

1.     CWnd 의 파생 클래스

2.     CWinThread의 파생 클래스 : 사용자 인터페이스 클래스(User Interface Class)

 

 

 

 

, 윈도우를 출력하는 클래스가 사용자 정의 메시지를 받을 수 있다.

MFC의 계층 구조도 모르고 Document 에서 사용자 정의 메시지 처리기를 만들면 아마 다음과

같은 에러 메시지를 볼 수 있을 것이다.

 

LRESULT (__thiscall CWnd::* )(WPARAM,LPARAM)'()로 변환할 수 없습니다

 

인터넷을 뒤지면 아마 사용자 정의 메시지 처리기의 리턴값을 LRESULT 로 바꾸면 해결 된다고

하는 글이 가장 많은데  아마 그렇게 변경해 주어도 안되면 CWnd CWinThread의 파생 클래스

가 아닌 곳에서 구현하려 해서 에러가 나는 경우일 것이다.

 

CMainFrame CView의 파생 클래스 에서 사용자 정의 메시지 처리기를 만들어 주자 

 

위에서 언급한 사용자 정의 메시지 처리기 함수 리턴값에 대한 에러는

VC6.0 에서 .Net 이상의 컴파일러 사용시 나타나는 문제이다.

.Net 이상부터 MFC 에서는 메시지 처리기의 반환 형식 및 매개변수 형식을 보다 엄격하게

검사한다. 이로써 안전하지 않은 메시지 처리기를 오류 메시지로 플래그를 지정하여 발생할 수

있는 문제를 개발자에게 알린다.

MFC 에서 ON_MESSAGE, ON_REGISTERED_MESSAGE, ON_THREAD_MESSAGE,

ON_REGISTERED_THREAD_MESSAGE 에 대해 정적 캐시팅을 사용한다.

 

VC6.0 에서 ON_MESSAGE 또는 ON_REGISTERED_MESSAGE 에 대해 메시지 처리기 함수가

void 값을 리턴하였을 경우 .Net 이상에서는 컴파일 에러를 일으킨다.

, 메시지 처리기는 LRESULT 를 리턴해야 한다.

 

 

 

#  CWnd 파생 클래스 에서 사용자 정의 메시지 처리기 사용

 

1. 사용자 정의 메시지 정의

    const UINT UM_READ_RESULT = WM_USER + 1; 

 

2. 메시지 맵에 메시지 함수 Overriding

   - 구현파일 (*.cpp)

 

BEGIN_MESSAGE_MAP(CReadProgressThread, CWinThread)

    ON_MESSAGE(UM_PROGRESS_SET, OnSetProgress)

END_MESSAGE_MAP()

LRESULT CProgressThread::OnSetProgress(WPARAM _wParam, LPARAM _lParam)

{

       ...

}

 

      

    UM_PROGRESS_SET 사용자 정의 메시지를  OnSetProgress 함수와 연결

    수신 프로그램에 메시지가  도착 하므로 해당하는 메시지 핸들러가 수행 되도록

    메시지 핸들러를 오버라이딩

      

     ※ Message Map

        - 메시지와 메시지 핸들러(함수) 를 연결하는 역할

 

3. 사용자 정의 메시지 함수 작성

   - 헤더파일 (*.h)        

protected:

    afx_msg LRESULT OnSetProgress(WPARAM _wParam, LPARAM _lParam);

    DECLARE_MESSAGE_MAP()

 

 

 

# CWinThread 파생 클래스 에서 사용자 정의 메시지 처리기 사용

 

- 사용자 인터페이스 스레드 즉, CWinThread의 파생클래스가 사용자 정의 메시지를 수신 하는

    경우는 Message Map을 연결하는 부분과 메시지 핸들러 원형만 다르다.

 

1. 사용자 정의 메시지 정의

    const UINT UM_READ_RESULT = WM_USER + 1; 

 

2. 메시지 맵에 메시지 함수 Overriding

   - 구현파일 (*.cpp)

 

BEGIN_MESSAGE_MAP(CReadProgressThread, CWinThread)

    ON_THREAD_MESSAGE(UM_PROGRESS_SET, OnSetProgress)

END_MESSAGE_MAP()

 

void CProgressThread::OnSetProgress(WPARAM _wParam, LPARAM _lParam)

{

       ...

}   

 

 

3. 사용자 정의 메시지 함수 작성

   - 헤더파일 (*.h)         

protected:

    afx_msg void OnSetProgress(WPARAM _wParam, LPARAM _lParam);

    DECLARE_MESSAGE_MAP()

 

2016/07/29 17:31 2016/07/29 17:31

[MFC] Notification Message(통지 메시지, WM_NOTIFY) 에 대해서

Posted by 빵빵빵
2016/07/29 17:28 전산(컴퓨터)/PC-Windows



펌... 윈도우 메시지 처리에 대해 아주 정확하고 자세한 설명이 되어 있어서.... 퍼놓습니다. 절친 설명, 찰진 설명

출처 : http://anster.tistory.com/11



Notification Message




1. Background Knowledge


윈도우즈 프로그래밍시 프로그래머가 사용할 수 있는 컨트롤들은 여러가지가 있다. Static Box 부터 시작해서 List Box, List View, Button, Edit Control, Address Control 등등. 하지만 이러한 컨트롤들이 모두 처음부터 윈도우에 존재했던것은 아니다.

< 기본 컨트롤 >

윈도우 95 이전에 존재했던 컨트롤들을 기본(표준) 컨트롤이라 부른다. 기본 컨트롤은 16비트 윈도우의 경우 USER.EXE 에, 32비트 에서는 윈도우 XP 이전까지 USER32.DLL 에 의해서 구현되었으나 XP 이후 부터는 공통 컨트롤과 함께 COMCTL32.DLL 에 의해서 구현된다.

- Static Text
- Button
- Edit Control
- List Box
- Combo Box

< 공통 컨트롤 >

윈도우 95 이후부터 생긴 컨트롤들을 공통 컨트롤이라 부른다. 기본 컨트롤에 비해서 더 화려한 외관과 강력한 기능들을 제공한다. 16비트 윈도우에서는 COMMCTRL.DLL 에 의해서 구현되었고, 32비트에서는 COMCTL32.DLL 에 의해서 구현된다.

- Progress Bar
- List View Control
- Spin Control
- Command Bar Control
- Address Control
- 이외의 기본컨트롤을 제외한 모든 컨트롤




2. Notification Messge 란 무엇인가?

Notification Message 를 한글로 번역하면 통지 메시지 다. 무엇을 통지 해준다는 것일까? 부모 윈도우에게 자신의 상태가 변하였음을 통지해주는 것이다.

예를들어 다이얼로그가 있고, 그 안에 버튼이 존재한다고 하자. 사용자가 버튼을 눌렀다면 버튼의 눌려진 모양을 다시 그리도록 부모 윈도우에게 알려줘야 한다. 이때 자식 윈도우(버튼) 으로부터 부모 윈도우(다이얼로그) 에게 보내지는 메시지를 Notification Message 라 부른다.

우리가 프레임윈도우에 있는 메뉴를 선택했을때 발생하는 메시지인 WM_COMMAND 도 대표적인 Notification Message 다. 이 메시지가 발생했을때 전달되는 파라미터인 WPARAM 과 LPARAM 의 내용은 아래와 같다.

MESSAGE ID  WPARAM(32 Bit)
 LPARAM(32 Bit)
 WM_COMMAND BN_CLICKED (High) Control ID(Low) Control HWND


WPARAM 의 상위 16비트에는 BN_CLICKED 와 같은 Notification Code 가, 하위 16비트에는 어떤 컨트롤에서 발생했는지 IDC_BUTTON1 과 같은 컨트롤의 ID 값이 들어간다.

LPARAM 의 32비트 값은 메시지가 발생한 컨트롤의 핸들이다. 이를테면 버튼이 눌렸을때 눌려진 버튼의 HWND 값이다.




2. 왜 WM_NOTIFY 가 생겨나게 되었는가?

메시지를 받고 보낼때 사용하는 파라미터는 WPARAM, LPARAM 두가지이므로 위의 WM_COMMAND 통지 메시지에서는 더이상의 추가적인 정보를 부모 윈도우에게 보낼 수 없다. 예를들면, 버튼이 눌려졌을때 커서의 위치가 어느지점이었는지 Point 구조체로 만들어 보내고 싶다고 해도 파라미터를 모두 사용했기에 보낼 수 없다.

이러한 이유에서 윈도우 3.x 에서는 추가적인 통지메시지들을 만들었다. 그 대표적인 예가 아래의 메시지들이다.

- WM_CTLCOLOR
- WM_VSCROLL
- WM_HSCROLL
- WM_DRAWITEM
- WM_MEASUREITEM
- WM_COMPAREITEM
- WM_DELETEITEM
- WM_CHARTOITEM
- WM_VKEYTOITEM

윈도우 버전이 올라감에 따라 더 강력한 기능을 가진 공통 컨트롤(COMCTL32.DLL)들이 추가되었고 이제 전혀 다른종류의 추가적인 데이터를 포함하는 통지 메시지가 필요해 졌다. 그렇다면 또 다시 WM_*과 같은 또다른 통지 메시지 만들어야 하는가? 그렇다면 수많은 컨트롤들이 포함되었을때 통지 메시지는 얼마나 많아지겠는가?

이러한 문제를 해결하기 위해 Win32 API 디자이너는 WM_*과 같은 수 많은 메시지를 추가하는 방법 대신에 WM_NOTIFY 단 하나의 메시지만 추가하는 방법을 선택했다. WM_NOTIFY 는 파라미터를 어떻게 사용하길래 추가적인 데이터를 메시지와 같이 보낼 수 있을까? 아래 그림을 확인 해 보자.

MESSAGE ID  WPARAM(32 Bit)
 LPARAM(32 Bit)
 WM_NOTIFY Control ID NMHDR *


NMHDR 구조체를 살펴보자.

typedef struct tagNMHDR
{
    HWND     hwndFrom; // HWND of the Control
    UINT_PTR idFrom; // Control ID
    UINT     code; // Notification Code
} NMHDR;
typedef NMHDR FAR * LPNMHDR;
 

WM_COMMAND 메시지의 LPARAM 에서 사용헀던 HWND 와 WPARAM 에서 사용헀던 Notification Code, Control ID 값이 있다. LPARAM 인자가 NMHDR * 이므로 윈도우는 처음 3개의 인자만 NMHDR 구조체와 같게 하거나, 아니면 NMHDR 구조체를 포함하는 구조체를 만들어 추가적인 데이터를 보내곤 한다.

typedef struct tagPointNMHDR
{
    HWND     hwndFrom;
    UINT_PTR idFrom;
    UINT     code;    
    Point    point;
} PointNMHDR;

typedef struct tagPointNMHDR
{
    NMHDR    nmHdr;
    Point    point;
} PointNMHDR;


NMHDR 을 포함하는 구조체를 만들어 메시지 핸들러에서 인자로 사용함으로써 추가적인 데이터를 보낼 수 있다.




3. 어떻게 WM_NOTIFY 메시지를 사용하는가?

WM_NOTIFY 메시지는 메시지 맵에 ON_NOTIFY 혹은 ON_NOTIFY_RANGE 매크로를 등록함으로써 사용할 수 있다. ON_NOTIFY 매크로의 형식은 다음과 같다.

                                   ON_NOTIFY ( wNotifyCode, ControlID, memberFxn )



ON_NOTIFY 매크로에서 사용하는 WM_NOTIFY 메시지의 핸들러는 아래와 같다.


                   afx msg void memberFxn ( NMHDR * pNotifyStruct, LRESULT * result )



만약, 다수의 컨트롤로 부터 오는 동일한 코드의 WM_NOTIFY 메시지를 하나의 핸들러에서 다루고 싶다면 ON_NOTIFY_RANGE 매크로를 사용하면 된다.

             ON_NOTIFY_RANGE ( wNotifyCode, ControlIDFrom, ControlIDTo, memberFxn )


ON_NOTIFY_RAGNE 매크로는 Control ID From 부터 Control ID To 까지의 모든 윈도우로 부터 온 wNotifyCode 의 WM_NOTIFY 메시지를 memberFxn 핸들러에서 처리합니다. 이때 주의할점은 원하는 윈도우들의 컨트롤 ID가 순차적이어야 한다는 것.




4. 컨트롤 별 통지 메시지 처리법

컨트롤의 Notification Message 를 처리하는 방법은 컨트롤 마다 다르다. 기본 컨트롤의 경우 WM_COMMAND 와 같은 Notification Message 가 발생하고, 공통 컨트롤의 경우 WM_NOTIFY Notification Message 발생한다. 이러한 메시지를 처리하는 방법은 두가지가 있다.


< 가상 함수에서 Notification Message 처리 >

Notification Message 를 처리하는 가상함수는 어떠한 핸들러보다 먼저 호출된다.


    < 기본 컨트롤 >


기본 컨트롤의 경우 CWnd 의 OnCommand 함수에서 통지 메시지인 WM_COMMAND 메시지를 처리하면 된다. 하지만 모든 WM_COMMAND 메시지가 이곳으로 오기때문에 특정 컨트롤만을 위한 작업을 이곳에 코딩하는 것은 비 효율적이다.

virtual BOOL OnCommand(
    WPARAM wParam, // High 16 : Code, Low 16 : Control ID
    LPARAM lParam  // Control HWND
);


    < 공통 컨트롤 >

공통 컨트롤의 경우 CWnd 의 OnNotify 함수에서 통지 메시지인 WM_NOTIFY 를 처리한다.

virtual BOOL OnNotify( 
    WPARAM wParam, // Control ID
    LPARAM lParam, // NMHDR *
    LRESULT* pResult  
);



< 메시지 핸들러에서 Notification Message 처리 >


     < 기본 컨트롤 >

기본 컨트롤의 경우 ON_CONTROL 매크로를 사용하면 된다. 이때는 공통 컨트롤에서 WM_NOTIFY 를 위해 사용하는 NM_* 메시지를 사용하지 않는다. BN_CLICKED 나 LBN_SELCHANGE 와 같은 기본 컨트롤을 위한 Notification Code를 사용한다.


    < 공통 컨트롤 >

공통 컨트롤의 경우 위해서 언급했듯이 ON_NOTIFY 매크로를 사용하면 된다.




참고 문헌



블로그 및 웹페이지

1. http://ko.wikipedia.org/wiki/%EC%9C%88%EB%8F%84_API  (윈도우 API 위키백과)
2. http://www.tipssoft.com/bulletin/tb.php/FAQ/63 (팁스소프트 WM_NOTIFY 1)
3. http://www.tipssoft.com/bulletin/tb.php/FAQ/65 (팁스소프트 WM_NOTIFY 2)
4. http://msdn.microsoft.com/en-us/library/749htf6k%28v=VS.100%29.aspx (MSDN TN061 - WM_NOTIFY)
5. http://codemaker.egloos.com/1825628 (컨트롤의 종류에 따른 통지 메시지 처리법)
6. http://program.egloos.com/1555310 (윈도우 메시지)

2016/07/29 17:28 2016/07/29 17:28