1. 참고링크 [Bottom] [Top]
2. MFC 주요 클래스 참조 방법 [Bottom] [Top]
// 전역 함수 CWinApp * AFXAPI AfxGetApp(); CWnd * AFXAPI AfxGetMainWnd(); // CWinApp 클래스 멤버 함수 POSITION GetFirstDocTemplatePosition() const; CDocTemplate * GetNextDocTemplate( POSITION & pos ) const; // CFrameWnd 클래스 멤버 함수 virtual CFrameWnd * GetActiveFrame(); virtual CDocument * GetActiveDocument(); CView * GetActiveView() const; // CView 클래스 멤버 함수 CFrameWnd * GetParentFrame() const; CDocument * GetDocument() const; // CDocument 클래스 멤버 함수 virtual POSITION GetFirstViewPosition() const; virtual CView * GetNextView( POSITION & rPosition ) const; void UpdateAllViews( CView * pSender, LPARAM lHint = 0L, CObject * pHint = NULL ); // CDocTemplate 클래스 멤버 함수 virtual POSITION GetFirstDocPosition() const = 0; virtual CDocument * GetNextDoc( POSITION & rPos ) const = 0;
3. 메뉴 [Bottom] [Top]
3.1. Context Menu 처리 [Bottom] [Top]
마우스 오른쪽 버튼을 클릭했을 때 메뉴 출력을 하는 소스 코드.
WM_CONTEXTMENU 메세지에 대한 핸들러를 추가한다.
- CMenu 클래스 변수를 선언한다.
CMenu menu, *pMenu;
- CMenu 클래스 변수에 메뉴를 적재한다.
menu.LoadMenu( ... ); pMenu = menu.GetSubMenu( ... );
- 메뉴를 출력한다.
pMenu->TrackPopupMenu( ... );
4. 대화상자 [Bottom] [Top]
4.1. Enter 키 처리 [Bottom] [Top]
Enter 키 입력시 대화상자가 닫기도록 처리하는 소스 코드.
PreTranslateMessage() 함수(가상) 를 재정의 한다.
- Enter 키 처리 코드를 추가한다.
1 BOOL CExampleDlg::PreTranslateMessage( MSG * pMsg ) 2 { 3 if( ( pMsg->message == WM_KEYDOWN ) && ( pMsg->wParam == VK_RETURN ) ) 4 { 5 ... 6 } 7 8 return CDialog::PreTranslateMessage( pMsg ); 9 } 10
참고> Eating returns in a single line edit
4.2. 유용한 CWnd 멤버 함수 [Bottom] [Top]
유용하지만 간혹 기억나지 않아 찾느라 고생하는 일이 있어서 몇가지를 정리한다.
CWnd* GetDlgItem( int nID ) const; int GetDlgCtrlID( ) const; UINT GetDlgItemInt( int nID, BOOL* lpTrans = NULL, BOOL bSigned = TRUE ) const; void SetDlgItemInt( int nID, UINT nValue, BOOL bSigned = TRUE ); int GetDlgItemText( int nID, LPTSTR lpStr, int nMaxCount ) const; int GetDlgItemText( int nID, CString& rString ) const; void SetDlgItemText( int nID, LPCTSTR lpszString ); BOOL EnableWindow( BOOL bEnable = TRUE ); BOOL ShowWindow( int nCmdShow );
위의 멤버 함수에 해당하는 WIN32 API 는 아래와 같다.
HWND GetDlgItem( HWND hDlg, int nIDDlgItem ); int GetDlgCtrlID( HWND hwndCtl ); UINT GetDlgItemInt( HWND hDlg, int nIDDlgItem, BOOL *lpTranslated, BOOL bSigned ); BOOL SetDlgItemInt( HWND hDlg, int nIDDlgItem, UINT uValue, BOOL bSigned ); UINT GetDlgItemText( HWND hDlg, int nIDDlgItem, LPTSTR lpString, int nMaxCount ); BOOL SetDlgItemText( HWND hDlg, int nIDDlgItem, LPCTSTR lpString ); BOOL EnableWindow( HWND hWnd, BOOL bEnable ); BOOL ShowWindow( HWND hWnd, int nCmdShow );
4.3. DDX/DDV 함수 [Bottom] [Top]
매번 헷갈리는 DDX/DDV 함수의 사용법이다.
함수
동작
UpdateData( FALSE )
대화상자 객체의 멤버 변수 값을 대화상자의 컨트롤로 전송
UpdateData( TRUE )
대화상자의 컨트롤 데이타를 대화상자 객체의 멤버 변수로 저장
4.4. CDialogBar 를 CDialog 처럼 사용하기 [Bottom] [Top]
How to initialize child controls in a derived CDialogBar http://support.microsoft.com/kb/185672/en-us
CDialogBar 를 CDialog 처럼 사용하기 http://myhome.hanafos.com/~kukdas/doc/mfc/dialogbar.html
참고> Difference between the return value of the CDialogBar::HandleInitDialog function in Visual C++ 6.0 and that in Visual C++ .NET or that in Visual C++ 2005 http://support.microsoft.com/kb/839297/en-us
4.5. 대화상자 드래그 이동하기 [Bottom] [Top]
일반 창이나 대화상자에서 윈도우 타이틀(창의 제목 표시줄) 이 없을 경우, 또는 윈도우의 아무 곳이나 클릭하여 드래그할 때 윈도우를 이동시키고 싶다면 WM_NCHITTEST 메세지 핸들러를 추가하여 처리한다.
1 // WM_NCHITTEST 메세지 추가 2 UINT CExampleDlg::OnNcHitTest(CPoint point) 3 { 4 int nHit = CDialog::OnNcHitTest(point); 5 6 if( HTCLIENT == nHit ) 7 { 8 nHit = HTCAPTION; 9 } 10 11 return nHit; 12 } 13
4.6. 액셀러레이터 키 (바로 가기 키) 처리 [Bottom] [Top]
대화상자에서 액셀러레이터 키 처리를 추가하는 순서와 소스 코드.
- 액셀러레이터 키 테이블 변수 추가
// 대화상자 클래스에 변수 추가 HACCEL m_hAccelTable;
리소스 편집기에서 액셀러레이터 키 테이블 추가 (예: 리소스ID = IDR_ACCELERATOR )
- 액셀러레이터 키 처리 코드 추가
1 // STEP 1: 액셀러레이터 키 테이블 로딩 (가상 함수 재정의) 2 BOOL CExampleDlg::OnInitDialog() 3 { 4 CDialog::OnInitDialog(); 5 6 // 리소스ID IDR_ACCELERATOR 의 액셀러레이터 키 테이블 로딩 7 m_hAccelTable = ::LoadAccelerators( AfxGetInstanceHandle(), MAKEINTRESOURCE( IDR_ACCELERATOR ) ); 8 9 return TRUE; 10 } 11 12 // STEP 2: 액셀러레이터 키 처리 코드 추가 (가상 함수 재정의) 13 BOOL CExampleDlg::PreTranslateMessage( MSG * pMsg ) 14 { 15 if( WM_KEYDOWN == pMsg->message ) 16 { 17 if( NULL != m_hAccelTable ) 18 { 19 if( ::TranslateAccelerator( m_hWnd, m_hAccelTable, pMsg ) ) 20 { 21 return TRUE; 22 } 23 } 24 } 25 26 return CDialog::PreTranslateMessage(pMsg); 27 } 28
4.7. ToolBar 와 StatusBar 추가하기 [Bottom] [Top]
참고> DLGCBR32 Sample: Demonstrates Adding a Status Bar and Toolbar to Dialog Boxes
1 // File: MFC/AddControlBar.cpp 2 3 //------------------------------------------------------------------------------ 4 // STEP 1: ControlBar 객체 추가 5 6 CStatusBar m_statusBar; 7 CToolBar m_toolBar; 8 9 //------------------------------------------------------------------------------ 10 // STEP 2: StatusBar 에 표시될 Pane 정의 11 12 static UINT indicators[] = 13 { 14 ID_SEPARATOR, // 상태 줄 표시기 15 ID_INDICATOR_CAPS, 16 ID_INDICATOR_NUM, 17 ID_INDICATOR_SCRL, 18 }; 19 20 //------------------------------------------------------------------------------ 21 // STEP 3: ToolBar / StatusBar 생성 후 초기화 22 23 BOOL CMyDialog::OnInitDialog() 24 { 25 ... 26 27 // STEP 3-1: ToolBar 추가 28 if( !m_toolBar.CreateEx( this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE 29 | CBRS_ALIGN_TOP | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC ) 30 || !m_toolBar.LoadToolBar( IDR_TOOLBAR1 ) ) 31 { 32 TRACE0( "도구 모음을 만들지 못했습니다.\n" ); 33 EndDialog( 0 ); 34 return TRUE; 35 } 36 37 // STEP 3-2: StatusBar 추가 38 if( !m_statusBar.Create( this ) 39 || !m_statusBar.SetIndicators( indicators, 40 sizeof( indicators ) / sizeof( UINT ) ) ) 41 { 42 TRACE0("상태 표시줄을 만들지 못했습니다.\n"); 43 EndDialog( 0 ); 44 return TRUE; 45 } 46 47 // StatusBar 의 첫번째 Pane 크기 조정 48 m_statusBar.SetPaneInfo( 0, m_statusBar.GetItemID( 0 ), 49 SBPS_STRETCH, NULL ); 50 51 // STEP 3-3: ControlBar 추가로 인하여 Dialog 크기 재계산 52 AddControlBar(); 53 54 return TRUE; 55 } 56 57 //------------------------------------------------------------------------------ 58 // STEP 4: Dialog 크기 재계산 함수 추가 59 60 void CMyDialog::AddControlBar() 61 { 62 CRect rcClientStart; 63 CRect rcClientNow; 64 65 GetClientRect( rcClientStart ); 66 RepositionBars( AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0, 67 reposQuery, rcClientNow ); 68 69 CPoint ptOffset( rcClientNow.left - rcClientStart.left, 70 rcClientNow.top - rcClientStart.top ); 71 72 CRect rcChild; 73 CWnd * pwndChild = GetWindow( GW_CHILD ); 74 75 while( pwndChild ) 76 { 77 pwndChild->GetWindowRect( rcChild ); 78 ScreenToClient( rcChild ); 79 rcChild.OffsetRect( ptOffset ); 80 pwndChild->MoveWindow( rcChild, FALSE ); 81 pwndChild = pwndChild->GetNextWindow(); 82 } 83 84 CRect rcWindow; 85 86 GetWindowRect( rcWindow ); 87 rcWindow.right += rcClientStart.Width() - rcClientNow.Width(); 88 rcWindow.bottom += rcClientStart.Height() - rcClientNow.Height(); 89 MoveWindow( rcWindow, FALSE ); 90 91 RepositionBars( AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0 ); 92 } 93 94 //------------------------------------------------------------------------------ 95 // STEP 5: ToolBar 의 ToolTip 을 출력하기 위한 메시지 헨들러 추가 96 97 // STEP 5-1: ToolTip 메시지 헨들러 등록 98 BEGIN_MESSAGE_MAP( CMyDialog, CDialog ) 99 ... 100 ON_NOTIFY_EX_RANGE( TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText ) 101 ON_NOTIFY_EX_RANGE( TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText ) 102 END_MESSAGE_MAP() 103 104 // STEP 5-2: ToolTip 메시지 헨들러 선언 105 afx_msg BOOL OnToolTipText( UINT, NMHDR * pNMHDR, LRESULT * pResult ); 106 107 // STEP 5-3: ToolTip 메시지 헨들러 구현 108 BOOL CMyDialog::OnToolTipText( UINT, NMHDR * pNMHDR, LRESULT * pResult ) 109 { 110 ASSERT( pNMHDR->code == TTN_NEEDTEXTA || pNMHDR->code == TTN_NEEDTEXTW ); 111 112 if( GetRoutingFrame() != NULL ) 113 return FALSE; 114 115 TOOLTIPTEXTA * pTTTA = (TOOLTIPTEXTA*)pNMHDR; 116 TOOLTIPTEXTW * pTTTW = (TOOLTIPTEXTW*)pNMHDR; 117 TCHAR szFullText[ 256 ]; 118 CString strTipText; 119 UINT_PTR nID = (UINT_PTR)pNMHDR->idFrom; 120 121 if( pNMHDR->code == TTN_NEEDTEXTA && ( pTTTA->uFlags & TTF_IDISHWND ) || 122 pNMHDR->code == TTN_NEEDTEXTW && ( pTTTW->uFlags & TTF_IDISHWND ) ) 123 { 124 nID = ((UINT_PTR)(WORD)::GetDlgCtrlID((HWND)nID)); 125 } 126 127 if( nID != 0 ) 128 { 129 AfxLoadString( (UINT)nID, szFullText ); 130 AfxExtractSubString( strTipText, szFullText, 1, '\n' ); 131 } 132 133 #ifndef _UNICODE 134 if( pNMHDR->code == TTN_NEEDTEXTA ) 135 { 136 _tcsncpy_s( pTTTA->szText, 137 ( sizeof( pTTTA->szText ) / sizeof( pTTTA->szText[ 0 ] ) ), 138 strTipText, _TRUNCATE ); 139 } 140 else 141 { 142 int n = MultiByteToWideChar( CP_ACP, 0, strTipText, -1, pTTTW->szText, 143 sizeof( pTTTW->szText ) / sizeof( pTTTW->szText[ 0 ] ) ); 144 if( n > 0 ) 145 pTTTW->szText[ n - 1 ] = 0; 146 } 147 #else 148 if( pNMHDR->code == TTN_NEEDTEXTA ) 149 { 150 int n = WideCharToMultiByte( CP_ACP, 0, strTipText, -1, pTTTA->szText, 151 sizeof( pTTTA->szText ) / sizeof( pTTTA->szText[ 0 ] ), 152 NULL, NULL ); 153 if( n > 0 ) 154 pTTTA->szText[ n - 1 ] = 0; 155 } 156 else 157 { 158 _tcsncpy_s( pTTTW->szText, 159 ( sizeof( pTTTW->szText ) / sizeof( pTTTW->szText[ 0 ] ) ), 160 strTipText, _TRUNCATE ); 161 } 162 #endif 163 164 *pResult = 0; 165 166 ::SetWindowPos( pNMHDR->hwndFrom, HWND_TOP, 0, 0, 0, 0, 167 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE ); 168 169 return TRUE; 170 }
1 // File: MFC/MyControlBar.h 2 3 // MyControlBar.h : 클래스 선언 4 // 5 6 #pragma once 7 8 9 //------------------------------------------------------------------------------ 10 // CMyToolBar 클래스 선언 11 12 class CMyToolBar : public CToolBar 13 { 14 // 생성자/소멸자 15 public: 16 CMyToolBar(); 17 virtual ~CMyToolBar(); 18 19 protected: 20 //{{AFX_MSG(CMyToolBar) 21 afx_msg LRESULT OnIdleUpdateCmdUI(WPARAM wParam, LPARAM); 22 //}}AFX_MSG 23 DECLARE_MESSAGE_MAP() 24 }; 25 26 //------------------------------------------------------------------------------ 27 // CMyStatusBar 클래스 선언 28 29 class CMyStatusBar : public CStatusBar 30 { 31 // 생성자/소멸자 32 public: 33 CMyStatusBar(); 34 virtual ~CMyStatusBar(); 35 36 protected: 37 //{{AFX_MSG(CMyStatusBar) 38 afx_msg LRESULT OnIdleUpdateCmdUI(WPARAM wParam, LPARAM); 39 //}}AFX_MSG 40 DECLARE_MESSAGE_MAP() 41 };
1 // File: MFC/MyControlBar.cpp 2 3 // MyControlBar.cpp : 클래스 구현 4 // 5 6 #include "stdafx.h" 7 #include <afxpriv.h> 8 #include "MyControlBar.h" 9 10 #ifdef _DEBUG 11 #define new DEBUG_NEW 12 #undef THIS_FILE 13 static char THIS_FILE[] = __FILE__; 14 #endif 15 16 17 //////////////////////////////////////////////////////////////////////////////// 18 // CMyToolBar 클래스 구현 19 20 BEGIN_MESSAGE_MAP(CMyToolBar, CToolBar) 21 //{{AFX_MSG_MAP(CMyToolBar) 22 ON_MESSAGE(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI) 23 //}}AFX_MSG_MAP 24 END_MESSAGE_MAP() 25 26 //------------------------------------------------------------------------------ 27 // 생성자/소멸자 구현 28 29 CMyToolBar::CMyToolBar() 30 { 31 } 32 33 CMyToolBar::~CMyToolBar() 34 { 35 } 36 37 //------------------------------------------------------------------------------ 38 // 메시지 핸들러 구현 39 40 LRESULT CMyToolBar::OnIdleUpdateCmdUI(WPARAM wParam, LPARAM) 41 { 42 if( IsWindowVisible() ) 43 { 44 CFrameWnd * pParent = (CFrameWnd *)GetParent(); 45 if( pParent ) 46 OnUpdateCmdUI( pParent, (BOOL)wParam ); 47 } 48 49 return 0L; 50 } 51 52 //////////////////////////////////////////////////////////////////////////////// 53 // CMyStatusBar 클래스 구현 54 55 BEGIN_MESSAGE_MAP(CMyStatusBar, CStatusBar) 56 //{{AFX_MSG_MAP(CMyStatusBar) 57 ON_MESSAGE(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI) 58 //}}AFX_MSG_MAP 59 END_MESSAGE_MAP() 60 61 //------------------------------------------------------------------------------ 62 // 생성자/소멸자 구현 63 64 CMyStatusBar::CMyStatusBar() 65 { 66 } 67 68 CMyStatusBar::~CMyStatusBar() 69 { 70 } 71 72 //------------------------------------------------------------------------------ 73 // 메시지 핸들러 구현 74 75 LRESULT CMyStatusBar::OnIdleUpdateCmdUI(WPARAM wParam, LPARAM) 76 { 77 if( IsWindowVisible() ) 78 { 79 CFrameWnd * pParent = (CFrameWnd *)GetParent(); 80 if( pParent ) 81 OnUpdateCmdUI( pParent, (BOOL)wParam ); 82 } 83 84 return 0L; 85 }
4.8. 메뉴 기능 추가 [Bottom] [Top]
대화상자에서 기본으로 제공되지 않는 메뉴 기능을 추가한다. 대화상자의 속성에서 간단한 추가로 쉽게 사용할 수 있지만 명령 사용자 인터페이스(UPDATE_COMMAND_UI) 가 처리되지 않기 때문에 추가적인 작업이 필요하다.
[리소스 뷰] 에서 [Menu] 를 추가한다. (메뉴 리소스 ID 지정 - 필수)
[리소스 뷰] 에서 대화상자의 속성 창으로 열고 [Menu] 속성에 메뉴 리소스 ID 를 설정한다.
대화상자 클래스(CDialog 를 상속받은 클래스) 에서 각 메뉴의 메시지 핸들러(COMMAND) 를 추가한다.
하지만, 메뉴의 명령 사용자 인터페이스(UPDATE_COMMAND_UI) 가 처리되지 않는다.
// 예제 코드 void CTestDlg::OnUpdateFileExit(CCmdUI* pCmdUI) { pCmdUI->Enable( FALSE ); // Not calling the command handler, but does not show as disabled. pCmdUI->SetCheck( TRUE ); // Does not show check mark before the text. pCmdUI->SetRadio( TRUE ); // Does not show dot before the text. pCmdUI->SetText( "Close" ); // Does not change the text. }
따라서 필요한 경우 다음과 같은 추가 구현이 필요하다.
대화상자 클래스에 WM_INITMENUPOPUP 메시지 핸들러 추가한다. (OnInitMenuPopup() 멤버 함수가 추가됨)
OnInitMenuPopup() 함수를 다음과 같이 구현한다.
1 // File: MFC/Dlg_OnInitMenuPopup.cpp 2 3 void CTestDlg::OnInitMenuPopup( CMenu * pPopupMenu, UINT nIndex, BOOL bSysMenu ) 4 { 5 ASSERT( pPopupMenu != NULL ); 6 // Check the enabled state of various menu items. 7 8 CCmdUI state; 9 state.m_pMenu = pPopupMenu; 10 ASSERT( state.m_pOther == NULL ); 11 ASSERT( state.m_pParentMenu == NULL ); 12 13 // Determine if menu is popup in top-level menu and set m_pOther to 14 // it if so (m_pParentMenu == NULL indicates that it is secondary popup). 15 HMENU hParentMenu; 16 if( AfxGetThreadState()->m_hTrackingMenu == pPopupMenu->m_hMenu ) 17 state.m_pParentMenu = pPopupMenu; // Parent == child for tracking popup. 18 else if( ( hParentMenu = ::GetMenu( m_hWnd ) ) != NULL ) 19 { 20 CWnd * pParent = this; 21 // Child windows don't have menus--need to go to the top! 22 if( ( pParent != NULL ) 23 && ( ( hParentMenu = ::GetMenu( pParent->m_hWnd ) ) != NULL ) ) 24 { 25 int nIndexMax = ::GetMenuItemCount( hParentMenu ); 26 for( int nIndex = 0; nIndex < nIndexMax; ++nIndex ) 27 { 28 if( ::GetSubMenu( hParentMenu, nIndex ) == pPopupMenu->m_hMenu ) 29 { 30 // When popup is found, m_pParentMenu is containing menu. 31 state.m_pParentMenu = CMenu::FromHandle( hParentMenu ); 32 break; 33 } 34 } 35 } 36 } 37 38 state.m_nIndexMax = pPopupMenu->GetMenuItemCount(); 39 for( state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax; ++state.m_nIndex ) 40 { 41 state.m_nID = pPopupMenu->GetMenuItemID( state.m_nIndex ); 42 if( state.m_nID == 0 ) 43 continue; // Menu separator or invalid cmd - ignore it. 44 45 ASSERT( state.m_pOther == NULL ); 46 ASSERT( state.m_pMenu != NULL ); 47 if( state.m_nID == (UINT)-1 ) 48 { 49 // Possibly a popup menu, route to first item of that popup. 50 state.m_pSubMenu = pPopupMenu->GetSubMenu( state.m_nIndex ); 51 if( ( state.m_pSubMenu == NULL ) 52 || ( ( state.m_nID = state.m_pSubMenu->GetMenuItemID( 0 ) ) == 0 ) 53 || ( state.m_nID == (UINT)-1 ) ) 54 { 55 continue; // First item of popup can't be routed to. 56 } 57 state.DoUpdate( this, TRUE ); // Popups are never auto disabled. 58 } 59 else 60 { 61 // Normal menu item. 62 // Auto enable/disable if frame window has m_bAutoMenuEnable 63 // set and command is _not_ a system command. 64 state.m_pSubMenu = NULL; 65 state.DoUpdate( this, FALSE ); 66 } 67 68 // Adjust for menu deletions and additions. 69 UINT nCount = pPopupMenu->GetMenuItemCount(); 70 if( nCount < state.m_nIndexMax ) 71 { 72 state.m_nIndex -= ( state.m_nIndexMax - nCount ); 73 while( ( state.m_nIndex < nCount ) 74 && ( pPopupMenu->GetMenuItemID( state.m_nIndex ) == state.m_nID ) ) 75 { 76 ++state.m_nIndex; 77 } 78 } 79 state.m_nIndexMax = nCount; 80 } 81 }
5. 문자열 [Bottom] [Top]
5.1. CString 객체를 C-Style Null-Terminated 문자열로 변환하기 [Bottom] [Top]
표준 런타임 라이브러리를 사용하기 위해서 C-Style 문자열이 필요할 때 사용.
1 CString theString( "Hello World!" ); 2 3 printf( "%s\n", (LPCTSTR)theString ); 4
6. 글꼴 [Bottom] [Top]
6.1. 속성 바꾸기 [Bottom] [Top]
CWnd 를 상속받은 객체에서 현재 사용중인 글꼴의 속성(크기)를 바꾸는 소스 코드.
1 // 클래스의 멤버로 선언 2 //--------------------- 3 CFont m_font; 4 ... 5 6 // 함수에서 구현 7 //-------------- 8 LOGFONT logFont; 9 10 GetFont()->GetLogFont( &logFont ); // STEP 1: 글꼴의 속성을 얻는다. 11 logFont.lfHeight -= 2; // STEP 2: 글꼴의 크기를 크게한다. (음수 사용) 12 m_font.CreateFontIndirect( &logFont ); // STEP 3: 새로운 글꼴을 만든다. 13 m_listView.SetFont( m_font ); // STEP 4: 새로운 글꼴을 지정한다. 14
6.2. 글꼴 크기 계산 [Bottom] [Top]
일반적으로 많이 쓰는 포인트(pt) 크기와 논리 크기 사이에는 크기 변환을 다음과 같은 식으로 계산한다.
- 일반 크기(pt) 를 논리 크기로 변환
// nFontSize : 일반 크기(pt) // LOGFONT::lfHeight : 논리 크기 LOGFONT::lfHeight = -MulDiv( nFontSize, GetDeviceCaps( pDC, LOGPIXELSY ), 72 ); // 논리크기 = -((일반크기 * GetDeviceCaps(pDC, LOGPIXELSY)) / 72)
- 논리 크기를 일반 크기로 변환
// LOGFONT::lfHeight : 논리 크기 // nFontSize : 일반 크기(pt) nFontSize = -MulDiv( LOGFONT::lfHeight, 72, GetDeviceCaps( pDC, LOGPIXELSY ) ); // 일반크기 = -((논리크기 * 72) / GetDeviceCaps(pDC, LOGPIXELSY))
6.3. DC 에서 GDI 글꼴 크기 계산하기 [Bottom] [Top]
DC (device-context) 에서 표시될 GDI 글꼴 크기를 계산하는 소스 코드. 아래의 소스 코드는 한글 1자의 크기, 영문 1자의 크기를 계산하는 코드.
1 // STEP 0: 지정된 컨트롤의 글꼴을 DC 에 설정 2 CPaintDC dc( this ); 3 4 CFont * pCurFont = m_myEdit.GetFont(); 5 CFont * pOldFont = dc.SelectObject( pCurFont ); 6 7 // STEP 1: 한글 글꼴 크기 얻기 8 CString strText = _T( "가" ); 9 CSize sizeLen = dc.GetOutputTextExtent( strText ); 10 11 korChar.cx = sizeLen.cx; // '가' 문자의 가로 크기 12 korChar.cy = sizeLen.cy; // '가' 문자의 세로 크기 13 14 // STEP 2: 영문 글꼴 크기 얻기 15 strText = _T( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ); 16 sizeLen = dc.GetOutputTextExtent( strText ); 17 18 engChar.cx = sizeLen.cx / strText.GetLength(); // 영문 문자의 평균 가로 크기 (가변폭일 경우) 19 engChar.cy = sizeLen.cy; // 영문 문자의 세로 크기 20 21 // STEP 3: 글꼴 해제 22 dc.SelectObject( pOldFont ); 23
7. 컨트롤 [Bottom] [Top]
7.1. 8 비트 이상의 이미지 사용하기 [Bottom] [Top]
Tree 컨트롤에서 8 비트 (256색) 이상의 이미지를 사용하기 위한 코드로 ToolBar 에서도 사용 가능하다.
1 // 팔레트 사용 설정 2 #define _USE_PALETTE 3 4 // 상수 정의 5 #define IMAGE_WITH 16 6 #define IMAGE_MASK_INDEX 255 7 #define IMAGE_MASK_COLOR ( RGB( 0xFF, 0xFF, 0xFF ) ) 8 9 // 변수 정의 10 CImageList m_imageList; 11 12 // 함수 구현 13 HRESULT CreateImageList256( DWORD dwImageList ) 14 { 15 CBitmap bitmap; 16 BITMAP bitmapInfo; 17 18 if( !( 19 #ifdef _USE_PALETTE 20 bitmap.Attach( 21 ::LoadImage( 22 AfxGetResourceHandle(), 23 MAKEINTRESOURCE( dwImageList ), 24 IMAGE_BITMAP, 25 0, 0, 26 LR_DEFAULTSIZE | LR_CREATEDIBSECTION 27 ) 28 ) 29 #else 30 bitmap.LoadBitmap( dwImageList ) 31 #endif 32 ) || !( bitmap.GetBitmap( &bitmapInfo ) ) ) 33 { 34 return E_FAIL; 35 } 36 37 #ifdef _USE_PALETTE 38 RGBTRIPLE * rgb = static_cast< RGBTRIPLE * >( bitmapInfo.bmBits ); 39 COLORREF rgbMask = RGB( 40 rgb[ IMAGE_MASK_INDEX ].rgbtRed, 41 rgb[ IMAGE_MASK_INDEX ].rgbtGreen, 42 rgb[ IMAGE_MASK_INDEX ].rgbtBlue 43 ); 44 #else 45 COLORREF rgbMask = IMAGE_MASK_COLOR; 46 #endif 47 48 if( !( m_imageList.Create( 49 IMAGE_WITH, 50 bitmapInfo.bmHeight, 51 ILC_COLOR8 | ILC_MASK, 52 bitmapInfo.bmWidth / IMAGE_WITH, 0 ) ) 53 || ( m_imageList.Add( &bitmap, rgbMask ) == -1 ) ) 54 { 55 return E_FAIL; 56 } 57 58 bitmap.Detach(); 59 60 return S_OK; 61 } 62
참고> Very simple true color toolbar (It's true!) - Code Project
7.2. CBitmapButton 사용하기 [Bottom] [Top]
버튼 모양을 마음대로 만들 수 있는 CBitmapButton 을 생성하는 소스 코드.
1 // STEP 0: CBitmapButton 타입으로 m_btnBitmap 변수를 추가한다. 2 3 BOOL CExampleDlg::OnInitDialog() 4 { 5 // STEP 1: 버튼 위치 설정. 6 m_btnBitmap.MoveWindow( 200, 100, 100, 30, FALSE ); 7 8 // STEP 2: 버튼의 이미지 로딩. 9 // IDB_BUTTON_UP : 기본 이미지 10 // IDB_BUTTON_DOWN : 버튼을 누른 경우 11 // IDB_BUTTON_FOCUS : 포커스를 받은 경우 12 // IDB_BUTTON_DISABLE : Disable된 경우 13 m_btnBitmap.LoadBitmaps( IDB_BUTTON_UP, IDB_BUTTON_DOWN, IDB_BUTTON_FOCUS, IDB_BUTTON_DISABLE ); 14 15 // STEP 3: 이미지 크기에 맞게 버튼 크기를 자동 조절. 16 m_btnBitmap.SizeToContent(); 17 18 return TRUE; 19 } 20
7.3. CListCtrl 사용하기 [Bottom] [Top]
- 초기화 - 확장 스타일 설정, 리스트 컬럼 추가
1 // 변수 선언 2 CListCtrl m_listCtrl; 3 4 //------------------------------------------------------------------------------ 5 6 // STEP 0: 리스트 컨트롤의 확장 스타일 설정 (행 전체 선택, 눈금선 표시) 7 m_listCtrl.SetExtendedStyle( LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES ); 8 9 RECT rect; 10 m_listCtrl.GetClientRect( &rect ); // 리스트 컨트롤의 크기 얻기 11 int nWidth = rect.right / 2; // 리스트 컨트롤의 컬럼 크기 계산 12 13 // STEP 1: 컬럼 추가 14 // 방법 1-1: 함수의 매개변수를 이용하여 추가 15 m_listCtrl.InsertColumn( 0, _T( "컬럼0" ), LVCFMT_LEFT, nWidth ); // 첫번째 컬럼 추가 16 17 // 방법 1-2: LVCOLUMN 구조체를 이용하여 추가 18 LVCOLUMN listCol = { 19 LVCF_FMT | LVCF_TEXT | LVCF_WIDTH, // UINT mask : 문자열, 넓이 지정 (MSDN 참고) 20 LVCFMT_LEFT, // int fmt : 문자열 왼쪽정렬 (MSDN 참고) 21 nWidth, // int cx : 컬럼 넓이 22 _T( "컬럼1" ), // LPCTSTR pszText : 컬럼 문자열 23 0, // int cchTextMax : MSDN 참고 24 0, // int iSubItem : MSDN 참고 25 0, // int iImage : MSDN 참고 26 0 // int iOrder : MSDN 참고 27 }; 28 29 m_listCtrl.InsertColumn( 1, &listCol ); // 두번째 컬럼 추가 30
- 아이템 삽입/수정
1 //------------------------------------------------------------------------------ 2 // 방법 1: 함수의 매개변수를 이용하여 삽입 3 4 // STEP 1-1: 아이템 삽입 후 첫번째 컬럼의 데이터 설정 5 m_listCtrl.InsertItem( 0, _T( "아이템 컬럼0" ) ); 6 7 // STEP 1-2: 삽입된 아이템에 대하여 각 컬럼의 데이터 설정 8 m_listCtrl.SetItem( 0, 1, LVIF_TEXT, _T( "아이템 컬럼1" ), 0, 0, 0, 0 ); 9 10 //------------------------------------------------------------------------------ 11 // 방법 2: LVCOLUMN 구조체를 이용하여 삽입 12 13 // STEP 2-1: 아이템 삽입 후 첫번째 컬럼의 데이터 설정 14 LVITEM listItem0 = { 15 LVIF_TEXT, // UINT mask : 문자열 지정 (MSDN 참고) 16 nPos, // int iItem : 삽입될 아이템 위치 17 0, // int iSubItem : 아이템 컬럼 위치 18 0, // UINT state : MSDN 참고 19 0, // UINT stateMask : MSDN 참고 20 _T( "아이템 컬럼0" ), // LPCTSTR pszText : 아이템 문자열 21 0, // int cchTextMax : MSDN 참고 22 0, // int iImage : MSDN 참고 23 0, // LPARAM lParam : MSDN 참고 24 0, // int iIndent : MSDN 참고 25 0, // int iGroupId : MSDN 참고 (XP 이상 지원) 26 0, // UINT cColumns : MSDN 참고 (XP 이상 지원) 27 0, // PUINT puColumns : MSDN 참고 (XP 이상 지원) 28 }; 29 30 m_listCtrl.InsertItem( &listItem0 ); 31 32 // STEP 2-2: 삽입된 아이템에 대하여 각 컬럼의 데이터 설정 33 LVITEM listItem1 = { 34 LVIF_TEXT, // UINT mask 35 nPos, // int iItem 36 1, // int iSubItem 37 0, // UINT state 38 0, // UINT stateMask 39 _T( "아이템 컬럼1" ), // LPCTSTR pszText 40 0, // int cchTextMax 41 0, // int iImage 42 0, // LPARAM lParam 43 0, // int iIndent 44 0, // int iGroupId 45 0, // UINT cColumns 46 0, // PUINT puColumns 47 }; 48 49 m_listCtrl.SetItem( &listItem1 ); 50
- 아이템 선택/삭제
1 // 선택된 아이템 찾기 2 int nSelectedItem = reinterpret_cast< int >( m_listCtrl.GetFirstSelectedItemPosition() ); 3 4 // 아이템 삭제 (한개씩) 5 m_listCtrl.DeleteItem( nSelectedItem ); 6 7 // 아이템 전체 삭제 8 m_listCtrl.DeleteAllItems(); 9
- 아이템 검색
1 int nItem; 2 LVFINDINFO findKey; 3 4 findKey.flags = LVFI_PARTIAL | LVFI_STRING; // 문자열로 시작하는 아이템 검색 5 findKey.psz = _T( "검색문자열" ); 6 7 while( -1 != ( nItem = m_listCtrl.FindItem( &findKey ) ) ) 8 { 9 ... 10 } 11
7.4. 컨트롤의 문자색/배경색 바꾸기 [Bottom] [Top]
특정 타입의 컨트롤이나 지정된 하나의 컨트롤에 글자색, 배경색, 배경 모드(예: 혼합 모드) 등을 설정하는 소스 코드.
1 HBRUSH CExampleDlg::OnCtlColor( CDC * pDC, CWnd * pWnd, UINT nCtlColor ) 2 { 3 HBRUSH hbr = CDialog::OnCtlColor( pDC, pWnd, nCtlColor ); 4 5 switch( nCtlColor ) 6 { 7 case CTLCOLOR_EDIT: 8 // Edit 컨트롤 타입의 처리 추가 9 ... 10 11 if( IDC_MY_EDIT == pWnd->GetDlgCtrlID() ) 12 { 13 // Edit 컨트롤 타입의 IDC_MY_EDIT 컨트롤 처리 14 pDC->SetTextColor( m_colorMyEdit ); 15 } 16 17 break; 18 } 19 20 return hbr; 21 } 22
- nCtlColor 매개변수로 받을 수 있는 값
타입
설명
CTLCOLOR_BTN
Button 컨트롤
CTLCOLOR_DLG
Dialog 박스
CTLCOLOR_EDIT
Edit 컨트롤
CTLCOLOR_LISTBOX
List box 컨트롤
CTLCOLOR_MSGBOX
Message 박스
CTLCOLOR_SCROLLBAR
Scroll-bar 컨트롤
CTLCOLOR_STATIC
Static 컨트롤
7.5. 컨트롤 내부 스크롤하기 [Bottom] [Top]
CListBox 와 같은 컨트롤에서 내부 영역을 스크롤하는 방법. 특히 문자열을 추가할 때마다 자동으로 스크롤되어야 할 경우 유용하다.
// 컨트롤 변수 CListBox m_mesgList; // 문자열 추가 m_mesgList.AddString( szMesg ); // 내부 영역 스크롤 (최하단으로 이동) m_mesgList.SendMessage( WM_VSCROLL, SB_BOTTOM );
8. 파일 처리 [Bottom] [Top]
8.1. 파일 대화상자 - 파일이름 얻기 [Bottom] [Top]
- CFileDialog 클래스의 생성자
CFileDialog( BOOL bOpenFileDialog, // 모드 (TRUE: Read / FALSE: Save) LPCTSTR lpszDefExt = NULL, // 기본 파일 확장자 LPCTSTR lpszFileName = NULL, // 대화상자에 처음 나타낼 파일 이름 DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, // 아래 참고 LPCTSTR lpszFilter = NULL, // 파일 형식 필터 CWnd * pParentWnd = NULL, // 부모 윈도우 포인터 DWORD dwSize = 0 // 사용 안 함 );
CFileDialog() 생성자의 dwFlags 매개변수는 OPENFILENAME 구조체의 Flags 변수에 설정되는 값들을 사용한다.
- 사용 예
1 HRESULT GetFileName( CString & strFile, BOOL bRead ) 2 { 3 DWORD dwFlags = ( TRUE == bRead ) 4 ? OFN_FILEMUSTEXIST | OFN_HIDEREADONLY // READ mode 5 : OFN_CREATEPROMPT | OFN_OVERWRITEPROMPT; // SAVE mode 6 7 CFileDialog fileDlg( bRead, _T( "csv" ), strFile, dwFlags, 8 _T( "Excel 파일 (*.csv)|*.csv|텍스트 파일 (*.txt)|*.txt|모든 파일 (*.*)|*.*||" ) ); 9 10 if( IDOK == fileDlg.DoModal() ) 11 { 12 strFile = fileDlg.GetFileName(); 13 return S_OK; 14 } 15 16 return E_FAIL; 17 } 18
8.2. 파일 열기 [Bottom] [Top]
1 // 읽기 모드 2 CFile file( m_strFileName, CFile::modeRead ); 3 CArchive ar( &file, CArchive::load ); 4 5 // 쓰기 모드 6 CFile file( m_strFileName, CFile::modeWrite | CFile::modeCreate ); 7 CArchive ar( &file, CArchive::store ); 8
- CFile 클래스의 열기 모드
- CFile::modeRead - 읽기 모드
- CFile::modeWrite - 쓰기 모드
- CFile::modeCreate - 파일이 없으면 생성
- CArchive 클래스의 열기 모드
- CArchive::load - 읽기 모드
- CArchive::store - 저장 모드
8.3. Serialize() 함수 구현 [Bottom] [Top]
1 void CMesgList::Serialize( CArchive & ar ) 2 { 3 if( ar.IsLoading() ) // if( !ar.IsStoring() ) 와 동일 4 { 5 // 읽기 모드 6 ... 7 } 8 else 9 { 10 // 쓰기 모드 11 ... 12 } 13 } 14
8.4. INI 파일 사용하기 [Bottom] [Top]
- ini 파일과 관련된 MFC 멤버 변수와 함수
// 멤버 변수 CWinApp::m_pszProfileName // ini 파일 이름 // - 기본값: 프로그램이름.INI, (C:\Windows 폴더에 생성됨) CWinApp::m_pszRegistryKey // NULL 아닐 경우: 레지스트리 저장 // NULL 일 경우 : ini 파일 저장 // 멤버 함수 CWinApp::GetProfileBinary() // 이진 데이터 CWinApp::GetProfileInt() // 정수값 CWinApp::GetProfileString() // 문자열 CWinApp::WriteProfileBinary() // 이진 데이터 CWinApp::WriteProfileInt() // 정수값 CWinApp::WriteProfileString() // 문자열
- ini 파일을 설정하는 함수 추가
1 // 함수 선언 2 void CMyApp::SetProfile( LPCTSTR szProfile=NULL ); 3 4 // 함수 구현 5 void CMyApp::SetProfile( LPCTSTR szProfile ) 6 { 7 TCHAR szCurDir[ MAX_PATH ]; 8 TCHAR * pFile = ( NULL == szProfile ) ? m_pszProfileName : szProfile; 9 10 //---------------------------------------------------------------------- 11 12 ::GetCurrentDirectory( MAX_PATH, szCurDir ); 13 14 _tcscat( szCurDir, _T( "\\" ) ); 15 _tcscat( szCurDir, pFile ); 16 17 //---------------------------------------------------------------------- 18 19 BOOL bEnable = AfxEnableMemoryTracking( FALSE ); 20 21 free( const_cast< LPTSTR >( m_pszRegistryKey ) ); 22 m_pszRegistryKey = NULL; 23 24 free( const_cast< LPTSTR >( m_pszProfileName ) ); 25 m_pszProfileName = _tcsdup( szCurDir ); 26 27 AfxEnableMemoryTracking( bEnable ); 28 } 29
- ini 파일을 사용하기 위한 초기 설정
1 BOOL CMyApp::InitInstance() 2 { 3 ... 4 5 // STEP 0-1: 기본 생성 코드 중 아래의 라인을 주석 처리한다. 6 // (레지스트리를 사용하지 않고 ini 파일을 사용하도록 설정된다.) 7 // SetRegistryKey(_T("로컬 응용 프로그램 마법사에서 생성된 응용 프로그램")); 8 9 // STEP 0-2: ini 파일을 사용하도록 설정한다. 10 // (현재 폴더에 {프로그램이름}.INI 파일로 설정된다.) 11 SetProfile(); 12 13 LoadStdProfileSettings(4); // MRU를 포함하여 표준 INI 파일 옵션을 로드합니다. 14 15 ... 16 } 17
- ini 파일 함수 사용하기
1 //------------------------------------------------------------------------------ 2 // 사용 예1) INI 파일 읽기 3 UINT nNum; 4 CString strTemp; 5 BYTE * pData; 6 UINT nSize; 7 8 nNum = GetProfileInt( _T( "Application" ), _T( "Number" ), 0 ); 9 strTemp = GetProfileString( _T( "System" ), _T( "String" ), _T( "-" ) ); 10 GetProfileBinary( _T( "Data" ), _T( "Binary" ), reinterpret_cast< LPBYTE * >( &pData ), &nSize ); 11 12 TRACE1( "Application: Number = %d\n", nNum ); 13 TRACE1( "System : String = %s\n", strTemp ); 14 TRACE1( "Data : Binary = %s\n", pData ); 15 16 delete [] pData; // GetProfileBinary() 함수는 메모리를 할당해서 반환하기 17 // 때문에 사용 후 메모리를 제거한다. 18 19 //------------------------------------------------------------------------------ 20 // 사용 예2) INI 파일 저장 21 BYTE byData[] = { '1', '2', '3', '4', '5', 0 }; 22 23 WriteProfileInt( _T( "Application" ), _T( "Number" ), 100 ); 24 WriteProfileString( _T( "System" ), _T( "String" ), _T( "My Program" ) ); 25 WriteProfileBinary( _T( "Data" ), _T( "Binary" ), byData, 6 ); 26
실행 결과> MyApp.ini 파일 내용
[Application] Number=100 [System] String=My Program [Data] Binary=BDCDDDEDFDAA
8.4.1. ToolBar 정보 읽기/쓰기 [Bottom] [Top]
CMainFrame 객체에 등록되어 있는 ToolBar 정보(상태)를 읽기/쓰기 위한 방법.
void CFrameWnd::LoadBarState( LPCTSTR lpszProfileName ); void CFrameWnd::SaveBarState( LPCTSTR lpszProfileName ) const;
CWinApp 객체에서 정보를 저장하는 방식(ini 파일 / 레지스트리) 에 따라 lpszProfileName 변수는 다음과 같이 사용된다.
ini 파일 사용 - Section 이름
레지스트리 사용 - 레지스트리 키 이름
ini 파일을 사용할 경우 CWinApp 객체에서 지정된 ini 파일(CWinApp::m_pszProfileName)에 저장된다.
9. 윈도우즈 메시지 처리 [Bottom] [Top]
9.1. 사용자 정의 메시지 처리하기 [Bottom] [Top]
사용자 정의 메시지 처리에 대하여 수동으로 추가하는 방법(MS 에서는 자동으로 추가하는 방법을 제공하지 않음).
- 1단계: 사용자 정의 메시지와 메시지 맵 정의
1 // 사용자 정의 메시지 정의 2 #define WM_MYMESSAGE (WM_USER + 100) 3 4 // 메시지 맵 정의 5 BEGIN_MESSAGE_MAP(CMyWnd2, CWnd) 6 ON_MESSAGE(WM_MYMESSAGE, OnMyMessage) 7 END_MESSAGE_MAP() 8
- 2단계: 사용자 메시지 처리기 선언과 구현
1 // 사용자 메시지 처리기 선언 2 afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam); 3 4 // 사용자 메시지 처리기 구현 5 LRESULT CMyWnd2::OnMyMessage(WPARAM wParam, LPARAM lParam) 6 { 7 // 처리기 구현 8 ... 9 10 return 0; 11 } 12
호출
1 // 사용자 메시지 전송(MFC 함수) 2 PostMessage( WM_MYMESSAGE ); 3
참고> ON_MESSAGE(MFC)
