vc++之cview

視圖是程序設計中使用率最高的窗口對象,它是用戶的主要操作界面。因爲它通常以某種形式表示文檔數據,所以稱之爲視圖。一個視圖對象只關聯一個文檔對象;一個文檔對象可以關聯多個視圖,每個視圖對象以不同形式表示文檔數據。

  從文檔類中將文檔中的數據取出後顯示給用戶;接受用戶對文檔中數據的編輯和修改;將修改的結果反饋給文檔類,由文檔類將修改後的內容保存到磁盤文件中。

  文檔負責了數據真正在永久介質中的存儲和讀取工作,視圖呈現只是將文檔中的數據以某種形式向用戶呈現,因此一個文檔可對應多個視圖。

  Invalidate(TRUE)將整個窗口設置爲需要重繪的無效區域,它會產生WM_PAINT消息,這樣OnDraw將被調用。

  假如文檔中的數據發生了變化,必須通知所有鏈接到該文檔的視圖,這時候文檔類的UpdateAllView函數需要被調用。

  然而,除了打印和打印預覽外,視圖類顯示得很簡單。

關聯文檔對象

  前面已經提到,視圖需要表示文檔數據,所以文檔對象與視圖對象必須建立關聯。這樣,當文檔數據發生變化時,它可以及時通知視圖;當視圖需要顯示不同的文檔數據時,它可以從文檔對象中提取。

  在文檔/視圖框架程序中,文檔對象總是在視圖之前建立,而在視圖的WM_CREATE消息處理函數中,建立了它與文檔對象的關聯。代碼如下:

  Int CView::OnCreate (LPCREATESTRUCT lpcs)

  {

  if (CWnd::OnCreate(lpcs) == -1)

  return -1;

  // m_pDocuemnt視圖成員,用於存儲相應文檔對象的指針,此時爲空

  ASSERT(m_pDocument == NULL);

  CCreateContext* pContext = (CCreateContext*)lpcs->lpCreateParams;

  // pContext->m_pCurrentDoc是已經建立的文檔對象的指針

  if (pContext != NULL && pContext->m_pCurrentDoc != NULL)

  {

  // 將當前視圖加入文檔對象的視圖列表中,因爲一個文檔可關聯多個視圖

  pContext->m_pCurrentDoc->AddView(this);

  /* 在文檔的AddView()函數中,已經將當前文檔對象指針賦給m_pDocument視圖成員。這樣,在視圖建立之初,二者就建立了關聯。*/

  ASSERT(m_pDocument != NULL);

  }

  else

  {

  TRACE(“Warning: Creating a pane with no CDocument./n”);

  }

  return 0; // ok

  }

  同時,視圖類定義了成員函數GetDocument(),返回文檔對象的指針。代碼如下:

  CDocument* CView::GetDocument() const

  {

  ASSERT(this != NULL);

  return m_pDocument;

  }

  視圖總是在文檔對象之前銷燬,在視圖的析構函數中,與文檔對象解除關聯。代碼如下:

  CView::~CView()

  {

  if (m_pDocument != NULL)

  m_pDocument->RemoveView(this);

  }

視圖的繪製

  窗口的繪製工作總是在WM_PAINT消息處理中進行的,當窗口需要繪製時,它會收到系統發來的WM_PAINT消息。在繪製過程中,首先要準備顯示設備句柄,最後要釋放句柄。衆所周知,在視圖窗口中繪製,無需重載WM_PAINT消息處理函數OnPaint(),因爲有一個更加友好的繪製新口:OnDraw()。該函數的實參是一個已經準備好的顯示設備,最後無需釋放。其實,這一切還是WM_PAINT消息處理函數OnPaint()爲我們準備的,代碼如下:

  void CView::OnPaint()

  {

  // CPaintDC類維護顯示設備

  CPaintDC dc(this);

  OnPrepareDC(&dc);

  OnDraw(&dc);

  }

  OnPrepareDC()是一個虛擬函數,它總是在OnDraw()之前執行,可以重載它,設置繪圖模式。因爲OnDraw()每次執行,都使用不同的CPaintDC對象,所以本次繪圖模式的狀態不能保留到下一次。

  OnDraw()在視圖基類CView中定義爲純虛函數。例如:

  virtual void OnDraw(CDC* pDC) = 0;

  所以CView是抽象基類,不能實例化,而派生類必須重載OnDraw()。

虛函數 OnUpdate

  當文檔數據發生變化時,文檔對象調用CDocument::UpdateAllView()通知所有視圖,作爲響應,視圖的OnUpdate()成員被調用。所以,重載的OnUpdate()應該能夠根據需要,將文檔數據的變化反映在視圖中。CView::OnUpdate()只是簡單地使客戶區無效,導致客戶區重畫。例如:

  void CView::OnUpdate(CView* pSender, LPARAM, CObject*)

  {

  ASSERT(pSender != this);

  UNUSED(pSender); // unused in release builds

  Invalidate(TRUE);

  }

