1. CWnd 클래스
● MFC = Microsoft Foundation Class
: 마이크로 소프트사가 만든 c++기반의 윈도우 라이브러리, 객체 지향적인 구조를 갖는다.
● CWnd 클래스
: MFC에서 모든 윈도우 클래스의 기본 클래스
● 상속관계
CObject > CCmdTarget > CWnd
CCmdTarget: MFC의 메시지맵과 관련된 코드가 구현된 클래스
● View에 새로운 윈도우를 보이게 하는 법
: View.h에 CWnd를 멤버 변수로 선언하고 View.cpp의 OnCreate함수내부에서 CWnd의 Create함수를 이용하여 생성한다. (Edit 형태 or Button형태)
● 부모 윈도우 설정 방법
- 윈도우의 부모 윈도우 설정 (CWnd.SetParent(부모윈도우)) //부모윈도우가 NULL이면 바탕화면에 윈도우생성
2. MFC 코드의 기본 구조
● 기본적인 SDI 화면 구성
- CMainFrame(기본 프레임윈도우) - CFrameWnd를 상속 받은 것
- CToolBar(도구모음)
- CStatusBar(아래의 상태표시줄)
- Menu(메뉴)
- CCreateDemoView(하얀 화면)
- CWnd( 사용자가 선언한 멤버변수. 새로운 윈도우)
● 기본적으로 생성되는 클래스
- Doc : CDocument 상속
- App: CWinApp 상속
- View: CView 상속 (BaseView설정을 CView로 했을 때)
- MainFrm : CFrameWnd상속
*App과 Doc클래스는 우리눈에 보이는 윈도우 클래스가 아니라
각각 응용프로그램 자체와 문서를 객체화한 클래스이다.
● CMainFrame
- SDI 최상위 프레임 윈도우
- CFrameWnd를 상속받아 만든다
- 도구 모음과 상태표시줄을 자식 윈도우로 갖고 cpp onCreate에서 생성 및 docking한다.
● CFrameWnd
- 프레임(Frame)이 있는 윈도우가 가져야할 기본기능을 정의한 클래스
- 부모윈도우가 없는 최상위 윈도우가 될 수 있는 윈도우
- 상속관계 : CObject> CCmdTarget>CWnd>CFrameWnd
● CWinApp 클래스
- 상속관계 : CObject>CCmdTarget> CWinThread > CWinApp
- CWinThread를 상속받은 걸보아도 알 수 있듯 이 클래스는 응용프로그램 자체를 객체화한 클래스이다.
- 가상함수: InitInstance(), ExitInstance(), Run()
* 프로그램 시작과 동시에 메모리를 할당한다든지 시스템 리소스를 할당받아야 한다면 이런 코드는 InitInstance에 넣고 메모리나 시스템 리소스를 반환하는 코드는 ExitInstance에 넣는 것이 보편적
- App.cpp파일에 CWinApp클래스를 상속받은 클래스가 전역 객체 theApp으로 생성되어있다.
- theApp객체의 생성은 프로그램의 실행을 의미
- InitInstance()에서 Doc, CMainFrame, View를 순차적으로 생성함
● CDocument 클래스
- 상속관계: CObject> CCmdTarget> Document
- 문서를 관리하는 역할
- 가상함수: OnNewDocument()
L SDI응용프로그램이 실행되면 반드시 한번 실행된다. 처음 문서를 처리하는 객체를 생성하면 아무런
내용이 없는 빈문서(새파일)를 열도록 하고 있기 때문
- 대표적 함수: OnOpenDocument(), OnCloseDocument(), OnSaveDocument()
* 메모장 프로그램이 대표적인 SDI 문서형식의 응용프로그램이다.(Base Class를 CEditView로 설정하면 메모장과 같은 윈도우가생성된다.)
●빈번하게 사용하는 MFC전역함수
-AfxMessageBox(): 메시지 상자의 출력
-AfxGetMainWnd(): 최상위 프레임 윈도우의 포인터 반환
-AfxGetInstanceHandle(): 응용프로그램의 인스턴스 핸들 반환
●CDocument의 SetModifiedFlag(BOOL)메서드
-문서가 변경되었음을 설정/해제하는 역할
(문서가 변경되었는데도 저장을 하지 않고 종료하는 경우 문서의 내용이 사라지지 않게 저장을 유도하는 장치)
● MFC 동작 원리
1. 모든 윈도우가 CWnd클래스를 상속받아 만들어진다.
2. 개발자는 윈도우의 함수를 다시 재정의 하여 사용
3. 이 함수를 특정 메시지에 대응하도록 메시지 맵을 이용하여 연결
4. 특정 메시지가 발생
5. 재정의한 함수 호출(재정의된 상위계층의 함수를 명시적으로 호출)
6. MFC의 코드와 우리가 작성한 코드 모두가 동작하게 됨
● 메시지 맵
- 특정 메시지가 발생했을 때 어떤 함수를 호출해야하는지 명시하는 매크로의 집합체
3. MFC 코드의 흐름
● CWinApp클래스를 상속받은 App클래스에서 InitInstance()가 호출되는 과정
1. 프로그램 실행 초기에 App생성자 호출
2. _tWinMain()이 호출
3. AfxWinMain()이 호출 > 여기서 InitInstance() 호출
● 메시지 처리
- CWinApp 클래스의 Run()함수가 호출되어 MFC응용프로그램의 메인 메시지 루프가 작동한 이후의 상황
- 보통은 메시지 핸들러 함수를 등록하여 처리하는 것이 일반적
- 직접 메시지를 발생시키는 경우도 있음(PostMessage, SendMessage)
● 메시지를 처리하는 방법
- 메시지 핸들러 함수 등록
- 부족한 경우 WindowProc()함수를 재정의하여 처리
- 메시지를 필터링 하고 싶다면 PreTranslateMessage() 함수 재정의
- PostMessage(): 메시지 큐에 메시지 추가하는 함수
- SendMessage(): 메시지 핸들러 함수를 호출하는 함수
4. 키보드 입력
● 키보드 메시지
- WM_KEYDOWN: SYSTEM키를 제외한 모든 키를 누르면 발생 - 핸들러함수: onKeyDown()
- WM_KEYUP
- WM_SYSTEMDOWN : Alt키 또는 F10키를 누르면 발생 - 핸들러 함수: onSysKeyDown()
- WM_SYSTEMUP
● WM_CHAR
1. 문자키(ASCII코드의 문자에 해당하는 값)를 누르게되면 제일먼저 WM_KEYDOWN메시지가 발생
2. TranslateMessage()함수를 거쳐서 WM_CHAR메시지로 전환
- 메시지 루츠에서 WM_KEYDOWN메시지를 분석한 수 ASCII코드 문자에 해당하는 키보드 입력이면 추가로 발생하는 메시지.
- WM_KEYDOWN, WM_CHAR 메시지 연이어 발생
- WM_CHAR의 메시지 핸들러 함수: onChar()
*굳이 onChar함수를 이용하지 않고 keydown메시지의 핸들러 함수를 이용해서 처리해도 되지만 그렇게하면 OnKeyDown()함수의 길이가 너무 커질 수 있으믈 특수키의 입력과 문자키의 입력을 구분하여 처리하는 것이 바람직
● WM_SYSCHAR
- WM_CHAR와 마찬가지로 TranslateMessage()함수가 생성
- Alt+문자키 입력 조합의 경우 발생 ex) Alt+s
● CString 클래스
- MFC가 제공하는 거의 유일한 문자열 처리 클래스
- 유니코드 문자열을 처리하고 일반 멀티 바이트 문자열을 처리하는 코드나 함수를 따로 분리하지 않아도 됨
(C보다 훨씬간단)
- 더하기 연산자나 할당 연산자가 정의되어 있음
- 문자열처리에 필요한 메모리를 알아서 관리 >> 메모리 할당/해제 코드 작성 필요x
(char배열을 사용하는 처리보다는 CString 클래스를 활용하는 것이 여러모로 유익)
*enter키를 예전에는 return키라고 부름 (남아있는 흔적 \r)
5. 마우스 입력
● 마우스 메시지
- WM_MOUSEMOVE
- WM_LBUTTONDOWN/WM_LBUTTONUP
- WM_LBUTTONDBCLK
- WN_MOUSEWHELL
● 더블클릭에서 발생하는 메시지
- WM_LBUTTONDOWN>WM_LBUTTONUP>WM_LBUTTONDBCLK>WM_SBUTTONUP
● 윈도우를 다시 그리는 OnPaint()
- WM_PAINT 메지시의 핸들러함수
- RedrawWindow()함수를 호출하면 내부에서 WM_PAINT메시지를 발생시킴.
(그러므로 윈도우를 업데이트해서 다시그리고싶으면 RedrawWindow()함수를 호출하면됨)
● 마우스 이벤트를 추적하는 방법
- Drag& Drop을 실행했을 때 윈도우 바깥에서 마우스버튼 UP을 해도 윈도우로 다시 돌아오면 외부에서 UP한 것을 인식하지 못했기 때문에 계속해서 마우스 Down되어있다고 인식을한다.
이를 해결하기위한 이벤트 추적방법이 아래 두가지가 있다.
방법1. SetCapture() >ReleaseCapture()
- 응용프로그램 자신이 직접 마우스를 점유하고 계속 마우스 메시지를 수신하여 경계를 벗어났는지 검사
- 윈도우 영역을 벗어나더라도 계속해서 마우스 메시지를 수신함
- 자신의 윈도우를 넘어 바탕화면이나 다른 프로그램으로 드래그 앤 드롭이 가능한 경우 캡쳐방법이 최선
- 이 방법은 드래그 앤 드롭에 사용하는 것으로 한정!
(특정 윈도우가 마우스를 선점하여 놓아주지 않으면 운영체제 입장에서는 큰 부담으로 작용하기 때문)
- 사용법: *LButton이 눌러질 때* SetCapture()함수를 사용하고, 떼어질때 ReleaseCapture()로 캡쳐를 품
- 부작용: 드래그하고 윈도우 밖에서 드롭을 할 경우 윈도우에서 아예 사라진 상태가 됨
방법2. _TrackMouseEvent() 함수 사용
- 운영체제에 자신의 윈도우를 등록하여 마우스가 자신의 영역을 벗어나면 메시지(WM_MOUSELEAVE)를
수신하여 경계가 벗어났음을 판단. (마우스를 캡쳐할 필요x(점유안함),오동작 가능성줄어듬)
사용법:
1) 마우스가 움직일 때( onMouseMove (WM_MOUSEMOVE 메시지의 핸들러함수)에서) 함수 사용
if(m_bTrack==FALSE){
TRACKMOUSEEVENT MouseEvent;
::ZeroMemory(&MouseEvent, sizeof(MouseEvent));
MouseEvent.cbSize =sizeof(MouseEvent);
MouseEvent.dwFlags =TME_LEAVE; //메시지를 수신하도록 함
MouseEvent.hwndTrack =m_hWnd; //CWnd클래스의 멤버. 윈윈도우 객체의 핸들
MouseEvent.dwHoverTime =0;
m_bTrack::_TrackMouseEvent(&MouseEvent); //TRUE를 반환하면 추적시작
}
2) 마우스가 윈도우에서 벗어날 때( onMouseLeave(WM_MOUSELEAVE 메시지의 핸들러함수)에서) 함수 사용
TRACKMOUSEEVENT MouseEvent;
::ZeroMemory(&MouseEvent, sizeof(MouseEvent));
MouseEvent.cbSize =sizeof(MouseEvent);
MouseEvent.dwFlags =TME_CANCEL; //이벤트 추적을 멈추라는 메시지
MouseEvent.hwndTrack =m_hWnd;
::_TrackMouseEvent(&MouseEvent); //TRUE를 반환하면 추적시작
m_bTrack =FALSE;
* WM_MOUSEHOVER
: 마우스 포인터가 윈도우 영역으로 돌아오는 순간에 한번만 발생
dwFlags에서 TME_HOVER 옵션을 사용할 때 이 메시지를 수신할 수 있음
*WM_MOUSEMOVE메시지를 마우스가 해당 윈도우에서 움직이면 계속 발생
기타 명령어
해당 윈도우의 두 좌표정보(모니터 스크린 기준)을 알아온다.
- GetWindowRect(Rect*)
: Cwnd 클래스의 멤버, 모니터 기준 좌표를 RECT구조체로 알아내는 함수
스크린 기준좌표를 클라이언트 뷰 기준의 좌표로 환산
- ScreenToClient(CPoint*)
: CWnd클래스의 멤버. POINT 구조체로 전달받은 좌표를 특정 윈도우 기준 좌표로 변환
자식 윈도우의 위치를 변경된 새좌표로 이동
- SetWindowPos(const CWnd * pWndInsertAfter, int x, int y, int cx, int cy, UNIT nFlags)
▷pWndInsertAfter: 윈도우가 겹쳐있을 때 어떤 것이 위이고 아래일 지 결정하는 값
-value: &Cwnd::wndTop(or wndBottom, wndTopMost,wndNoTopMost)
▷x,y: 클라이언트 뷰 기준의 변경할 왼쪽 위 좌표
▷cx,cy: 새로 설정할 윈도우의 폭과 넓이
▷nFlags: 함수의 종작을 변경하거나 다른 인자를 적용/무시 하는 것
CPoint ptChild
CRect Rect;
m_wndChild.GetWindowRect(&Rect); //Rect구조체에 m_wndChild(CWnd)윈도우의 모니터 기준 좌표를 반환
ptChild.x = Rect.left;
ptChild.y = Rect.top; //왼쪽 위의 좌표를 CPoint에 저장
ScreenToClient(&ptChild); //스크린 기준좌표를 클라이언트 뷰 기준의 좌표로 환산
m_wndChild.SetWindowPos(&CWnd::wndTop, ptChild.x,ptChild.y,0,0,
SWP_SHOWWINDOW|SWP_NOZORDER|SWP_NOSIZE);
윈도우의 텍스트 변경
- Cwnd.SetWindowText(CString)
윈도우의 텍스트 가져오기
- Cwnd.GetWindowText();
system키와 조합이되어서 눌렀는지 확인하는 방법
- 시스템키와 특정 키가 조합이되었는지 ::GetKeyState()로 16비트(WORD)정보를 얻어낼 수있다.
*GetKeyState의 반환값이 음수이면 눌러졌다고 판단하기도함
* 메시지 핸들러에서만 사용가능
(OnSysKeyDown함수에서 해야 됨. 이 함수가 실행됐다는 건 이미 시스템키(alt)가 눌러졌다는 것이기 때문)
1) WORD wResult = ::GetKeyState(VK_SPACE) //스페이스키가 눌러졌는지 확인해본다.
(16비트정보- 상위 8비트: 눌러졌는지, 하위 8비트: 토글 키가 눌러졌는지)
2) BYTE byHigh =HIBYTE(wResult) //상위바이트 알아내기
3) if(byHigh & 0x01) //상위바이트의 1번비트가 1이면 SPACE키가 같이 눌러진거임. 조건문 동작
4) wResult =::GetKeyState(VK_CAPITAL) //caps lock키가 눌려졌는지 검사
5) BYTE byLow = LOBYTE(wResult) //토클키가 켜졌는지 확인
6) if(byLow & 0x01) //하위 바이트의 1번키트가 1이면 키가 눌려진 상태
메시지 핸들러 외부에서 키가 눌러졌는지 확인하는 방법
- ::GetAsynckeyState() 사용
윈도우 종료
- AfxGetMainWnd()->SendMessage(WM_CLOSE)
화면의 원하는 위치에 Text출력 (OnPaint()함수 내부)
-CPaintDC dc(this)로 화면의 dc가 주어진다.
-dc.TextOust(10,10,CString); //10,10좌표에 CString이 출력된다.
자식 윈도우에서 일어나는 마우스 동작을 부모 윈도우에게 알리는 방법
- Create에서 SS_NOTIFY 설정을 추가한다.
'◼️MFC' 카테고리의 다른 글
[MFC] 윈도우에서 제공하는 캡쳐기능을 사용하여 캡쳐하고, 캡쳐한 파일을 원하는 위치에 저장 시키기 (0) | 2024.02.15 |
---|---|
[MFC] 다이얼로그 상하좌우로 창크기 늘릴수 있도록한다. (0) | 2024.02.15 |
[MFC] visual studio 2015 MFC 상단 메뉴바에 하위메뉴 추가 (0) | 2024.02.15 |
[MFC] 마우스 위치의 윈도우 핸들값 얻기 (0) | 2024.02.15 |
[MFC] UpdateData(true), UpdateData(false) (0) | 2023.11.29 |