티스토리 뷰
MFC SDI (Single Document Interface)
하나의 응용프로그램에서 어느 한 순간에 하나의 문서만을 대상으로 작업할 수 있는 사용자 인터페이스
- 도큐먼트 객체
응용프로그램에 오직 하나만 존재
- 뷰 객체
응용프로그램의 필요에 따라 여러개(화면분할) 생성 가능. (View Splitter)
- 도큐먼트 프레임 윈도우 (Document Frame Window)
SDI 에서는 메인 윈도우
도큐먼트의 내용을 화면에 표시하는 뷰를 자식으로 갖는 윈도우
뷰의 부모윈도우
<객체간 상호 작용>
SDI 프로젝트를 만들면 프로젝트명App, 프로젝트명Doc, 프로젝트명View, CMainFrame(도큐먼트 프레임 윈도우) 클래스들이 자동으로 만들어지고, 위 그림에 나와있는 도큐먼트 템플릿(CSingleDocTempakte)은 아래 코드처럼 프로젝트App::InitInstance() 내에서 객체가 만들어 진다.
1
2
3
4
5
6
7
8
9
10
11
12 |
BOOL CMFCApplication12App::InitInstance()
{
...
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CMFCApplication12Doc),
RUNTIME_CLASS(CMainFrame), // 주 SDI 프레임 창입니다.
RUNTIME_CLASS(CMFCApplication12View));
if (!pDocTemplate)
return FALSE;
} |
cs |
- CWinApp* AfxGetApp()
응용프로그램 객체의 주소를 리턴. 전역함수
CWinApp 클래스의 멤버함수를 호출할때는 그대로 사용하지만 그렇지 않을때는 반드시 형변환을 해야 한다.
1
2 |
CMyApp* pMyApp = (CMyApp*)AfxGetApp();
pMyApp->Doit(); |
cs |
- CWnd* AfxGetMainWnd()
메인 윈도우 객체의 주소를 리턴, 전역함수
CWnd 클래스의 멤버함수외 함수를 호출하려면 형변환 해야 한다.
1
2 |
CMainFrame* pMainFrame = (CMainFrame*)AfxGetMainWnd();
pMainFrame->DoThat(); |
cs |
- CFrameWnd* CWnd::GetParentFrame()
부모 윈도우 중 CFrameWnd 또는 CFrameWnd 파생 클래스에 속한 해당 윈도우 객체 주소를 리턴
일반적으로 도큐먼트 프레임 윈도우가 된다.
- CView* CFrameWnd::GetActiveView()
사용자가 현재 작업하고 있는 활성 뷰(Active View) 객체의 주소를 리턴
- CDocument* CView::GetDocument()
하나의 뷰 객체는 반드시 하나의 도큐먼트 객체와 연결되어 있다. 뷰 객체와 연결된 도큐먼트 객체의 주소를 리턴, 뷰 객체가 생성될때 멤버변수인 m_pDocument 가 이 도큐먼트 객체의 주소로 초기화 된다.
실제 코드도 m_pDocument 값을 리턴한다.
1
2
3
4
5 |
CMFCApplication11Doc* CMFCApplication11View::GetDocument() const // 디버그되지 않은 버전은 인라인으로 지정됩니다.
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMFCApplication11Doc)));
return (CMFCApplication11Doc*)m_pDocument;
} |
|
POSITION CDocument::GetFirstViewPosition()
CView* CDocument::GetNextView(POSITION& rPosition)
도큐먼트 객체는 자신과 연결된 뷰를 연결 리스트로 관리한다.
GetFirstViewPosition()는 연결 리스트의 시작점에 해당하는 POSITION 타입의 값을 리턴
이 값을 이용하여 GetNextView()를 호출할 때마다 뷰 객체의 주소를 순서대로 얻을 수 있다.
1
2
3
4
5 |
POSITION pos = GetFirstViewPosition();
while (pos != NULL) {
CView* pView = GetNextView(pos);
...
} |
cs |
POSITION CWinApp::GetFirstDocTemplatePosition();
CDocTemplate* CWinApp::GetNextDocTemplate(POSITION& pos);
응용프로그램 객체는 도큐먼트 템플릿 객체를 연결 리스트로 관리한다.
POSITION CDocTempalte::GetFirstDocPosition();
CDocument* CDocTempalte::GetNextDoc(POSITION& rPos);
도큐먼트 템플릿 객체는 도큐먼트 객체를 연결 리스트로 관리한다.
CDocTemplate* CDocument::GetDocTemplate();
도큐먼트 객체가 도큐먼트 템플릿 객체의 주소를 얻을때 사용한다.
1
2
3
4
5
6
7
8
9
10
11
12
13 |
BOOL CMFCApplication11App::InitInstance()
{
...
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CMFCApplication11Doc),
RUNTIME_CLASS(CMainFrame), // 주 SDI 프레임 창입니다.
RUNTIME_CLASS(CMFCApplication11View));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);
} |
cs |
6행의 IDR_MAINFRAME 은 리소스의 String Table 에 있으며
프레임 윈도우 제목, 새로 생성되는 문서의 제목, 파일 확장자 등의 정보를 가지고 있다.
<문자열 의미> '\n' 으로 구분
MFCApplication11\n\nMFCApplication1\nMFCApplication11 Files (*.sdi)\n.sdi\nMFCApplication11.Document\nMFCApplication11.Document
순서 |
의미 |
MFCApplication11 |
프레임 윈도우의 타이틀 바에 표시되는 제목 |
|
새 문서의 제목, 생략하면 기본값은 '제목없음' 이다 |
MFCApplication1 |
MDI에서만 사용, 새 문서를 생성할때 도큐먼트 타입을 묻는 대화상자에 표시될 문자열 |
MFCApplication11 Files (*.sdi) |
열기 또는 저장하기 대화상자에 표시 |
.sdi |
파일의 기본 확장자 |
MFCApplication11.Document |
레지스트리에 등록되는 도큐먼트 타입 ID, 공백 문자는 안된다. |
MFCApplication11.Document |
레지스트리에 등록되는 도큐먼트 타입 문자열, 공백문자 안된다. |
void CDocument::SetModifiedFlag(BOOL bModified = TRUE);
도큐먼트 객체가 유지하는 데이터를 수정하는 경우 이 함수를 호출해서 도큐먼트 객체에게 알려준다.
수정된 내용을 디스크에 저장하지 않은 상태로 종료하거나 새 문서를 작성하려고 하면 변경한 내용을 저장할 건지 묻는 대화상자가 뜨게 된다.
void CDocument::UpdateAllViews(CView* pSender, LPARAM lHint = 0L, CObject* pHint = NULL);
도큐먼트 객체가 유지하는 데이터가 수정될 경우 모든 뷰 객체에게 이 사실을 알려줄 필요가 생긴다. 그래야만 뷰 객체가 자신의 화면을 갱신할 수 있기 때문이다. 내부 코드는 아래와 같다.
1
2
3
4
5
6
7
8
9
10
11 |
void CDocument::UpdateAllViews(CView* pSender, LPARAM lHint = 0L, CObject* pHint = NULL)
{
...
POSITION pos = GetFirstViewPosition();
while (pos != NULL)
{
CView* pView = GetNextView(pos);
if (pView != pSender)
pView->OnUpdate(pSender, lHint, pHint);
}
} |
cs |
CDocument::UpdateAllViews() 는
CView::OnUpdate() - CWnd::Invalidate() - CWnd::OnPaint() - CView::OnDraw() 를 순서대로 호출하게된다.
UpdateAllViews(NULL) - 모든 뷰를 갱신
UpdateAllViews(this) - 특정 객체(this)를 제외한 모든 뷰를 갱신
virtual BOOL CDocument::OnNewDocument();
새 문서를 생성할 때 (파일-새파일 메뉴) 자동으로 호출되는 함수
MDI는 새 문서가 생설될때 마다 새 도큐먼트 객체를 생성하지만 SDI는 하나의 도큐먼트 객체를 초기화해서 재사용 하므로 도큐먼트 클래스의 생성자 보다 OnNewDocument() 에서 초기화 코드를 추가해야만 한다.
virtual BOOL CDocument::onOpenDocument(LPCTSTR lpszPathName);
파일- 열기 메뉴를 선택할때 자동으로 호출되는 함수
새 파일을 열때마다 필요한 작업은 여기에 추가한다.
virtual void CDocument::DeleteContents();
OnNewDocument(), OnOpenDocument() 함수 이전에 자동으로 호출하는 가상함수
SDI에서 도큐먼트 객체를 재사용하기 위해서 새 문서를 생성하거나 열때마다 기존의 데이터를 삭제할 필요가 있다. 이 함수는 도큐먼트 객체 데이터를 삭제하는 등의 초기화 코드를 추가에 좋은 위치다.
virtual void CDocument::Serialize(CArchive& ar);
파일을 열거나 저장할때 자동으로 호출되는 함수
파일-새파일 일때 호출 순서> DeleteContents() - OnNewDocument()
파일-열기 일때 호출 순서> DeleteContents() - Serialize() - onOpenDocument()
파일-저장 혹은 파일-다른이름으로 저장 일때 호출 순서> Serialize()
virtual void CView:;OnDraw(DCD* pDC);
CView 클래스에서 순수 가상함수로 선언되어 있으므로 반드시 재정의해야 한다.
화면 출력, 인쇄, 인쇄 미리보기를 할때 자동 호출된다.
virtual void CView::OnInitialUpdate();
뷰 객체가 도큐먼트 객체와 연결된 후 화면에 보이기 전에 자동으로 호출되는 함수
CView:;OnUpdate()를 호출하여 뷰의 화면을 무효화 시킨다.
virtual void CView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint);
CDocument::UpdateAllViews()와 CView::OnInitialUpdate() 에서 호출하는 가상함수
기본 구현에 해당하는 CView::OnUpdate()는 CWnd::Invalidate()를 이용하여 뷰의 화면 전체를 무효화 시킨다. 좀 다 효과적인 화면 갱신이 필요한 경우 이 함수를 재정의하고 두번째 인자를 참조하여 뷰의 화면 일부만 무효화 시킬 수 있다.
<효과적인 화면 갱신>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 |
UpdateAllView(NULL, 1, (CObject*)pRect);
void CMFCApplication11View::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
{
if (lHint == 1)
{
CRect* pRect = (CRect*)pHint;
InvalidateRect(pRect);
return;
}
CView::OnUpdate(pSender, lHint, pHint);
}
void CCMFCApplication11View::OnDraw(CDC* pDC)
{
...
CRect rect;
pDC->GetClipBox(&rect);
// 사각형을 다시 그린다.
} |
cs |