본문 바로가기

TIPS/MFC

[TIPS] 10th 20160804

10번째 강좌 정리입니다~~


<MFC>


C를 C++로 바꿀 때는 클래스 작업을 해야한다. 

클래스 작업을 할 때에는 소스에서 무엇이 바뀌고 무엇이 바뀌지 않는지 파악해야한다.


아래 대표님블로그에 더 자세한 설명이 있다.

http://blog.naver.com/tipsware/220070159066

C++에서는 마음에 안드는 부분은 class를 만들어두고 상속해서 오버라이팅하는 방법이 있다.




MFC에서는 작업을 크게 두가지로 나눌 수 있다.

-동작부분(윈도우프로시드와 크리에이티브부분)

-프로그램을 등록하고 메세지 처리하는 부분(윈메인에서 크리에이티브부분 빼고 다)


<클래스 이름 정하기>


MFC는 다 클래스 이름을 C를 붙여서 사용한다.


<멤버변수>


바뀌는 부분을 다 변수로 만들어둔다.


unsigned short InitApplication(); 이 부분에 윈도우 클래스 등록하는 것을 넣는다.

unsigned short InitInstance(); 라는 함수도 만들어준다. (메인윈도우만들기)


<메세지 처리부분>


unsigned int Run(); 메세지 처리부분







MFC;


CWinApp 


역할 1. 어플리케이션 등록

역할 2. 메인윈도우만들기

역할 3. 메세지 처리하기


예전엔 CWinApp에 run이 있었는데 지금은 CWinThread라는 클래스에 Run이 넘어갔다. 하지만 CWinApp에 다 있다고 생각하자!


어플리케이션을 만들면 기본적으로 thread는 하나만 사용한다. 한 프로그램이 이론적으로는 thread를 1700개를 만들 수 있다. API를 한다면 createthread라는 함수를 사용해서 Thread를 추가해 효율적으로 사용할 수 있다.


'InitApplication'은 어플리케이션을 등록하는 함수이다. 이 함수는 오버라이딩하는 경우가 거의 없다고 볼 수 있다.


'InitInstance'는 윈도우를 만드는 함수이다. 100% 오버라이딩한다. 

내용은 비어있기 때문에 반드시 사용자가 이 클래스로부터 상속을 받아 재정의하여 createwinodw, show, update window 코드를 직접 써야한다. 하지만 스켈레톤코드는 자동적으로 만들어지긴 하기 때문에 직접 우리가 칠 필요는 없다.


'Run'은 메세지를 처리하는 함수이다. 이 역시 오버라이딩 할 일이 없다.



Init Application는


class MyApp : CWinApp에서 상속받음.

{

Initapplication(){

---}

InitInstance()

{

---}

}

이런식으로 사용할 수 있다.



CWnd


CWinApp과 이 클래스 두가지만 있어도 어플리케이션은 작동이 가능하다.


Cwnd는 안에 멤버함수가 어마어마하게 많다. (예를 들어 WndProc담당..!) 저번 시간에 API할때는 펜과 브러쉬 설정시 if문을 돌려서 일일이 설정해주었는데, 여기엔 클래스마법사가 있어서 자동으로 다 해준다. 이 클래스를 만들면 x와 캡션만 있는 창이 하나 만들어진다. 나머지는 우리가 직접 설정하고 추가해주어야 한다.



CToolBar : 라는 클래스가 공식적으로 등록됐다. (예전엔 speed bar라고 프로그래머가 직접 만들어쓰던 것.)


CStatusBar : 하단의 상태바 역시 제공되게 되었다.


CView : 원래는 툴바와 상태바 모두 클라이언트창 부분이다. 툴바와 상태바를 만들게되면 실질적으로 사용하는 클라이언트창 부분의 좌표가 복잡하게 꼬이게 된다.(툴바의 좌측 상단이 좌표가 (0,0)이기 때문에.) 그래서 툴바와 상태바 사이에 윈도우를 새로 만들어넣어 (0,0)을 다시 정의하게 되었다. 


CFrameWnd : CWnd의 자식클래스로 앞서 말한 툴바, 상태바, CView의 크기를 관리해준다.



우리가 가장 많이 사용하는 것은 CWinApp, CFrameWnd, Cview 이렇게 세가지라고 할 수 있다.



CDocument : 열기 저장 등 파일, 데이터 입출력을 담당한다. CView와 병렬적인 관계로 함께 사용한다.




<클래스가 만들어지는 과정>


CWinApp -> CFrameWnd -> CView / CDocument    


MS에서는 이 구조를 SDI(single document interface)라고 부른다.

따라서 프로젝트 생성시 SDI를 선택하면 자동으로 만들어진다. (SDI와 반대로 MDI는 document가 여러개이다.)


CDocument를 제대로 사용할 수 있는 사람들이 흔치 않다.

CView와 CDocument는 병렬관계로, 절차지향을 생각한다면 이해하기 어려운 부분일 수 있다.


CFileDialog에 확장자를 선택하는 것이 제공되어 있다. 그래서 사용자들이 편의상 CDocument에서 사용하지 않고 CView에서 사용해버리다보니 프로그래머들에게 CDocument가 버려지게 된다. 그러다 보니 visual studio 6.x 부터 이는 옵션으로 바뀌게 되었다.


따라서 MFC는 Framework 방식임을 알 수 있다. (부분적으로 교체가능한 방식) 

MS가 틀로 다 만들어두었고, 프로그래머가 부분적으로 상속 후 오버라이딩해서 사용, 즉 MS가 만들어둔 SDI에서 부분적으로 바꾸는 프레임워크이다.


