전산쟁이의 카피질

뒤로 검색

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

2016/08/23 15:11

출처 및 다운로드 : 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로만 개발할 수 있다면 좋겠습니다.;;

이 페이지는 Textcube 1.10.0 : beta 1 로 구동됩니다 데스크탑 화면