虛函數 OnInitialUpdate()

  在初始創建、調用OnCreate()之後,或者在File/New、File/Open命令後被框架調用。基類CView::OnInitialUpdate()只是簡單地調用OnUpdate(),可以重載它完成初始化工作。但注意,它可能被多次調用。

  void CView::OnInitialUpdate()

  {

  OnUpdate(NULL, 0, NULL);

  }

虛函數 CalcWindowRect

  每當主框架窗口的客戶區尺寸發生變化或控制條的位置發生變化,需要重新排列客戶區時,調用該函數,根據視圖客戶區尺寸計算視圖窗口的尺寸。

  我們知道,排列主窗口客戶區是由CFrameWnd::RecalcLayout()完成的。顯然,視圖的CalcWindowRect()函數也是由它觸發調用的。主窗口的客戶區尺寸減掉所有控制佔用的部分,剩下的區域分給視圖,這部分區域作爲實參傳入CalcWindowRect()。在CalcWindowRect()函數內,需要計算視圖窗口的尺寸。代碼如下:

  void CView::CalcWindowRect(LPRECT lpClientRect, UNIT nAdjustType)

  {

  // lpClientRect此時是整個視圖客戶區的尺寸

  // 需要爲滾動條增加尺寸嗎

  if (nAdjustType != 0)

  {

  // 調用API,根據窗口風格計算窗口尺寸

  ::AdjustWindowRectEx(lpClientRect, 0, FALSE, GetExStyle());

  DWORD dwStyle = GetStyle();

  if (dwStyle & WS_VSCROLL)

  {

  // 爲垂直滾動條增加尺寸

  int nAdjust = afxData.csVScroll;

  if (dwStyle & WS_BORDER)

  nAdjust -= CX_BORDER;

  lpClientRect->right += nAdjust;

  }

  if (dwStyle & WS_HSCROLL)

  {

  // 爲水平滾動條增加尺寸

  int nAdjust = afxData.cyHScroll;

  if (dwStyle & WS_BORDER)

  nAdjust -= CY_BORDER;

  lpClientRect->bottom += nAdjust;

  }

  return;

  }

  // 無需爲滾動條增加尺寸,調用基類成員完成計算

  CWnd::CalcWindowRect(lpClientRect, nAdjustType);

  }

虛函數PostNcDestroy

  在視圖窗口關閉時最後調用的成員函數,它與CFrameWnd::PostNcDestroy完成相同的功能,即刪除視圖對象。代碼如下:

  void CView::PostNcDestroy()

  {

  delete this;

  }

  這樣,可以不必關心視圖的釋放工作,即使它在堆中構造。

虛函數OnCmdMsg

  在討論CFrameWnd::OnCmdMsg()時已經瞭解過該函數,下面只給出它的代碼:

  BOOL CView::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)

  {

  // 首先查找自身的命令消息映射

  if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))

  return TRUE;

  // 如果視圖本身沒有處理該命令,將機會留給與其關聯的文檔對象

  if (m_pDocument != NULL)

  {

  CPushRoutingView push(this);

  return m_pDocument->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);

  }

  return FALSE;

  }

虛函數 OnActivateView

  當視圖被激活爲活動視圖,或由活動轉爲非活動時,調用該函數通知視圖。基類的實現只是設置該視圖爲焦點。代碼如下:

  void CView::OnActivateView(BOOL bActivate, CView* pActivateView, CView*)

  {

  UNUSED(pActivateView); // unused in release builds

  if (bActivate) // 當前狀態爲活動嗎

  {

  ASSERT(pActivateView == this);

  // 如果其父窗口也是活動的,則設置焦點。(在MDI中,其父框架可能是非活動的)

  if (IsTopParentActive())

  SetFocus();

  }

  }

MFC中豐富的CView派生類

  MFC提供了豐富的CView派生類,各種不同的派生類實現了對不同種類控件的支持,以爲用戶提供多元化的顯示界面。

  CScrollView: 提供滾動支持;

  CCtrlView: 支持tree、list和rich edit控件;

  CDaoRecordView: 在dialog-box控件中顯示數據庫記錄;

  CEditView: 提供了一個簡單的多行文本編輯器視圖;

  CFormView: 包含dialog-box控件,可滾動,基於對話框模板資源;

  CListView: 支持列表控件視圖;

  CRecordView: 在dialog-box控件中顯示數據庫記錄;

  CRichEditView: 支持富文本編輯的控件視圖;

  CTreeView: 支持重點在樹控件的文檔/視圖結構的視圖。

發佈了16 篇原創文章 · 獲贊 1 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章