3.2 MFC 프로그램의작성, 컴파일, 실행 먼저간단한 MFC 예제프로그램을 Visual C++ 에서작성, 컴파일, 실행등의작업을수행하는방법을보인다음예제프로그램을설명할것이다. 예제프로그램소스코드 //hello.cpp #include <afxwin.h> // Declare the application class class CHelloApp : public CWinApp { public: virtual BOOL InitInstance(); }; // Create an instance of the application class CHelloApp HelloApp; // Declare the main window class class CHelloWindow : public CFrameWnd { CStatic* cs; public: CHelloWindow(); }; // The InitInstance function is called each // time the application first executes. BOOL CHelloApp::InitInstance() { m_pmainwnd = new CHelloWindow(); m_pmainwnd->showwindow(m_ncmdshow); m_pmainwnd->updatewindow(); return TRUE; } // The constructor for the window class CHelloWindow::CHelloWindow() { // Create the window itself Create(NULL, _T("Hello World!"), WS_OVERLAPPEDWINDOW, CRect(0, 0, 300, 200)); // Create a static label cs = new CStatic(); cs->create(_t("hello world"), WS_CHILD WS_VISIBLE SS_CENTER, CRect(50, 80, 250, 150), this); } 이프로그램은다음과같은세가지작업을수행한다. 1 응용프로그램객체 를생성한다. 모든 MFC 프로그램은 MFC 및윈도우운영체제관련초기화작업을수행하는한개의응용프로그램객체를갖는다. 2 응용프로그램객체는화면상에창을하나만드는데, 이창은응용프로그램의주실행창이된다. 3 주실행창안에 "hello world" 라는내용을담고있는텍스트레이블컨트롤을생성한다. 위의예제프로그램을입력하고컴파일하는일은 1장에서텍스트기반프로그램을처리하는일과크게다르지않으며, 다음과같이진행된다. - 69 -
(1) [ 파일 ] 메뉴 -> [ 새로만들기 ] - [ 프로젝트 ] 메뉴항목을선택하여아래화면이나타나면, Win32 - Win32 프로젝트유형을선택하고프로젝트이름을입력한후, < 확인 > 버튼을누른다. 아래화면에서 < 빈프로젝트 > 체크박스를선택하고, < 마침 > 버튼을누른다. - 70 -
C++ 소스프로그램의입력을위한과정은 1 장의경우와같다. - 71 -
(2) 예제프로그램을입력한후 [ 디버깅하지않고시작 ] 메뉴항목등의선택을통해실행프로그램을빌드하는과정의링크단계에서다음과같은오류메시지를얻게될것이다. 1>c: program files microsoft visual studio 10.0 vc atlmfc include afx.h(24): fatal error C1189: #error : Building MFC application with /MD[d] (CRT dll version) requires MFC shared dll version. Please #define _AFXDLL or do not use /MD[d] ========== 빌드 : 성공 0, 실패 1, 최신 0, 생략 0 ========== (3) 위의오류메시지가나타내는것은이프로그램이 MFC 라이브러리를사용하고있는데, 컴파일러옵션이이를반영하고있지않은때문에발생한것이다. 그이유는 Win32 프로젝트유형에서는 MFC 라이브러리를사용하지않는것이디폴트옵션이기때문이다. 따라서컴파일전에 MFC 라이브러리관련옵션을변경해주어야한다. [ 프로젝트 ] 메뉴하단의 [MFCTest( 프로젝트이름 ) 속성 ] 메뉴항목을선택하면아래그림과같은다이얼로그박스가나타날것이다. 왼쪽열은 < 구성속성 > - < 일반 > 으로선택되어있을때, 가운데열의프로젝트기본값중두번째항목인 <MFC 사용 > 의값이 표준 Windows 라이브러리사용 으로되어있을것이다. <MFC 사용 > 항목의오른쪽끝의콤보박스버튼을누르면, 아래와같은리스트가나타날것이다. 이리스트에는 표준 Windows 라이브러리사용 이외에도 정적라이브러리에서 - 72 -
MFC 사용, 공유 DLL에서 MFC 사용 등의항목들이있다. 이들중어느것을선택해도무방하지만, 가장아래항목인 공유 DLL에서 MFC 사용 을선택한다. 이런후에다시 [ 디버깅하지않고시작 ] 메뉴항목을선택하면컴파일과실행이이루어지게될것이다. 프로그램이실행되면전체화면의좌측상단에오른쪽그림과같은주실행창이나타날것이다. 이프로그램에대해주실행창의크기변경, 위치변경, 종료등의사용자작업을수행할수있다. 참고사항 1 [Project -> Settings -> General] 에서 Microsoft Foundation Classes: 라는텍스트레이블아래의콤보박스버튼으로선택할수있는세개의항목은다음과같다. 표준 Windows 라이브러리사용 정적라이브러리에서 MFC 사용 공유 DLL에서 MFC 사용 첫번째옵션은 Win32 프로젝트유형에서는디폴트이며, 물론 MFC를사용하지않을것임을나타낸다. 나머지두옵션은 MFC 라이브러리를사용하되사용하는방식을다르게지정하고있다. 정적라이브러리방식으로 MFC를사용하겠다는두번째옵션은컴파일과정의링크단계에서프로그램에서사용하고있는라이브러리코드들을실행파일에포함시킨다는뜻이다. 반면에마지막옵션인동적링크라이브러리 (Dynamic Link Library; DLL) 방식으로 MFC를사용한다는것은링크단계에서실행파일을만들때라이브러리코드를포함시키는대신실행중에동적링크를할수있는준비만해둔다. 동적링크라이브러리방식의경우정적라이브러리방식에비해실행파일의크기가훨씬작은장점이있다. 따라서실행시메모리에읽어들일파일의크기도작으므로메모리사용량측면에서도유리하며읽어들이는시간도적게걸린다. 동적링크를위해사용되는라이브러리파일은 mfc??.dll이라는이름을갖는데, C: WINDOWS SYSTEM32 폴더에서이파일들을찾을수있다. 파일이름의?? 는버전을나타내며, Visual C++ 6.0의경우 mfc42.dll 파일이며 Visual C++ 2010의경우 mfc100.dll이다. 이파일에는개별프로그램이필요로하는라 - 73 -
이브러리코드만있는것이아니라전체 MFC 라이브러리코드를다포함하고있어큰파일이다. 그러나이파일을필요로하는첫번째프로그램에의해메모리에올라와있으면다른프로그램들은동일한 DLL 파일을공유할수있어추가로읽어들이는작업은필요없다. 이와같은이유에서 Shared( 공유 ) DLL이라는표현을사용한다. DLL 방식의단점은다른컴퓨터로옮겨실행할때, 필요한 DLL 파일이그컴퓨터에갖추어져있지않을경우프로그램을실행할수없다는점이다. 이러한문제를피하고싶을경우라면여러단점에도불구하고정적라이브러리방식의링크가사용되어야할것이다. 참고사항 2 컴파일작업의결과로만들어지는파일들은프로젝트폴더안의 Debug 폴더들과 ipch 폴더안에저장된다. 여기에는물론.exe 확장자를갖는실행파일도포함된다. 여기에들어있는파일들은언제라도프로그램소스에서다시만들어낼수있으므로당장필요하지않을경우지움으로써디스크공간을절약할수있다. [ 특히과제제출시에는이들파일을반드시삭제한후제출하여야한다.] 참고사항 3 컴파일오류가있을경우, 오류메시지가 Visual C++ 화면아래쪽의출력윈도우에나타낸다. 해당메시지를클릭하면그메시지에대응되는소스위치를찾아준다. 이기능은링크단계의오류의경우에는사용할수없다. 링크단계오류는대체로다음과같은경우자주발생한다. MFC 사용옵션이선택되어있지않다. ( 참고사항 1 참고 ) 함수호출시사용하는이름에철자오류가포함되어정의되어있는함수이름과정확히일치하지않는다. 이전에실행된프로그램을종료하지않은상태에서프로그램을수정한후새로컴파일한다. 이경우이전의실행파일을지울수없어새로운실행파일을만들수없으며, 다음과같은오류메시지를보게될것이다. 1>------ 빌드시작 : 프로젝트 : MFCTest, 구성 : Debug Win32 ------ 1> Hello.cpp 1> _WIN32_WINNT not defined. Defaulting to _WIN32_WINNT_MAXVER (see WinSDKVer.h) 1>LINK : fatal error LNK1168: D: class Projects MFCTest Debug MFCTest.exe을 ( 를 ) 쓰기용으로열수없습니다. ========== 빌드 : 성공 0, 실패 1, 최신 0, 생략 0 ========== 3.3 MFC 프로그램예제해설 이절에서는앞절에서사용된 MFC 예제프로그램의전체구조와각부분의내용을차례로 - 74 -
살펴본다. 프로그램설계 여기에서작성하려는프로그램은창안에 "hello world" 라는메시지를출력하는프로그램이다. 이러한응용프로그램은먼저화면상에창을생성하는데, 그창의타이틀바에 "Hello World!" 라는제목을넣는다. 그다음창안에 "hello world" 라는텍스트를표시한다. 이러한작업을위해다음과같은 3개의객체가필요하다. 응용프로그램을초기화하고윈도우운영체제에연결하는작업을수행하는응용프로그램객체가필요하며, 이객체는또한이벤트처리작업을담당한다. 응용프로그램의주실행창의역할을담당하는창객체. "hello world" 라는텍스트레이블을보여주는텍스트컨트롤객체. 위의객체들중첫두객체는모든 MFC 프로그램에공통적으로포함되는객체들이다. 세번째객체는여기서의예제응용프로그램에서필요하여포함시킨객체이다. 개별응용프로그램마다사용자로부터의입력을받기위해또는출력을표현하기위한여러가지사용자인터페이스객체들을가질수있다. 대체적인사용자인터페이스설계가완료되면그와같은인터페이스를구현하기위해창이나컨트롤등을생성하기위한코드를작성하게될것이다. 이프로그램의경우에는텍스트레이블을나타내는컨트롤하나만사용하고있지만주실행창이나여러다이얼로그박스들에서수백개의컨트롤들을사용할수도있다. 예제코드이해하기 아래에보여주고있는코드는앞의예제코드에설명의편의를위해행번호를붙인것이다. 1 //hello.cpp 2 #include <afxwin.h> 3 // Declare the application class 4 class CHelloApp : public CWinApp 5 { 6 public: 7 virtual BOOL InitInstance(); 8 }; - 75 -
9 // Create an instance of the application class 10 CHelloApp HelloApp; 11 // Declare the main window class 12 class CHelloWindow : public CFrameWnd 13 { 14 CStatic* cs; 15 public: 16 CHelloWindow(); 17 }; 18 // The InitInstance function is called each 19 // time the application first executes. 20 BOOL CHelloApp::InitInstance() 21 { 22 m_pmainwnd = new CHelloWindow(); 23 m_pmainwnd->showwindow(m_ncmdshow); 24 m_pmainwnd->updatewindow(); 25 return TRUE; 26 } 27 // The constructor for the window class 28 CHelloWindow::CHelloWindow() 29 { 30 // Create the window itself 31 Create(NULL, 32 -T("Hello World!"), 33 WS_OVERLAPPEDWINDOW, 34 CRect(0, 0, 300, 200)); 35 // Create a static label 36 cs = new CStatic(); 37 cs->create(_t("hello world"), 38 WS_CHILD WS_VISIBLE SS_CENTER, 39 CRect(50, 80, 250, 150), 40 this); 41 } 위의프로그램은 6 개의부분으로나누어볼수있다. - 76 -
[2] 헤더파일 afxwin.h에대한 include를나타내고있는데, 이파일은 MFC에서사용되는타입정의, 클래스선언, 변수정의등을포함하고있으며, 윈도우 API 라이브러리사용을위한다른헤더파일들에대한 include들도포함하고있다. [3-8] MFC에서표준응용프로그램을나타내는 CWinApp 클래스를상속받아정의되는응용프로그램클래스 CHelloApp가선언되고있다. 이클래스를선언하는이유는 CWinApp 클래스에서상속받은가상함수 InitInstance() 를재정의하기위해서이며, 이함수는응용프로그램실행이시작될때호출된다. [10] 응용프로그램객체 HelloApp를전역변수로정의한다. 이프로그램의실행이시작되는곳이바로이전역변수의생성이다. 응용프로그램이메모리에읽혀들어가서실행이시작되면, 전역변수가생성되며 CHelloApp 클래스의기본생성자함수가실행되는데, 프로그램에서이함수를정의하고있지않으므로컴파일러가제공하는기본생성자를사용할것이다. 컴파일러가제공하는기본생성자는현재클래스에서정의되는멤버변수들에대한생성자호출과함께부모클래스인 CWinApp의기본생성자함수를호출하게된다. CWinApp의생성자함수는 InitInstance() 함수를호출하도록되어있는데, 이함수는가상함수이므로 CWinApp 클래스의 InitInstance() 함수가아니라 [18-26] 에정의되어있는 CHelloApp 클래스의 InitInstance() 함수를호출하게된다. [11-17] MFC 클래스인 CFrameWnd 클래스를상속받아정의되는 CHelloWindow 클래스가선언되고있다. 이클래스의인스턴스가이응용프로그램의주실행창의역할을한다. 새로운클래스를선언한것은생성자, 소멸자, 멤버변수등을구현할수있도록하기위함이다. [18-26] CHelloApp::InitInstance() 함수가구현되어있다. 이함수의역할은주실행창의생성을위해 CHelloWindow 클래스의인스턴스를생성하는일이며, 그결과로 [27-41] 에정의된생성자함수 CHelloWindow() 가실행된다. [27-41] 생성자함수 CHelloWindow() 가구현되어있으며, 이함수는창을생성한다음그내부에텍스트컨트롤을생성한다. 이프로그램에서유의할점은 C/C++ 프로그램의실행시작위치를나타내는 main() 함수도없고, 또예전윈도우응용프로그램의시작위치를나타내던 WinMain() 함수도없다는점이다. 그리고 WinMain() 함수의주요내용이었던이벤트처리루프도보이지않는다. 이러한내용들은모두 CWinApp 클래스에구현되어숨겨져있다. 또창의최대화, 최소화, 이동등을수행할수있는데, 이를위한코드들은 CFrameWnd 클래스안에숨겨져있다. 아래에서는위의프로그램을구성하는 3 개의객체를중심으로더욱자세히살펴본다. - 77 -
응용프로그램객체 모든 MFC 프로그램은 CWinApp 클래스에서파생된응용프로그램객체한개를갖는다. 이객체는반드시전역변수로선언되어야한다. CWinApp 클래스에서파생된객체는주실행창의생성을포함하는응용프로그램의초기화를수행하며, 이벤트처리루프를갖는다. 주실행창은응용프로그램마다다르며, 따라서이를위한코드작성이필요한데, 이를위해재정의되는함수가가상함수 InitInstance() 이다. MFC 프로그램에서 CWinApp 객체를사용하지못하고그파생클래스를사용하는이유가여기에있다. [3-8] 의코드가파생클래스 CHelloApp를선언하고있는데, CWinApp 클래스를상속받는다는사실과 InitInstance() 함수를재정의할것임을선언하고있다. 3 // Declare the application class 4 class CHelloApp : public CWinApp 5 { 6 public: 7 virtual BOOL InitInstance(); 8 }; [18-26] 에구현되어있는 InitInstance() 함수는 CHelloApp의데이터멤버의하나이며주실행창객체에대한포인터로사용되는변수 m_pmainwnd를사용하여창을생성하여화면에표시한다. CWinApp는 m_pmainwnd 외에도여러다른데이터멤버들을가지고있는데, 예를들면 m_pszappname( 응용프로그램이름 ), m_lpcmdline( 응용프로그램실행을위한명령 ), m_ncmdshow( 주실행창의초기모습 ) 등인데, 이변수들은응용프로그램객체가생성되면서프로그램등록정보로부터받아온적절한값으로초기화된다. 18 // The InitInstance function is called each 19 // time the application first executes. 20 BOOL CHelloApp::InitInstance() 21 { 22 m_pmainwnd = new CHelloWindow(); 23 m_pmainwnd->showwindow(m_ncmdshow); 24 m_pmainwnd->updatewindow(); 25 return TRUE; 26 } 25번행에서 TRUE를리턴하는것은초기화가성공적으로완료되었음을의미한다. 만일여기에서 FALSE를리턴한다면프로그램은즉시종료된다. - 78 -
창객체 MFC는주실행창으로사용될수있는창유형으로크기변경이가능한프레임윈도우와크기가고정된다이얼로그윈도우등두가지를정의하고있다. 이예제에서는프레임윈도우를사용하고있다. 아래코드는 CFrameWnd 클래스의파생클래스 CHelloWindow를선언하고있다. 11 // Declare the main window class 12 class CHelloWindow : public CFrameWnd 13 { 14 CStatic* cs; 15 public: 16 CHelloWindow(); 17 }; 파생클래스에는새로운생성자함수와컨트롤객체에대한포인터변수가포함되어있다. 응용프로그램마다주실행창안에각자고유한컨트롤들을가질것이며, 이를위해새로운생성자함수를필요로한다. 이클래스에서는정의되어있지않지만, 생성자함수에서생성된객체들을삭제하기위한소멸자함수를정의할수도있다. 그러나주실행창이소멸되는상황이라면일반적으로전체프로그램이종료될것이므로동적으로할당된객체들을따로삭제할필요는없다. 그리고창자체나창안의컨트롤들을통해발생된이벤트들을처리하는함수도이클래스안에표시될수있으며, 5절에서그러한예들을살펴볼것이다. 대부분의윈도우응용프로그램들은한개씩의주실행창을갖는다. 따라서 CHelloApp 클래스도주실행창을가리키는데이터멤버 m_pmainwnd를갖고있는데, 이변수는 CWinThread에서상속된데이터멤버이다. 주실행창의생성을위해 InitInstance() 함수는 22 번행에서와같이 CHelloWindow 클래스의인스턴스를생성하고 m_pmainwnd 변수로하여금그창객체를가리키게만든다. 18 // The InitInstance function is called each 19 // time the application first executes. 20 BOOL CHelloApp::InitInstance() 21 { 22 m_pmainwnd = new CHelloWindow(); 23 m_pmainwnd->showwindow(m_ncmdshow); 24 m_pmainwnd->updatewindow(); 25 return TRUE; 26 } - 79 -
창객체의생성으로끝나는것이아니라새로운창을화면에적절히나타나게만들기위해두가지작업이더필요하다. 창객체에대해 ShowWindow() 함수를호출하여창자체를화면에나타나게하는작업, 그리고 UpdateWindow() 함수를호출하여창의클라이언트영역의내용물을새로그리게함으로써창안의컨트롤등이정확히나타나게만드는작업이다. 이함수들은 CFrameWnd의부모클래스인 CWnd 클래스의멤버함수들이다. (CWnd는 200개가넘는멤버함수들을갖고있는방대한클래스이다.) CWnd::ShowWindow() 함수의파라미터는창을어떤모습으로나타나게할것인지를지정하는데, SW_HIDE( 창감춤 ), SW_RESTORE( 원래크기복원 ), SW_SHOWMAXIMIZED( 최대크기 ), SW_SHOWMINIMIZED( 최소화, 작업표시줄아이콘 ), SW_SHOWNORMAL( 정상크기 ) 등의여러가지값을사용할수있다. 여기에서는프로그램등록정보로부터초기화된 m_ncmdshow를사용하고있다. 22번행에서는 new 연산자를사용하여 CHelloWindow 클래스인스턴스를생성하고있으며, 이클래스의생성자인 CHelloWindow() 가호출된다. 이생성자는먼저 CFrameWnd 클래스의멤버함수 Create() 를호출하여프레임윈도우를생성한다. 27 // The constructor for the window class 28 CHelloWindow::CHelloWindow() 29 { 30 // Create the window itself 31 Create(NULL, 32 _T("Hello World!"), 33 WS_OVERLAPPEDWINDOW, 34 CRect(0, 0, 300, 200)); Create() 함수에는 4개의파라미터가전달된다. 첫번째파라미터 NULL은디폴트클래스인 CWnd에서정의된속성구조가사용될것임을표시한다. 두번째파라미터는창의타이틀바에나타날텍스트이다. 세번째파라미터는창의스타일속성을나타내는값인데, 이창은다른 ( 응용프로그램의 ) 창에의해가려질수있는정상적인창임을나타낸다. 네번째파라미터는이창의위치와크기를나타내기위해사각영역을정의하는 CRect 클래스의생성자를사용하고있다. 처음두값 (0, 0) 은사각영역의좌측상단의좌표, 마지막두값 (300, 200) 은사각영역의우측하단의좌표를나타낸다. 이는이창이전체화면의좌측상단에위치하며, 가로세로의크기가각기 300, 200임을의미한다. 텍스트컨트롤 CHelloWindow() 는프레임윈도우의생성에이어텍스트컨트롤객체를생성한다. 이객체를가리키기위한 CStatic*, 타입의변수는 CHelloWindow 클래스에서 private 데이터멤버로선언되어있다. 텍스트컨트롤객체를만드는작업은두단계로이루어진다. 먼저 CStatic 클래스의생성자 - 80 -
를호출하여필요한객체를생성한다음, CStatic::Create() 함수를호출하여그세부사항을정한다. 27 // The constructor for the window class 28 CHelloWindow::CHelloWindow() 29 { 30 // Create the window itself 31 Create(NULL, 50 32 _T("Hello World!"), 33 WS_OVERLAPPEDWINDOW, 34 CRect(0, 0, 200, 200)); 35 // Create a static label 36 cs = new CStatic(); 37 cs->create(_t("hello world"), 38 WS_CHILD WS_VISIBLE SS_CENTER, 39 CRect(50, 80, 250, 150), 40 this); 41 } 80 CStatic::Create() 함수를위한파라미터들은 CWnd::Create() 함수의경우와유사하지만약간의차이가있다. 첫번째파라미터는텍스트컨트롤이보여주는텍스트를나타내는스트링이다. 두번째파라미터는스타일속성들을나타낸다. WS_CHILD 스타일은이컨트롤이나타내는창은다른창내부에위치하며, 독자적으로존재하는것이아니라그창과함께이동하는창임을나타낸다. 이러한성격의창을자식창이라고하며, 이창을포함하는창은부모창이라고한다. WS_VISIBLE은이창이감추어져있지않고화면에보인다는것을나타낸다. SS_CENTER는이컨트롤의 ( 수평방향으로 ) 중앙의위치에텍스트가표시될것임을나타낸다. 세번째파라미터는텍스트컨트롤의위치와크기를나타낸다. 이컨트롤은주실행창의타이틀바부분과테두리선을제외한클라이언트영역이라불리는창내부에나타날수있으며, 따라서좌표값의기준위치도클라이언트영역의좌측상단이된다. 네번째파라미터는이컨트롤을자식창으로갖는부모창을가리키는포인터인데, 현재 CHelloWindow 클래스안이므로 this는 CHelloWindow 인스턴스인주실행창을가리킨다. 3.4 MFC 스타일 컨트롤은윈도우응용프로그램들을위한인터페이스역할에이용되는사용자인터페이스요소들이다. 많은응용프로그램에나타나는윈도우나다이얼로그박스들에서프로그램기능을적절히표현하고있는컨트롤들이주된요소들이다. 따라서윈도우운영체제들이제공하고있는컨트롤의용법을이해하는것은대단히중요한일이다. 컨트롤들은무엇을할수있는가, 컨트롤의외관이나동작의세부사항을어떻게변경할수있는가, 사용자의이벤트에대해컨 - 81 -
트롤이적절히반응하도록만들기위해서필요한작업은무엇인가등의내용들에대해이해할필요가있다. 이러한지식과다이얼로그나메뉴등의용법에관한지식을활용하여다양한윈도우응용프로그램을만들수있다. 표준컨트롤들을표현하기위한클래스에는 CStatic, CButton, CEdit, CList, CComboBox, CScrollBar 등의 6개가있다. 이들중에가장용법이단순한클래스는내용이고정된텍스트를출력하는 CStatic이다. CStatic 클래스는데이터멤버를갖지않으며, 멤버함수도생성자와 Create() 함수, 그리고텍스트대신아이콘, 비트맵, 커서이미지등의그림을처리하기위한몇개의함수정도이다. 이절에서는컨트롤의외관과동작을조정하기위한스타일의설명을위해 CStatic 클래스의예를살펴보며, 다음절에서는 CButton 클래스와 CScrollBar 클래스등을다룬다. 기초사항 CStatic 클래스는사용자에게내용이고정된텍스트메시지를보여주는것이주된용도이며, 이러한텍스트는어떤정보를전달하기위한메시지일수도있고, 다른컨트롤을설명하는레이블일수도있다. CStatic 객체들은텍스트외에사각영역, 사각형프레임, 아이콘, 비트맵등의다른모습으로나타날수도있다. 사각영역이나사각형프레임은연관성있는다른컨트롤들을그룹으로묶어보여주거나분리해서보여주기위해사용될수있다. 이러한외관을정해줄때사용하는것이스타일이다. CStatic 컨트롤은항상다른창의자식창형태로나타난다. 3절에서살펴본예제에서텍스트컨트롤을만들기위한 2 단계코드는다음과같다. CStatic *cs;... cs = new CStatic(); cs->create(_t("hello world"), WS_CHILD WS_VISIBLE SS_CENTER, CRect(50, 80, 150, 150), this); 생성자와 Create() 함수를차례로호출하는 2 단계작업은대부분의컨트롤들에서일반적으로사용되는절차이다. 생성자는객체표현을위한메모리를할당하고초기화작업을수행하며, Create() 함수는컨트롤창을만들어화면에나타나도록만드는역할을수행한다. 아래와같은프로토타입을갖는 Create() 함수는다섯개의파라미터를갖지만마지막파라미터는디폴트인수를갖고있으며, CStatic 컨트롤의경우보통다른값을지정하지않는다. BOOL Create(LPCTSTR lpsztext, DWORD dwstyle, const RECT& rect, CWnd* pparentwnd, UINT nid = 0xffff); - 82 -
여기서설명의초점이되는파라미터는 32비트정수를나타내는 DWORD 타입의 dwstyle이며, 컨트롤의스타일정보를표현한다. CStatic 스타일 컨트롤들은여러가지스타일을갖는데, 이값은 Create() 함수에게전달되는 dwstyle 파라미터에의해정해진다. 각스타일속성은 32 비트중어느한비트를이용하여표시된다. 여러속성을결합하기위해서는비트단위 OR 연산자 를사용한다. 예를들면, 위에서사용된스타일속성및이들을결합한결과는아래와같다. WS_CHILD: 01000000 00000000 00000000 00000000 WS_VISIBLE: 00000001 00000000 00000000 00000000 SS_CENTER: 00000000 00000000 00000000 00000001 WS_CHILD WS_VISIBLE SS_CENETR: 01000001 00000000 00000000 00000001 텍스트컨트롤은일종의창이며, CStatic 클래스는 CWnd 클래스의자식클래스이다. 따라서 CStatic 클래스에고유하게적용되는스타일도있고 CWnd 클래스에적용되는스타일중 CStatic 클래스에사용될수있는것들도있다. CWnd 클래스에서상속받은스타일 WS_CHILD: 다른창의자식창임을나타내며, CStatic에서는필수스타일 WS_VISIBLE: 화면에보임을나타내며, 대개의경우사용됨. WS_DISABLED: 사용자로부터의이벤트에반응하지않음을나타내는데, 텍스트컨트롤의경우원래반응하지않으므로대체로불필요함. WS_BORDER: 컨트롤에경계선이그려짐. "WS" 는 Window Style 을나타낸다. CStatic 에고유한스타일 아래스타일상수에서 "SS" 는 Static Style 을나타낸다. 텍스트위치관련스타일 SS_CENTER: 텍스트를가운데정렬하여표시한다. 필요하면여러행을사용한다. - 83 -
SS_LEFT: 텍스트를왼쪽정렬하여표시한다. 필요하면여러행을사용한다. SS_RIGHT: 텍스트를오른쪽정렬하여표시한다. 필요하면여러행을사용한다. SS_LEFTNOWORDWRAP: 텍스트를왼쪽정렬하여표시한다. 한줄에들어가지않는부분은표시하지않는다. 사각영역및프레임표시스타일 아래의스타일중의하나가사용될경우 Create() 함수의첫번째파라미터로주어지는텍스트스트링은무시된다. SS_BLACKFRAME: 윈도우프레임의배경색과같은프레임을그린다. 디폴트색상은 BLACK이다. SS_BLACKRECT: 윈도우프레임의배경색과같은사각영역을그린다. 디폴트색상은 BLACK이다. SS_GRAYFRAME: 바탕화면의배경색과같은프레임을그린다. 디폴트색상은 GRAY 이다. SS_GRAYRECT: 바탕화면의배경색과같은사각영역을그린다. 디폴트색상은 GRAY 이다. SS_WHITEFRAME: 일반창의배경색과같은프레임을그린다. 디폴트색상은 WHITE이다. SS_WHITERECT: 일반창의배경색과같은사각영역을그린다. 디폴트색상은 WHITE 이다. 기타 : SS_SIMPLE, SS_NOPREFIX, SS_USERITEM, SS_ICON, SS_BITMAP, SS_CENTERIMAGE CStatic 객체에서 WS_CHILD와 WS_VISIBLE은거의항상나타난다. WS_DISABLED을사용하는것은대부분의경우의미가없다. 그외의모든속성은레이블의외관에영향을미치는선택항목이다. CStatic 텍스트외관 아래코드는 3 절의소스코드에서생성자함수 CHelloWindow() 만조금고친것이다. CHelloWindow::CHelloWindow() { // Create the window itself Create(NULL, _T("CStatic Tests"), WS_OVERLAPPEDWINDOW, CRect(0, 0, 300, 200)); // Get the size of the client rectangle - 84 -
CRect r; GetClientRect(&r); r.inflaterect(-40, -20); } // Create a static label cs = new CStatic(); cs->create(_t("hello world"), WS_CHILD WS_VISIBLE WS_BORDER SS_CENTER, r, this); 이코드에서의주요변화는텍스트컨트롤의위치와크기를나타내기위해 CRect(50, 80, 250, 150) 을사용하는대신, 주실행창클라이언트영역안에상하좌우각기 20 픽셀씩의여백을갖는사각영역을계산하여사용하는것이다. 이를위해 CRect 타입의변수 r을선언한다음, CHelloWindow::GetClientRect(&r) 함수를호출하고있다. GetClientRect() 함수는원래 CWnd 클래스에서상속된것인데, 창의클라이언트영역을나타내는좌표들을 CRect 객체에받아오기위해사용된다. CRect::InflateRect() 함수는 2개의정수파라미터를취하는데, 각기좌우및상하방향으로사각영역을확대하기위해사용된다. 여기서는실인수로각기 -20을사용하였으므로확대대신축소된다 (CRect::DeflateRect() 함수참고 ). 또다른변화는 WS_BORDER 스타일을 CStatic::Create() 함수의스타일파라미터로추가한것이다. 이프로그램의실행결과를오른쪽에보인다. cs->create() 함수에서한줄에표시될수없는긴스트링을사용한예제와실행결과는다음과같다. cs->create(_t("now is the time for all good men to come to the aid of their country"), WS_CHILD WS_VISIBLE WS_BORDER SS_CENTER, r, this); SS_LEFTNOWORDWRAP 의효과를보이기위한예제와실행결과는다음과같다. - 85 -
cs->create(_t("now is the time for all good men to come to the aid of their country"), WS_CHILD WS_VISIBLE WS_BORDER SS_LEFTNOWORDWRAP, r, this); SS_BLACKFRAME의효과를보이기위한예제와실행결과는다음과같다. cs->create(_t("hello world"), WS_CHILD WS_VISIBLE SS_BLACKFRAME, r, this); SS_GRAYRECT의효과를보이기위한예제와실행결과는다음과같다. cs->create(_t("hello world"), WS_CHILD WS_VISIBLE SS_GRAYRECT, r, this); 폰트 CStatic 객체에서사용되는폰트를변경하기위해서는 CFont 객체를이용한다. CStatic 객체를먼저생성한후, CFont 객체를생성한다. CFont::CreateFont() 함수를사용하여폰트를정한다음, CWnd::SetFont() 함수를사용하여새로정해진폰트를 CStatic 객체와연결해준다. CreateFont() 함수의프로토타입은아래와같다. BOOL CreateFont(int nheight, int nwidth, int nescapement, int norientation, int nweight, BYTE bitalic, BYTE bunderline, BYTE cstrikeout, BYTE ncharset, BYTE noutprecision, BYTE nclipprecision, BYTE nquality, BYTE npitchandfamily, LPCTSTR lpszfacename); 이중자주사용되는파라미터 3개만살펴보자. nheight = 36은포인트단위의글자크기를나타내는데, 10 포인트가일반텍스트용글자크기이며, 이책의본문글자크기는 11 포인트를사용하고있다. nweight = 700은글자의굵기를나타내는데, 400이면보통굵기이며 700 이면진한글씨체 (boldface) 이다. lpszfacename은윈도우운영체제에서사용될수있는글꼴의종류를나타내는데, Arial, Courier New, Symbol, Times New Roman 등등의수많은글꼴들이있다. - 86 -
CHelloWindow::CHelloWindow() { CRect r; // Create the window itself Create(NULL, _T("CStatic Tests"), WS_OVERLAPPEDWINDOW, CRect(0, 0, 300, 200)); // Get the size of the client rectangle GetClientRect(&r); r.inflaterect(-40, -20); // Create a static label cs = new CStatic(); cs->create(_t("hello World"), WS_CHILD WS_VISIBLE WS_BORDER SS_CENTER, r, this); } // Create a new 36 point Arial font CFont *font = new CFont(); font->createfont(36, 0, 0, 0, 700, 0, 0, 0, ANSI_CHARSET,OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH FF_DONTCARE, _T("Arial")); // Cause the label to use the new font cs->setfont(font); 3.5 메시지맵 (message map) 응용프로그램에서창안에배치한사용자인터페이스요소는두가지제어가능한특성을갖는데, 이들은외관과이벤트반응동작이다. 4절에서 CStatic 컨트롤의예를통해사용자인터페이스요소의외관을조정하기위해스타일속성을이용한다는것을살펴보았다. 이러한방식은 MFC 안의모든컨트롤클래스들에적용된다. 이절에서는메시지맵과간단한이벤트처리의이해에도움을주기위해먼저 CButton 컨트롤을살펴본다. 또컨트롤이아니라창자체에서발생하는이벤트들에대한처리방법도알아본다. 그런다음조금더복잡한예로 CScrollBar 컨트롤의경우를알아볼것이다. - 87 -