Chap. 6 Dialog and Control Classes Contents CDialog class Common Dialogs Property Sheets Control classes CDialog Class Dialog의 종류 Modal dialog Close될 때 까지 다른 부분을 사용할 수 없음 DoModal()함수를 이용 예 : common dialog box Modeless dialog Close되기 전에 다른 부분을 사용할 수 있음 Create()함수를 이용 DestroyWindow()함수를 이용하여 명시적으로 종료함 예 : find and replace dialog box CDialog Class (cont’d) History MFC 1.0 Current version Modal dialog : CModalDialog Modeless dialog : CDialog Modal dialog : CDialog Modeless dialog : Cdialog AFXWIN.H // all CModalDialog functionality is now in CDialog #define CModalDialog CDialog CDialog Class (cont’d) void CMyView::DisplayOrderDialog() { CMyDialog myDialog(ID_DLG_MYDIALOG); if ( myDialog.DoModal() == IDOK ) { // Do OK processing } else { // Do Calnel processing } } m_pDlgMyDlgPtr = new CMyDialog; m_pDlgMyDlgPtr->Create(ID_DLG_MYDIALOG); // Do something m_pDlgMyDlgPtr->DestroyWindow(); m_pDlgMyDlgPtr = NULL; Win32 APIs Dialog생성을 위한 Win32 APIs CreateDialog() CreateDialogIndirect() Modeless dialog생성, template pointer이용 DialogBox() Modeless dialog생성, template resource이용 Modal dialog생성, template resource이용 DialogBoxIndirect() Modal dialog생성, template pointer이용 Win32 APIs (cont’d) CDialog Class 오직 CreateDialogIndirect() API을 이용 Modality를 내부적으로 구현 AFXWIN.H DLGCORE.CPP, AFXWIN2.INL CDialog class CDialog : public CWnd { DECLARE_DYNAMIC(CDialog) BOOL Create(LPCTSTR lpszTemplateName, CWnd* pParentWnd = NULL); BOOL Create(UINT nIDTemplate, CWnd* pParentWnd = NULL); BOOL CreateIndirect(LPCDLGTEMPLATE lpDialogTemplate, CWnd* pParentWnd = NULL, void* lpDialogInit = NULL); BOOL CreateIndirect(HGLOBAL hDialogTemplate, CWnd* pParentWnd = NULL); // Modal construct public: CDialog(LPCTSTR lpszTemplateName, CWnd* pParentWnd = NULL); CDialog(UINT nIDTemplate, CWnd* pParentWnd = NULL); BOOL InitModalIndirect(LPCDLGTEMPLATE lpDialogTemplate, CWnd* pParentWnd = NULL, void* lpDialogInit = NULL); BOOL InitModalIndirect(HGLOBAL hDialogTemplate, CWnd* pParentWnd = NULL); // Operations public: // modal processing virtual int DoModal(); CDialog (cont’d) void NextDlgCtrl() const; void PrevDlgCtrl() const; void GotoDlgCtrl(CWnd* pWndCtrl); // termination void EndDialog(int nResult); // Overridables (special message map entries) virtual BOOL OnInitDialog(); virtual void OnSetFont(CFont* pFont); protected: virtual void OnOK(); virtual void OnCancel(); // Implementation public: virtual ~CDialog(); virtual BOOL PreTranslateMessage(MSG* pMsg); virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo); virtual BOOL CheckAutoCenter(); CDialog (cont’d) protected: // parameters for 'DoModal' LPCTSTR m_lpszTemplateName; // name or MAKEINTRESOURCE HGLOBAL m_hDialogTemplate; // indirect (m_lpDialogTemplate == NULL) LPCDLGTEMPLATE m_lpDialogTemplate; void* m_lpDialogInit; // DLGINIT resource data CWnd* m_pParentWnd; // parent/owner window HWND m_hWndTop; // top level parent window (may be disabled) virtual void PreInitDialog(); // implementation helpers HWND PreModal(); void PostModal(); BOOL CreateIndirect(LPCDLGTEMPLATE lpDialogTemplate, CWnd* pParentWnd, void* lpDialogInit, HINSTANCE hInst); BOOL CreateIndirect(HGLOBAL hDialogTemplate, CWnd* pParentWnd, HINSTANCE hInst); protected: DECLARE_MESSAGE_MAP() }; CDialog (cont’d) Declaration(AFXWIN.H) 멤버 변수 m_nIDHelp m_lpszTemplateName Resource template의 이름 m_hDialogTemplate Button을 위한 help ID 일단 load된 후의 resource template의 handle m_lpDialogInit 초기화에 관련된 data에 대한 pointer CDialog (cont’d) m_pParentWnd m_hWndTop Top-level parent window m_pOccDialogInfo Parent window에 대한 pointer OLE controls을 위한 stored information 멤버 함수 virtual PreTranslateMessage() 특별한 message(tool tips, context-sensitive help)에 대한 filtering CDialog (cont’d) virtual OnCmdMsg() virtual CheckAutoCenter() WM_INITDIALOG message이전에 불리워지는 함수 PreModal() M_pOccDialogInfo변수에 데이터를 setting virtual PreInitDialog() Auto-center옵션이 체크되었는지 확인 virtual SetOccDialogInfo() Command message처리작업 DoModal()함수 실행을 위한 준비작업 PostModal() DoModal()함수가 끝난후의 뒤처리 Modal Dialog Creation 일반적으로 두가지의 과정을 거침 CDialog construction DoModal()함수의 호출 CDialog construction DLGCORE.CPP에 있음 두가지 버전이 있으며 CDialog class의 필요한 변수에 값을 입력하는 역할을 함 Modal Dialog Creation (cont’d) “DlgCore.cpp” CDialog::CDialog(LPCTSTR lpszTemplateName, CWnd* pParentWnd) { m_pParentWnd = pParentWnd; m_lpszTemplateName = lpszTemplateName; if (HIWORD(m_lpszTemplateName) == 0) m_nIDHelp = LOWORD((DWORD)m_lpszTemplateName); } CDialog::CDialog(UINT nIDTemplate, CWnd* pParentWnd) { m_pParentWnd = pParentWnd; m_lpszTemplateName = MAKEINTRESOURCE(nIDTemplate); m_nIDHelp = nIDTemplate; } Modal Dialog Creation (cont’d) int CDialog::DoModal() { // STEP 1 : load resource as necessary LPCDLGTEMPLATE lpDialogTemplate = m_lpDialogTemplate; HGLOBAL hDialogTemplate = m_hDialogTemplate; HINSTANCE hInst = AfxGetResourceHandle(); if (m_lpszTemplateName != NULL) { hInst = AfxFindResourceHandle(m_lpszTemplateName, RT_DIALOG); HRSRC hResource = ::FindResource(hInst, m_lpszTemplateName, RT_DIALOG); hDialogTemplate = LoadResource(hInst, hResource); } if (hDialogTemplate != NULL) lpDialogTemplate = (LPCDLGTEMPLATE)LockResource (hDialogTemplate); // return -1 in case of failure to load the dialog template resource if (lpDialogTemplate == NULL) return -1; Modal Dialog Creation (cont’d) // STEP 2 : Preparing to create the dialog HWND hWndParent = PreModal(); AfxUnhookWindowCreate(); BOOL bEnableParent = FALSE; if (hWndParent != NULL && ::IsWindowEnabled(hWndParent)) { ::EnableWindow(hWndParent, FALSE); bEnableParent = TRUE; } // STEP 3 : create modeless dialog AfxHookWindowCreate(this); if (CreateDlgIndirect(lpDialogTemplate, CWnd::FromHandle(hWndParent), hInst)) { if (m_nFlags & WF_CONTINUEMODAL) { // enter modal loop DWORD dwFlags = MLF_SHOWONIDLE; if (GetStyle() & DS_NOIDLEMSG) dwFlags |= MLF_NOIDLEMSG; VERIFY(RunModalLoop(dwFlags) == m_nModalResult); } Modal Dialog Creation (cont’d) // hide the window before enabling the parent, etc. if (m_hWnd != NULL) SetWindowPos(NULL, 0, 0, 0, 0, SWP_HIDEWINDOW| SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER); } if (bEnableParent) ::EnableWindow(hWndParent, TRUE); if (hWndParent != NULL && ::GetActiveWindow() == m_hWnd) ::SetActiveWindow(hWndParent); // STEP 4 : destroy modal window DestroyWindow(); PostModal(); } Modal Dialog Creation (cont’d) HWND CDialog::PreModal() { // cannot call DoModal on a dialog already constructed as modeless ASSERT(m_hWnd == NULL); // allow OLE servers to disable themselves CWinApp* pApp = AfxGetApp(); if (pApp != NULL) pApp->EnableModeless(FALSE); // find parent HWND HWND hWnd = CWnd::GetSafeOwner_ (m_pParentWnd->GetSafeHwnd(), &m_hWndTop); // hook for creation of dialog AfxHookWindowCreate(this); // return window to use as parent for dialog return hWnd; } Modal Dialog Creation (cont’d) int CWnd::RunModalLoop(DWORD dwFlags) { BOOL bIdle = TRUE; LONG lIdleCount = 0; BOOL bShowIdle = (dwFlags & MLF_SHOWONIDLE) && !(GetStyle() & WS_VISIBLE); HWND hWndParent = ::GetParent(m_hWnd); // acquire and dispatch messages until the modal state is done for (;;) { // phase1: check to see if we can do idle work while(bIdle&&!::PeekMessage(pMsg,NULL,NULL,NULL,PM_NOREMOVE)) { } // phase2: pump messages while available do { // pump message, but quit on WM_QUIT !AfxGetThread()->PumpMessage(); } while (::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE)); } } Modal Dialog Creation (cont’d) DoModal()(DLGCORE.CPP) Dialog resource의 loading Dialog template name을 가지고 dialog template을 찾아서 load함 Dialog를 생성하기 위한 준비 PreModal()함수를 호출함 Parent window handle을 찾음 Safety checks m_hWndTop에 저장 EnableWindow(FALSE)를 호출 Parent window를 disable시킴 Modal Dialog Creation (cont’d) Dialog를 생성하고 보여줌 CWnd::CreateDlgIndirect()함수 호출 내부적으로 Win32API인 CreateDialogIndirect()를 호출 CWnd::RunModalLoop()함수 호출 Dialog가 끝날때 까지 일을 수행 사용자가 ok나 cancel버튼을 누르면 CWnd::EndModalLoop()함수가 호출됨 Dialog를 화면에서 보이지 않게 함 Dialog가 종료하면 EnableWindow(TRUE)를 호출 Parent window를 enable시킴 마지막 단계 DestroyWindow()함수 호출 PostModal()함수 호출 Modeless Dialog Creation Modeless dialog creation 두가지 과정을 거침 New operator를 사용하여 변수 생성 CDialog::Create()함수 호출 Modeless Dialog Creation (cont’d) BOOL CDialog::Create(LPCTSTR lpszTemplateName, CWnd* pParentWnd) { m_lpszTemplateName = lpszTemplateName; // used for help if (HIWORD(m_lpszTemplateName) == 0 && m_nIDHelp == 0) m_nIDHelp = LOWORD((DWORD)m_lpszTemplateName); if (!_AfxCheckDialogTemplate(lpszTemplateName, FALSE)) { PostNcDestroy(); // cleanup if Create fails too soon return FALSE; } HINSTANCE hInst = AfxFindResourceHandle(lpszTemplateName, RT_DIALOG); HRSRC hResource = ::FindResource(hInst, lpszTemplateName, RT_DIALOG); HGLOBAL hTemplate = LoadResource(hInst, hResource); BOOL bResult = CreateIndirect(hTemplate, pParentWnd, hInst); FreeResource(hTemplate); return bResult; } Modeless Dialog Creation (cont’d) BOOL CDialog::CreateIndirect (LPCDLGTEMPLATE lpDialogTemplate, CWnd* pParentWnd, void* lpDialogInit, HINSTANCE hInst) { ASSERT(lpDialogTemplate != NULL); if (pParentWnd == NULL) pParentWnd = AfxGetMainWnd(); m_lpDialogInit = lpDialogInit; return CreateDlgIndirect(lpDialogTemplate, pParentWnd, hInst); } Modeless Dialog Creation (cont’d) CDialog::Create()(DLGCORE.CPP) Template name과 help ID를 내부 변수에 저장 Dialog resource를 찾고 load함 CDialog::CreateIndirect()함수 호출 내부적으로 Win32 API인 CreateDialogIndirect()함수 호출 사용자가 ok나 cancel버튼을 누르면 EndDialog()가 호출되어 dialog가 사라짐 Dialog Terminator void CDialog::EndDialog(int nResult) { ASSERT(::IsWindow(m_hWnd)); if (m_nFlags & (WF_MODALLOOP|WF_CONTINUEMODAL)) EndModalLoop(nResult); ::EndDialog(m_hWnd, nResult); } CDialog Control Initialization Dialog에 있는 control의 초기화 작업 Resource에 의한 초기화 Combo box를 예로 살펴봄 Data는 “one”, “two”, “three”라 가정 다음과 같은 코드가 생성되지 않는다. m_pCombo->AddString(“one”); m_pCombo->AddString(“two”); m_pCombo->AddString(“three”); CDialog Control Initialization (cont’d) WM_INITDIALOG CDialog::HandleInitDialog() Dialog가 화면에 보이기 전에 발생하는 message Application으로 하여금 dialog에 있는 control들을 초기화 할 수 있도록 함 CDialog::HandleInitDialog()함수에 mapping되어 있음 OLE control 관련 초기화 작업을 하고 CDialog::OnInitDialog()를 호출 CDialog::OnInitDialog() CWnd::ExecuteDlgInit()를 호출 CDialog Control Initialization (cont’d) Resource file IDD_ABOUTBOX DLGINIT BEGIN IDC_COMBO1, 0x403, 4, 0 0x6e6f, 0x0065, IDC_COMBO1, 0x403, 4, 0 0x7774, 0x006f, IDC_COMBO1, 0x403, 6, 0 0x6874, 0x6572, 0x0065, 0 END CDialog Control Initialization (cont’d) CWnd::ExecuteDlgInit()(WINCORE.CPP) 두가지 버전이 존재 Argument로 dialog template name Argument로 dialog data에 대한 pointer 일단 첫번째 버전의 함수가 dialog template name을 가지고 resource 파일에서 control의 data를 load함 이 후 pointer를 얻어서 두번째 버전의 함수를 호출 why CWnd class member function? CDialog Control Initialization (cont’d) CWnd::ExecuteDlgInit(LPVOID) Resource file의 raw data(DLGINIT)를 parsing함 BOOL CWnd::ExecuteDlgInit(LPVOID lpResource) { BOOL bSuccess = TRUE; UNALIGNED WORD* lpnRes = (WORD*)lpResource; while(bSuccess && *lpnRes != 0) { WORD nIDC = *lpnRes++; WORD nMsg = *lpnRes++; DWORD dwLen = *((UNALIGNED DWORD*&)lpnRes)++; if (nMsg == LB_ADDSTRING || nMsg == CB_ADDSTRING) { if (::SendDlgItemMessageA(m_hWnd, nIDC, nMsg, 0, (LONG)lpnRes) == -1) bSuccess = FALSE; } } return bSuccess; } DDX/DDV DDX/DDV DDX(Dynamic Data eXchange) DDV(Dynamic Data Validation) Data members Controls 활용 사례 void CMyDlg::DoDataExchange(CDataExchange * pDX) { CDialog::DoDataExchange(pDX); DDX_Text(pDX, IDC_EDIT1, m_strEdit1); DDV_MaxChars(pDX, IDC_EDIT1, 20); DDX_Text(pDX, IDC_EDIT2, m_uEdit2); DDV_MinMaxChars(pDX, IDC_EDIT2, 1, 234); DDX_Check(pDX, IDC_CHECK, m_bCheckState); DDX_Radio(pDX, IDC_RADIO, m_nRadioState); } DDX/DDV (cont’d) Question CDataExchange class의 역할 Control들과 멤버 변수들간의 정보 교환은 언제, 어디에서, 어떻게 이루어지는가 DDX/DDV 함수는 무슨 일들을 하는가 DDX/DDV (cont’d) Helper class CDataExchange(AFXWIN.H) class CDataExchange { // Attributes public: BOOL m_bSaveAndValidate; // TRUE => save and validate data CWnd* m_pDlgWnd; // container usually a dialog // Operations (for implementors of DDX and DDV procs) HWND PrepareCtrl(int nIDC); // return HWND of control HWND PrepareEditCtrl(int nIDC); // return HWND of control void Fail(); // will throw exception CWnd* PrepareOleCtrl(int nIDC); // for OLE controls in dialog // Implementation CDataExchange(CWnd* pDlgWnd, BOOL bSaveAndValidate); HWND m_hWndLastControl; // last control used (for validation) BOOL m_bEditLastControl; // last control was an edit item }; DDX/DDV (cont’d) 멤버 변수 m_bSaveAndValidate m_pDlgWnd Dialog에 대한 CWnd pointer m_hWndLastControl TRUE data가 control에서 변수로 감 FALSE data가 변수에서 control로 감 Validation은 TRUE일때만 일어남 Previous control에 대한 handle을 저장 m_bEditLastControl Previous control이 수정되었는지를 저장 DDX/DDV (cont’d) 멤버 함수 Constructor PrepareCtrl() PrepareEditCtrl() Edit control을 위한 준비 Fail() Non-edit control을 위한 준비 Validation이 실패하면 호출됨 Focus를 previous control로 보내고 CUserException예외를 발생시킴 PrepareOleCtrl() OLE control을 위한 준비 DDX/DDV (cont’d) 특징 DDX/DDV는 Serialize과정과 유사 CDataExchange 는 CArchive 와 비슷 DoDataExchange()는 Serialize() 와 비슷 Save/load tag를 가짐 DDX/DDV (cont’d) DDX/DDV 함수들 DDX_Text()(DLGDATA.CPP) DDV_MaxChars()(DLGDATA.CPP) m_bSaveAndValidate의 값을 기준으로 data이동 m_bSaveAndValidate값이 TRUE인경우 validation코드 수행 DDX_TextWithFormat()(DLGDATA.CPP) String과 int사이의 conversion DDX/DDV (cont’d) void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, CString& value) { HWND hWndCtrl = pDX->PrepareEditCtrl(nIDC); if (pDX->m_bSaveAndValidate) { int nLen = ::GetWindowTextLength(hWndCtrl); ::GetWindowText(hWndCtrl, value.GetBufferSetLength(nLen), nLen+1); value.ReleaseBuffer(); } else { AfxSetWindowText(hWndCtrl, value); } } DDX/DDV (cont’d) void AFXAPI DDV_MaxChars(CDataExchange* pDX, CString const& value, int nChars) { ASSERT(nChars >= 1); // allow them something if (pDX->m_bSaveAndValidate && value.GetLength() > nChars) { TCHAR szT[32]; wsprintf(szT, _T("%d"), nChars); CString prompt; AfxFormatString1(prompt, AFX_IDP_PARSE_STRING_SIZE, szT); AfxMessageBox(prompt, MB_ICONEXCLAMATION, AFX_IDP_PARSE_STRING_SIZE); prompt.Empty(); // exception prep pDX->Fail(); } else if (pDX->m_hWndLastControl != NULL && pDX->m_bEditLastControl) { // limit the control max-chars automatically ::SendMessage(pDX->m_hWndLastControl, EM_LIMITTEXT, nChars, 0); } } DDX/DDV (cont’d) 언제 DoDataExchange()가 호출되는가? 필요할 때 마다 UpdateData()함수 호출 위 함수 내부에서 DoDataExchange()를 호출함 UpdateData()(WINCORE.CPP) DDX/DDV (cont’d) BOOL CWnd::UpdateData(BOOL bSaveAndValidate) { CDataExchange dx(this, bSaveAndValidate); _AFX_THREAD_STATE* pThreadState = AfxGetThreadState(); HWND hWndOldLockout = pThreadState->m_hLockoutNotifyWindow; ASSERT(hWndOldLockout != m_hWnd); // must not recurse pThreadState->m_hLockoutNotifyWindow = m_hWnd; BOOL bOK = FALSE; // assume failure TRY { DoDataExchange(&dx); bOK = TRUE; // it worked } CATCH(CUserException, e) { // validation failed - user already alerted, fall through ASSERT(!bOK); } } DDX/DDV (cont’d) void CDialog::OnOK() { if (!UpdateData(TRUE)) { TRACE0("UpdateData failed during dialog termination.\n"); // the UpdateData routine will set focus to correct item return; } EndDialog(IDOK); } BOOL CDialog::OnInitDialog() { BOOL bDlgInit; if (m_lpDialogInit != NULL) bDlgInit = ExecuteDlgInit(m_lpDialogInit); else bDlgInit = ExecuteDlgInit(m_lpszTemplateName); UpdateData(FALSE); return TRUE; // set focus to first one } Common Dialogs Common Dialogs 표준 interface제공을 위하여 종류 CColorDialog CFileDialog CFindReplaceDialog CFontDialog CPrintDialog CPageSetupDialog ( MFC 4.0 ) Common Dialogs (cont’d) Steps 생성자 호출 DoModal() 호출 IDOK가 리턴될 때 적절한 처리 CFileDialog myDialog(TRUE, NULL, NULL, OFN_FILEMUSTEXIST); if ( myDialog.DoModal() == IDOK ) { Cstring strPath = myDialog.GetPathName(); CString strFile = myDialog.GetFileName(); } else // User selected Cancel … Common Dialogs (cont’d) Win32 API 관점에서 모든 common dialog는 대응되는 structure를 가지고 있다. 대응되는 API를 가지고 있다. 예) Open File common dialog Structure – OPENFILENAME API – GetOpenFileName(LP OPENFILENAME) 모든 common dialog의 base class CCommonDialog(AFXDLGS.H) OnOK()와 OnCancel() 추가 Common Dialogs (cont’d) 예제 dialog CFileDialog(AFXDLGS.H) class CFileDialog : public CCommonDialog { public: OPENFILENAME m_ofn; // open file parameter block virtual int DoModal(); CString GetPathName() const; // return full path and filename CString GetFileName() const; // return only filename CString GetFileExt() const; // return only ext CString GetFileTitle() const; // return file title BOOL GetReadOnlyPref() const; // return TRUE if readonly checked // Enumerating multiple file selections POSITION GetStartPosition() const; CString GetNextPathName(POSITION& pos) const; Common Dialogs (cont’d) protected: friend UINT CALLBACK _AfxCommDlgProc(HWND, UINT, WPARAM, LPARAM); virtual UINT OnShareViolation(LPCTSTR lpszPathName); virtual BOOL OnFileNameOK(); virtual void OnLBSelChangedNotify(UINT nIDBox, UINT iCurSel, UINT nCode); // only called back if OFN_EXPLORER is set virtual void OnInitDone(); virtual void OnFileNameChange(); virtual void OnFolderChange(); virtual void OnTypeChange(); // Implementation BOOL m_bOpenFileDialog; // TRUE for file open, FALSE for file save CString m_strFilter; // filter string TCHAR m_szFileTitle[64]; // contains file title after return TCHAR m_szFileName[_MAX_PATH]; // contains full path name after return OPENFILENAME* m_pofnTemp; virtual BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult); }; Common Dialogs (cont’d) 예제 dialog CFileDialog(AFXDLGS.H) OPENFILENAME structure 여러 개의 virtual protected 함수 기타 여러 정보 저장을 위한 변수 CFileDialog의 생성(DLGFILE.CPP) 생성자의 argument들을 OPENFILENAME structure의 멤버에 setting함(m_ofn) Window95이상일 경우에는 OFN_EXPLORER와 OFN_ENABLEHOOK flag를 set함 OFN_ENABLEHOOK Hook routine을 제공(_AfxCommDlgProc()를 m_ofn의 hook field에 set) Common Dialogs (cont’d) CFileDialog::DoModal()(DLGFILE.CPP) M_bOpenFileDialog변수의 값을 보고 Win32 API인 GetOpenFileName()을 호출할건지 GetSaveFileName()을 호출할건지를 결정 Common Dialogs (cont’d) int CFileDialog::DoModal() { int nResult; BOOL bEnableParent = FALSE; m_ofn.hwndOwner = PreModal(); AfxUnhookWindowCreate(); if (m_ofn.hwndOwner != NULL && ::IsWindowEnabled(m_ofn.hwndOwner)) { bEnableParent = TRUE; ::EnableWindow(m_ofn.hwndOwner, FALSE); } if (m_bOpenFileDialog) nResult = ::GetOpenFileName(&m_ofn); else nResult = ::GetSaveFileName(&m_ofn); if (bEnableParent) ::EnableWindow(m_ofn.hwndOwner, TRUE); PostModal(); return nResult ? nResult : IDCANCEL; } Property Sheets Tabbed dialogs MFC 3.0 때부터 제공 하나의 dialog 에서 사용자로부터 많은 입력을 받을 수 있는 인터페이스 Property Sheets (cont’d) 두개의 class CPropertySheet CPropertyPage Tabbed dialog를 위한 class Tabbed dialog의 각각의 tab(page)을 위한 class 일반 dialog와의 차이점 Apply버튼 제공 Page별로 update하는 기능을 제공 Property Sheets (cont’d) STEP 1. Dialog template 생성 및 property sheet 의 layout 설정 2. 각 template 에 대응하는 CPropertyPage 파생 클래스들을 생성 3-1. (modal property sheet) CPropertySheet의 instance를 생성하고 AddPage() 멤버 함수를 통해 추가한 뒤 DoModal() 호출 OK/Apply/Cancel 버튼이 자동적으로 추가 3-2. (modeless property sheet) CPropertySheet의 파생클래스 생성한 뒤 인스턴스를 생성하고 Create() 함수 호출 OK/Apply/Cancel 버튼이 자동적으로 추가되지 않음 Property Sheets (cont’d) CPropertySheet mySheet(“My Property Sheet!”, this); CPageOne myPage1; CPageTwo myPage2; CPageThree myPage3; mySheet.AddPage(&myPage1); mySheet.AddPage(&myPage2); mySheet.AddPage(&myPage3); mySheet.DoModal(); … CPropertySheet Class Declaration of CPropertySheet AFXDLGS.H class CPropertySheet : public CWnd { // Attributes public: AFX_OLDPROPSHEETHEADER m_psh; int GetPageCount() const; CPropertyPage* GetActivePage() const; int GetActiveIndex() const; CPropertyPage* GetPage(int nPage) const; int GetPageIndex(CPropertyPage* pPage); BOOL SetActivePage(int nPage); BOOL SetActivePage(CPropertyPage* pPage); void SetTitle(LPCTSTR lpszText, UINT nStyle = 0); void EnableStackedTabs(BOOL bStacked); CPropertySheet Class (cont’d) // Operations public: virtual int DoModal(); void AddPage(CPropertyPage* pPage); void RemovePage(CPropertyPage* pPage); void RemovePage(int nPage); void EndDialog(int nEndID); // used to terminate a modal dialog BOOL PressButton(int nButton); // Implementation public: virtual ~CPropertySheet(); void CommonConstruct(CWnd* pParentWnd, UINT iSelectPage); virtual BOOL PreTranslateMessage(MSG* pMsg); virtual void BuildPropPageArray(); virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam); virtual BOOL OnInitDialog(); virtual BOOL ContinueModal(); CPropertySheet Class (cont’d) protected: CPtrArray m_pages; // array of CPropertyPage pointers CString m_strCaption; // caption of the pseudo-dialog CWnd* m_pParentWnd; // parent window of property sheet BOOL m_bStacked; // EnableStackedTabs sets this BOOL m_bModeless; // TRUE when Create called instead of DoModal // Generated message map functions //{{AFX_MSG(CPropertySheet) afx_msg BOOL OnNcCreate(LPCREATESTRUCT); afx_msg LRESULT HandleInitDialog(WPARAM, LPARAM); afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor); afx_msg LRESULT OnCommandHelp(WPARAM, LPARAM); afx_msg void OnClose(); afx_msg void OnSysCommand(UINT nID, LPARAM); afx_msg LRESULT OnSetDefID(WPARAM, LPARAM); //}}AFX_MSG friend class CPropertyPage; }; CPropertySheet Class (cont’d) Declaration of CPropertySheet AFXDLGS.H CWnd에서 상속받음 (why?) 멤버 CommonConstruct() 수많은 constructor들이 호출하는 공통 함수 m_pages CPtrArray형의 pointer로 CPropertyPage class의 pointer의 배열 CPropertySheet Class (cont’d) m_strCaption m_pParentWnd Parent window에 대한 pointer m_bStacked Property sheet의 caption Tab을 stacked mode로 할건지 scrolled mode로 할 건지를 결정 m_bModeless Property sheet를 modal로 할건지 modeless로 할건지 CPropertySheet Class (cont’d) CPropertySheet::DoModal() DLGPROP.CPP 내부 수행 AfxDeferRegisterClass()호출 내부적으로 InitCommonControls()함수 호출(Windows common controls DLL을 초기화) BuildPropPageArray()호출 Main window를 disable하고 ::PropertySheet()함수를 호출 (이후는 CDialog::DoModal()과 동일) CPropertySheet Class (cont’d) CPropertySheet::AddPage() DLGPROP.CPP m_pages멤버 변수에 page 추가 CPropertySheet::BuildPropPageArray() DLGPROP.CPP 각 page에 있는 PROPSHEETPAGE structure정보를 저장함 CPropertySheet Class (cont’d) int CPropertySheet::DoModal() { VERIFY(AfxDeferRegisterClass(AFX_WNDCOMMCTLS_REG)); BuildPropPageArray(); HWND hWndParent = CWnd::GetSafeOwner_(); ::EnableWindow(hWndParent, FALSE); HWND hWnd = (HWND)::PropertySheet((PROPSHEETHEADER*)psh); nResult = RunModalLoop(dwFlags); DestroyWindow(); ::EnableWindow(hWndTop, TRUE); return nResult; } CPropertySheet Class (cont’d) void CPropertySheet::AddPage(CPropertyPage* pPage) { m_pages.Add(pPage); if (m_hWnd != NULL) { HPROPSHEETPAGE hPSP = CreatePropertySheetPage((PROPSHEETPAGE*)ppsp); if (hPSP == NULL) AfxThrowMemoryException(); if (!SendMessage(PSM_ADDPAGE, 0, (LPARAM)hPSP)) { DestroyPropertySheetPage(hPSP); AfxThrowMemoryException(); } } } CPropertySheet Class (cont’d) void CPropertySheet::BuildPropPageArray() { delete[] (PROPSHEETPAGE*)m_psh.ppsp; m_psh.ppsp = NULL; AFX_OLDPROPSHEETPAGE* ppsp = new AFX_OLDPROPSHEETPAGE[m_pages.GetSize()]; m_psh.ppsp = (LPPROPSHEETPAGE)ppsp; for (int i = 0; i < m_pages.GetSize(); i++) { CPropertyPage* pPage = GetPage(i); memcpy(&ppsp[i], &pPage->m_psp, sizeof(pPage->m_psp)); pPage->PreProcessPageTemplate ((PROPSHEETPAGE&)ppsp[i], bWizard); } m_psh.nPages = m_pages.GetSize(); } Control Classes Control class의 세가지 group Old fashioned (6) New fangled Button, combo box, edit, list box, scroll bar, static Window common controls OLE controls Old-Fashioned 특징 모두 CWnd에서 상속받음 Cbitmap, CBitmapButton, CComboBox, CEdit, CListBox, CDragListBox, CCheckListBox, CScrollBar, CStatic Declaration은 AFXWIN.H Implementation AFXWIN2.INL WINCTRL1.CPP WINCTRL2.CPP WINCTRL3.CPP WINBTN.CPP Old-Fashioned (cont’d) Cbutton* pButton = (Cbutton *)pDialog->GetDlgItem(IDC_CHECK1); ASSERT(pButton != NULL); pButton->SetCheck(m_bShowState); //// Crect rectButton(10, 10, 50, 50); Cbutton* pButton = new Cbutton; pButton->Create(“Text”, WS_CHILD|BS_PUSHBUTTON, rectButton, pView, ID_BUTTON); … pBtton->DestroyWindow(); delete pButton; New-fangled 특징 모두 CWnd에서 상속받음 CAnimateCtrl, CDragListBox, CHotKeyCtrl, CImageList, CProgressCtrl, CRichEditCtrl, CSliderCtrl, CSpinButtonCtrl, … Declaration은 AFXCMN.H Implementation AFXCMN.INL WINCTRL2.CPP WINCTRL4.CPP Chap. 7 Document/View Architecture Contents Introduction Architecture Inside document/view architecture Document/view internals Introduction Application의 data관리 Data를 분리하여 관리하자. 중요한 이슈 누가 data를 관리할 것인가 누가 data를 update할 것인가 data 의 rendering 어떻게 다르게 할 것인가 … Print 와 print preview의 지원 두 부분 Data management User-interface management Introduction (cont’d) 기존의 방법 분리하지 않고 하나의 클래스에서 처리 대단히 복잡 중요성 Multiple ways to represent your data Rendering & Updating Multiple types of data User interface Architecture Document와 view Document Application의 data를 의미 View Application의 data의 표현을 의미 Document/View Components MFC document/view architecture를 위한 4개의 components Documents Views Document/view frames Document templates Document/View Components (cont’d) Documents CDocument class Managing file I/O Updating renderings of the data CCmdTarget(CObject)로 부터 상속 받음 CObject Run-time type information, dynamic creation, serialization CCmdTarget Command message (WM_COMMAND)를 받을 수 있음 Document/View Components (cont’d) Views CView class CWnd(CCmdTarget, CObject)로 부터 상속 받음 CCmdTarget Command message (WM_COMMAND)를 받을 수 있음 CWnd Window message(WM_PAINT)를 받을 수 있음 View는 border가 없는 window임 View를 둘러싸고 있는 border있는 window를 frame window라고 함 Document/View Components (cont’d) Document/View Frames 각각의 view에 서로 다른 user-interface를 적용할 수 있게 함 Frame View Document/View Components (cont’d) SDI CFrameWnd class Single Document Interface 워드패드 MDI CMDIChildWnd class Multiple Document Inteface MS Word Document/View Components (cont’d) Document templates CDocTemplate class Document, view, frame을 묶어서 하나의 unit으로 관리 각각의 서로 다른 document type에 대하여 하나씩의 template을 가질 수 있다. 실제 사용시 한 type의 document CSingleDocTemplate class 여러 type의 document CMultiDocTemplate class Document/View Components (cont’d) CSingleDocTemplate 생성자의 인자 Resource ID Documet/View/Frame 의 Run-time class CMultiDocTemplate CSingleDocTemplate와 생성자의 인자가 동일 차이점 : linked list를 통해 다수의 정보 유지 Document/View Components (cont’d) Resource ID application 의 string table window title, document name 새 document 생성 시 description 파일 open 시 파일 type 에 대한 description file extension filter, … CWinApp의 역할 CWinApp의 역할은? Document template을 관리함 CWinApp::InitInstance()함수 Document template을 생성함 생성이 끝나면 CWinApp::AddDocTemplate()호출 CWinApp의 document template list에 추가함 CWinApp의 역할 (cont’d) BOOL CTestApp::InitInstance() { … CMultiDocTemplate* pDocTemplate; pDocTemplate = new CMultiDocTemplate( IDR_TESTTYPE, RUNTIME_CLASS(CTestDoc), RUNTIME_CLASS(CChildFrame), // custom MDI child frame RUNTIME_CLASS(CTestView)); AddDocTemplate(pDocTemplate); … } Document/View Arch. Internals Document/View arch.의 기본 CWinApp Document template의 관리 Document template Frames/Views/Documents의 관리 CDocManager Class CDocManager CWinApp와 CDocTemplate사이의 interface역할 CWinApp가 필요한 document template의 list를 관리하는 역할 CWinApp의 declaration(AFXWIN.H) CDocManager * m_pDocManager; CDocManager Class(contd.) class CDocManager : public CObject { DECLARE_DYNAMIC(CDocManager) CDocManager(); virtual void AddDocTemplate(CDocTemplate* pTemplate); virtual POSITION GetFirstDocTemplatePosition() const; virtual CDocTemplate* GetNextDocTemplate(POSITION& pos) const; virtual void RegisterShellFileTypes(BOOL bCompat); void UnregisterShellFileTypes(); virtual CDocument* OpenDocumentFile(LPCTSTR lpszFileName); virtual BOOL SaveAllModified(); // save before exit virtual void CloseAllDocuments(BOOL bEndSession); virtual int GetOpenDocumentCount(); CPtrList m_templateList; int GetDocumentCount(); }; CDocManager Class(contd.) Declaration(AFXWIN.H) 멤버 변수 m_templateList CPtrList type 실제 document template의 list 멤버 함수 대부분의 함수가 m_templateList에 대한 조작과 CWinApp와의 interface를 위해서 존재 CDocManager Class(contd.) 대부분의 CWinApp의 document template관련 함수는 CDocManager의 함수를 그대로 호출함 CWinApp::AddDocTemplate() CWinApp::CloseAllDocument() CWinApp::DoPromptFileName() CWinApp::GetFirstDocTemplatePosition() CWinApp::GetNextDocTemplate() CWinApp::GetOpenDocumentCount() CWinApp::OnFileNew() CWinApp::OnFileOpen() … CDocManager Class(contd.) void CWinApp::AddDocTemplate(CDocTemplate* pTemplate) { if (m_pDocManager == NULL) m_pDocManager = new CDocManager; m_pDocManager->AddDocTemplate(pTemplate); } void CWinApp::OnFileNew() { if (m_pDocManager != NULL) m_pDocManager->OnFileNew(); } CDocManager Class(contd.) CDocManager::OnFileNew() 새로운 document template을 생성하는 역할 하나 이상의 document template가 있을때는 CNewTypeDlg dialog class를 이용(DOCMGR.CPP) 사용자가 document template을 선택하게 함 사용자에게 document template list로 보여주는 좋은 예가 됨 CDocManager Class(contd.) “DocMgr.cpp” void CDocManager::OnFileNew() { CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetHead(); if (m_templateList.GetCount() > 1) { CNewTypeDlg dlg(&m_templateList); int nID = dlg.DoModal(); if (nID == IDOK) pTemplate = dlg.m_pSelectedTemplate; else return; // none - cancel operation } pTemplate->OpenDocumentFile(NULL); } CDocManager Class(contd.) CNewTypeDlg class DOCMGR.CPP class CNewTypeDlg : public CDialog { protected: CPtrList* m_pList; // actually a list of doc templates public: CDocTemplate* m_pSelectedTemplate; enum { IDD = AFX_IDD_NEWTYPEDLG }; CNewTypeDlg(CPtrList* pList) : CDialog(CNewTypeDlg::IDD) { m_pList = pList; m_pSelectedTemplate = NULL; } protected: BOOL OnInitDialog(); void OnOK(); }; CDocManager Class(contd.) CNewTypeDlg resource AFXRES.RC CDocManager Class(contd.) BOOL CNewTypeDlg::OnInitDialog() { CListBox* pListBox = (CListBox*)GetDlgItem(AFX_IDC_LISTBOX); POSITION pos = m_pList->GetHeadPosition(); while (pos != NULL) { CDocTemplate* pTemplate = (CDocTemplate*)m_pList->GetNext(pos); CString strTypeName; if (pTemplate->GetDocString(strTypeName, CDocTemplate::fileNewName) && !strTypeName.IsEmpty()) { int nIndex = pListBox->AddString(strTypeName); pListBox->SetItemDataPtr(nIndex, pTemplate); } } return CDialog::OnInitDialog(); } CDocTemplate class CDocTemplate Document/View/Frame을 관리 Base class 실제 사용시에는 CSingleDocTemplate DOCSINGL.CPP CMultiDocTemplate DOCMULTI.CPP CDocTemplate class(contd.) Declaration(AFXWIN.H) class CDocTemplate : public CCmdTarget { DECLARE_DYNAMIC(CDocTemplate) protected: CDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass); // Attributes public: virtual POSITION GetFirstDocPosition() const = 0; virtual CDocument* GetNextDoc(POSITION& rPos) const = 0; virtual void AddDocument(CDocument* pDoc); // must override virtual void RemoveDocument(CDocument* pDoc); // must override virtual BOOL GetDocString(CString& rString, enum DocStringIndex index) const; // get one of the info strings CFrameWnd* CreateOleFrame(CWnd* pParentWnd, CDocument* pDoc, BOOL bCreateView); virtual CDocument* CreateNewDocument(); virtual CFrameWnd* CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther); CDocTemplate class(contd.) virtual void InitialUpdateFrame(CFrameWnd* pFrame, CDocument* pDoc, BOOL bMakeVisible = TRUE); virtual BOOL SaveAllModified(); // for all documents virtual void CloseAllDocuments(BOOL bEndSession); virtual CDocument* OpenDocumentFile( LPCTSTR lpszPathName, BOOL bMakeVisible = TRUE) = 0; virtual void SetDefaultTitle(CDocument* pDocument) = 0; public: BOOL m_bAutoDelete; virtual ~CDocTemplate(); protected: UINT m_nIDResource; CRuntimeClass* m_pDocClass; // class for creating new documents CRuntimeClass* m_pFrameClass; // class for creating new frames CRuntimeClass* m_pViewClass; // class for creating new views CString m_strDocStrings; // '\n' separated names }; CDocTemplate class(contd.) Declaration(AFXWIN.H) 멤버 변수 m_nIDResource m_pDocClass Document template에 대한 resource ID CDocument에 대한 CRuntimeClass structure의 pointer m_pFrameClass CFrameWnd에 대한 CRuntimeClass structure의 pointer CDocTemplate class(contd.) m_pViewClass m_strDocStrings CView에 대한 CRuntimeClass structure의 pointer Document template의 string resource Implementation(DOCTEMPL.CPP) Document/View/Frame을 생성하는 함수 CDocTemplate::CreateNewDocument() CDocTemplate::CreateNewFrame() View에 관한 명시적인 함수는 없음 CDocTemplate class(contd.) CDocument* CDocTemplate::CreateNewDocument() { CDocument* pDocument = (CDocument*)m_pDocClass->CreateObject(); AddDocument(pDocument); return pDocument; } CDocTemplate class(contd.) CFrameWnd* CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther) { CCreateContext context; context.m_pCurrentFrame = pOther; context.m_pCurrentDoc = pDoc; context.m_pNewViewClass = m_pViewClass; context.m_pNewDocTemplate = this; CFrameWnd* pFrame = (CFrameWnd*)m_pFrameClass->CreateObject(); pFrame->LoadFrame(m_nIDResource, WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, NULL, &context)) return pFrame; } CDocTemplate class(contd.) CDocTemplate::CreateNewDocument() m_pDocClass->CreateObject()를 이용해 document생성 생성된 document를 open된 document list에 추가 CDocTemplate::CreateNewFrame() CCreateContext에 필요한 정보를 채움 m_pFrameClass->CreateObject()를 이용해 frame생성 Frame에 필요한 resource를 load CDocTemplate class(contd.) CCreateContext structure Declaration(AFXEXT.H) struct CCreateContext // Creation information structure // All fields are optional and may be NULL { CRuntimeClass* m_pNewViewClass; CDocument* m_pCurrentDoc; // for creating MDI children (CMDIChildWnd::LoadFrame) CDocTemplate* m_pNewDocTemplate; // for sharing view/frame state from the original view/frame CView* m_pLastView; CFrameWnd* m_pCurrentFrame; // Implementation CCreateContext(); }; CDocTemplate class(contd.) CCreateContext structure Declaration(AFXEXT.H) m_pNewViewClass View생성을 위한 CRuntimeClass pointer m_pCurrentDoc Current document m_pCurrentFrame Current frame m_pNewDocTemplate Multiple document 일 때 마지막 document를 가리킴 m_pLastView Multiple view일 때 마지막 view를 가리킴 CDocTemplate class(contd.) Open된 document의 관리는? CSingleDocTemplate CSingleDocTemplate와 CMultiDocTemplate가 담당 CDocument* m_pOnlyDoc; CMultiDocTemplate CPtrList m_docList; int m_nUntitledCount; Untitled document의 수를 저장함(Untitled[X]) CDocTemplate class(contd.) CMultiDocTemplate::OpenDocumentFile() DOCMULTI.CPP Document와 frame의 생성 CFrameWnd::InitialUpdateFrame()호출 CDocTemplate class(contd.) “DocMulti.cpp” CDocument* CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bMakeVisible) { CDocument* pDocument = CreateNewDocument(); CFrameWnd* pFrame = CreateNewFrame(pDocument, NULL); if (lpszPathName == NULL) { // create a new document - with default document name SetDefaultTitle(pDocument); pDocument->OnNewDocument(); } else { // open an existing document CWaitCursor wait; pDocument->OnOpenDocument(lpszPathName); pDocument->SetPathName(lpszPathName); } return pDocument; } CFrameWnd class CFrameWnd CView의 생성! m_pViewActive 현재 atcive한 view에 대한 pointer CFrameWnd::GetActiveView() CFrameWnd::SetActiveView() InitialUpdateFrame()함수 모든 view의 OnInitialUpdate()함수를 호출함 CFrameWnd class(contd.) Implementation(WINFRM.CPP) CFrameWnd::CreateView() 진정한 view의 생성 CWnd* CFrameWnd::CreateView(CCreateContext* pContext, UINT nID) { CWnd* pView = (CWnd*)pContext->m_pNewViewClass ->CreateObject(); if (pView == NULL) return NULL; if (!pView->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW, CRect(0,0,0,0), this, nID, pContext)) return NULL; // can't continue without a view return pView; } CFrameWnd class(contd.) 그러면 CreateView()는 어디서 호출되나? int CFrameWnd::OnCreate(LPCREATESTRUCT lpcs) { return OnCreateHelper(lpcs, pContext); } int CFrameWnd::OnCreateHelper(LPCREATESTRUCT lpcs, CCreateContext* pContext) { if (CWnd::OnCreate(lpcs) == -1) return -1; // create special children first OnCreateClient(lpcs, pContext); } CFrameWnd class(contd.) BOOL CFrameWnd::OnCreateClient(LPCREATESTRUCT, CCreateContext* pContext) { if (pContext != NULL && pContext->m_pNewViewClass != NULL) { CreateView(pContext, AFX_IDW_PANE_FIRST); } } CFrameWnd class(contd.) CreateNewFrame()이 호출되면 Windows가 WM_CREATE message를 보냄 OnCreate()함수의 호출 OnCreateHelper()의 호출 OnCreateClient()의 호출 CreateView()의 호출 결국 CreateNewFrame()의 호출 결과임 CFrameWnd class(contd.) CFrameWnd::InitialUpdateFrame() Frame의 모든 view를 update함 void CFrameWnd::InitialUpdateFrame(CDocument* pDoc, BOOL bMakeVisible) { CView* pView = NULL; if (bMakeVisible) { SendMessageToDescendants(WM_INITIALUPDATE, 0, 0, TRUE, TRUE); ActivateFrame(nCmdShow); if (pView != NULL) pView->OnActivateView(TRUE, pView, pView); } // update frame counts and frame title (may already have been visible) if (pDoc != NULL) pDoc->UpdateFrameCounts(); OnUpdateFrameTitle(TRUE); } CDocument Class CDocument Declaration(AFXWIN.H) class CDocument : public CCmdTarget { public: const CString& GetTitle() const; virtual void SetTitle(LPCTSTR lpszTitle); const CString& GetPathName() const; virtual void SetPathName(LPCTSTR lpszPathName, BOOL bAddToMRU = TRUE); virtual BOOL IsModified(); virtual void SetModifiedFlag(BOOL bModified = TRUE); virtual POSITION GetFirstViewPosition() const; virtual CView* GetNextView(POSITION& rPosition) const; void UpdateAllViews(CView* pSender, LPARAM lHint = 0L, CObject* pHint = NULL); virtual void DeleteContents(); // delete doc items etc CDocument Class (cont’d) // File helpers virtual BOOL OnNewDocument(); virtual BOOL OnOpenDocument(LPCTSTR lpszPathName); virtual CFile* GetFile(LPCTSTR lpszFileName, UINT nOpenFlags, …); virtual void ReleaseFile(CFile* pFile, BOOL bAbort); protected: CString m_strTitle; CString m_strPathName; CDocTemplate* m_pDocTemplate; CPtrList m_viewList; // list of views BOOL m_bModified; // changed since last saved virtual BOOL DoSave(LPCTSTR lpszPathName, BOOL bReplace = TRUE); virtual BOOL DoFileSave(); virtual void UpdateFrameCounts(); void DisconnectViews(); void SendInitialUpdate(); friend class CDocTemplate; }; CDocument Class (cont’d) Implementation(DOCCORE.CPP) Key aspects Creating documents Saving documents Communicating with views CDocument Class (cont’d) Creating Documents Empty document일 경우 OnNewDocument()함수 호출 DeleteContents()호출 Modified flag를 FALSE로 함 m_strPathName이 NULL인지 확인 File로 부터 create하는 경우 OnOpenDocument()함수 호출 GetFile()함수를 이용하여 file open DeleteContents()함수 호출 Modified flag를 TRUE로 함 CArchive class를 이용하여 serialization Modified flag를 FALSE로 함 CDocument Class (cont’d) BOOL CDocument::OnNewDocument() { if (IsModified()) TRACE0("Warning: OnNewDocument replaces an unsaved document.\n"); DeleteContents(); m_strPathName.Empty(); SetModifiedFlag(FALSE); return TRUE; } // no path name yet // make clean CDocument Class (cont’d) BOOL CDocument::OnOpenDocument(LPCTSTR lpszPathName) { CFileException fe; CFile* pFile = GetFile(lpszPathName, CFile::modeRead|CFile::shareDenyWrite, &fe); DeleteContents(); SetModifiedFlag(); // dirty during de-serialize CArchive loadArchive(pFile, CArchive::load | CArchive::bNoFlushOnDelete); loadArchive.m_pDocument = this; loadArchive.m_bForceFlat = FALSE; CWaitCursor wait; if (pFile->GetLength() != 0) Serialize(loadArchive); // load me loadArchive.Close(); ReleaseFile(pFile, FALSE); SetModifiedFlag(FALSE); // start off with unmodified return TRUE; } CDocument Class (cont’d) Saving Documents OnSaveDocument() GetFile()함수를 이용하여 file open CArchive class를 이용하여 serialization Modified flag를 FALSE로 함 CDocument Class (cont’d) BOOL CDocument::OnSaveDocument(LPCTSTR lpszPathName) { CFileException fe; CFile* pFile = NULL; pFile = GetFile(lpszPathName, CFile::modeCreate | CFile::modeReadWrite | CFile::shareExclusive, &fe); CArchive saveArchive(pFile, CArchive::store | CArchive::bNoFlushOnDelete); saveArchive.m_pDocument = this; saveArchive.m_bForceFlat = FALSE; CWaitCursor wait; Serialize(saveArchive); // save me saveArchive.Close(); ReleaseFile(pFile, FALSE); SetModifiedFlag(FALSE); // back to unmodified return TRUE; // success } CDocument Class (cont’d) View와의 통신 m_viewList에 view의 list를 저장 통신이 필요한 경우 Destroy시 Frame에 접근하고자 할 때 DisconnectViews()함수를 호출하여 각 viwe의 m_pDocument가 NULL이 되게 함 Cview::GetParentFrame()함수 이용 View들에게 document의 변경을 알리고자 할 때 UpdateAllViews()함수 호출 CDocument Class (cont’d) void CDocument::UpdateAllViews(CView* pSender, LPARAM lHint, CObject* pHint) // walk through all views { ASSERT(pSender == NULL || !m_viewList.IsEmpty()); // must have views if sent by one of them POSITION pos = GetFirstViewPosition(); while (pos != NULL) { CView* pView = GetNextView(pos); ASSERT_VALID(pView); if (pView != pSender) pView->OnUpdate(pSender, lHint, pHint); } } CView Class CView Declaration(AFXWIN.H) Implementation(VIEWCORE.CPP) void CView::OnPaint() { // standard paint routine CPaintDC dc(this); OnPrepareDC(&dc); OnDraw(&dc); } Document/View Internals CWinApp::OnFileOpen() CWinApp::OnFileNew() CDocManager::OnFileOpen() CDocManager::OnFileNew() CDocTemplate::OpenDocumentFile() CDocTemplate::CreateNewDocument() CDocTemplate::CreateNewFrame() WM_CREATE Document/View…(contd.) WM_CREATE CFrameWnd::OnCreate() CFrameWnd::OnCreateHelper() CFrameWnd::OnCreateClient() CFrameWnd::CreateView() CMyDocument::OnOpenDocument() CMyDocument::OnNewDocument()