CDialog : 대화상자를 만들어주는 클래스이다. CWnd에서 상속받는다. (과거에는 대화상자가 없어서 프로그래머가 좌표를 다 계산해서 만들었다.)


사람들이 "CWinApp -> CFrameWnd -> CView / CDocument"대신 간단한 "CWindApp + CDialog"를 쓰기 시작하였다.(Dialog based program) 이 역시 많이 통용되다보니 MS에 받아져서 옵션으로 사용 할 수 있게 되었다.




<MFC 프로젝트 만들기>





다음과 같은 창이 뜨게된다.




이제 소스파일에 "파일명.cpp"로 들어간다.



InitInstance()함수에서 

이렇게 남기고 나머지는 모두 지워준다.


DoModal() 함수를 빠져나오지 않으면 메세지루프를 들어가지 못한다. 그래서 변칙적으로 DoModal안에 메세지루프가 들어간다.

실제로 이렇게 해버리면 프로그램 종료시 비정상종료라고 남는다. 하지만 우리는 배우기 쉬우니까 이렇게 작성하기로 한다.






지난시간 API와 달리 MFC는 콜백함수를 손쉽게 추가할 수 있다.

예시로,

프로젝트>클래스마법사 : 메세지탭

메세지탭에서 LButtondown을 더블클릭 > 코드편집을 눌러면 함수가 생성된다.

참고로 프로젝트>클래스마법사 : 클래스이름에 Dlg로 선택되어있어야 한다. 아닐 경우도 있으니 꼭 확인해준다.


UINT nFlags : 조합키정보가 저장된다

CPoint point : 좌표값이 저장된다. 



dc는 윈도우에 종속된 개념으로, dc정보는 윈도우에 있다.

대화상자는 CWnd에 있기 때문에 이 윈도우의 this를 호출하는 것이다. 이 한줄이면 getdc, realesdc 는 없어도 된다.



마우스 왼쪽버튼을 누르면 사각형이 그려지고, 컨트롤키와 함께 누르면 원이 그려지는 코드를 작성해보자.




<오목_(1)>

1. 위에서 InitInstance()함수 정리까지 같은 과정을 해준다.

- 오목판 만들기

2. 프로젝트명Dlg.cpp에서 Onpaint()를 찾아준다.

3. else부분에 아래와 같이 코드를 작성한다.

CPaintDC dc(this);
for (int y = 0; y < 12; y++) {
for (int x = 0; x < 12; x++) {
dc.Rectangle(40 + x * 40, 40 + y * 40, 40 + x * 40 + 41, 40 + y * 40 + 41);
}
}
//CDialogEx::OnPaint();


-코드설명-
CPaintDC dc(this); : CClientDC의 경우에는 wm_paint flag를 1에서 0으로 바꾸는 기능이 없다. 따라서 OnPaint에서는 CPaintDC를 사용해야한다! 그 외의 함수에서는 CClientDC를 사용하면 된다.
dc.Rectangle(40 + x * 40, 40 + y * 40, 40 + x * 40 + 41, 40 + y * 40 + 41); : 격자를 만들기 위해 x와 y를 선언하여 for문을 돌린다. 격자는 사각형을 이용해서 만들어본다. 이와 같은 좌표는 익숙해지기 위해서 많이 그려봐야한다!
//CDialogEx::OnPaint(); : 주석처리를 하면 wm_paint 루틴을 계속 돌게된다. 하지만 위에 CPaintDC를 사용하였으니 문제없다.



-돌 놓기

4. 먼저 Dlg헤더파일에 돌의 색을 결정할 변수를 private로 하나 추가해준다.
private:
int m_dol_state = 0; //0->흑, 1->백

5. 마우스 왼쪽버튼을 눌렀을 때 동작할 함수 "OnLButtonDown"를 클래스마법사를 통해 만들어준다. 함수는 이렇게 구성해준다.

CClientDC dc(this);
int x = (point.x + 20) / 40;
int y = (point.y + 20) / 40;

x = x * 40;
y = y * 40; 
/*바둑돌을 교차점에만 둘 수 있도록 만든 코드이다. 마우스로 클릭한 부분이 (44,44)라면 가장 가까운 부분인 교차점(1,1)인 부분에 돌이 놓어야 한다. 따라서 x와 y에 20(격자크기는 40이므로 반으로 나눈 20사용)을 더해 격자크기인 40으로 나누어 주면 교차점(1,1)로 나오게 된다. 교차점좌표를 실제좌표로 변환하기 위해 40을 곱해주면 된다. 이러한 수학적인 생각을 잘 해야한다!!*/
CBrush *p_old_brush;
if (m_dol_state == 0) {
p_old_brush = (CBrush *)dc.SelectStockObject(BLACK_BRUSH);
}
else {
p_old_brush = (CBrush *)dc.SelectStockObject(WHITE_BRUSH);
//오버로딩이 되지 않는 하나의 함수로 되어있기 때문에 강제 캐스팅해서 사용한다.
}
dc.Ellipse(x - 20, y - 20, x + 20, y + 20);
dc.SelectObject(p_old_brush);
m_dol_state = !m_dol_state;
CDialogEx::OnLButtonDown(nFlags, point);

여기까지 작성하면 아래와 같이 돌을 놓을 수 있다.







www.tipssoft.com


'TIPS > MFC' 카테고리의 다른 글

[TIPS} 15th 20160825  (0) 2016.08.28
[TIPS] 13th 20160818 (list box / socket)  (0) 2016.08.20
[TIPS] 11th 20160808  (0) 2016.08.20