初識MFC視窗類別

來源:互聯網
上載者:User

  Frame、View、Document的關係,非常重要,它們的確不是孤立的,而是互相聯絡的。

   如果是SDI程式,CMainFrame調用GetActiveView獲得CView,而CView可以調用GetDocument獲得CDocument。如果已知CDocument,可以使用GetFirstViewPosition獲得第一個相關的CView,然後使用GetNextView獲得下一個的CView。之所以這麼做,是因為MFC允許一個CDocument擁有多個CView。

  如果是MDI程式,稍微複雜一些。你不能直接調用GetActiveView,而需要先調用CMainFrame::MDIGetActive獲得當前的CMDIChildWnd,而這個CMDIChildWnd相當於SDI中的CMainFrame

1、架構視窗

  架構視窗為應用程式的使用者介面提供結構架構,它是應用程式的主視窗,負責管理其包容的視窗,一個應用程式的最頂層的架構視窗是應用程式啟動時建立的第一個視窗。

  MFC提供三種類型的架構視窗:單文件視窗,多文件視窗(MDI),對話方塊。在AppWizard的第一個對話方塊中,就提供了選項,讓使用者選擇應用程式是基於單文檔、多文檔還是對話方塊的。MFC單文件視窗一次只能開啟一個文檔架構視窗,而MDI應用程式運行時,在應用程式的一個執行個體中開啟多個文檔架構視窗,這些視窗稱作子視窗(Child Window)。這些文檔可以是同一類型的,也可以是不同類型的。如Visual Studio就可以開啟資源檔視窗和來源程式視窗等不同類型的視窗。此時,啟用不同類型的MDI子視窗,菜單也將相應變化。

  MFC提供了三個類CFrameWnd、CMDIFrameWnd、CMDIChildWnd和CDialog 分別用於支援單文件視窗、多文件視窗和對話方塊。

CFrameWnd
  用於SDI架構視窗,形成單個文檔及其視的邊框。架構視窗既是應用程式的主架構視窗,也是當前文檔對應的視圖的邊框。

CMDIFrameWnd
  用於MDI應用程式的主架構視窗。主架構視窗是所有MDI文件視窗的容器,並與它們共用菜單條。MDI架構視窗是出現在案頭中的頂層視窗。

CMDIChildWnd
  用於在MDI主架構視窗中顯示開啟的各個文檔。每個文檔及其視都有一個MDI子架構視窗,子架構視窗包含在MDI主架構視窗中。子架構視窗看起來類似一般的架構邊框視窗,但它是包含在主架構視窗中,而不是位於案頭的,並且為主視窗所裁剪。而且MDI子視窗沒有自己的菜單,它與主MDI架構視窗共用菜單。

CDialog
  對話方塊是一種特殊類型的視窗,它的邊框一般不可以調整,而且內部包含一些控制項視窗。有關對話方塊作為主視窗的技術可以參見下一章。

  要產生一個單文件視窗,主視窗就必須從CFrameWnd派生;要產生一個多文件視窗,主視窗就必須從CMDIFrameWnd派生,而且其中的子視窗必須從CMDIChildWnd派生出來;而基於對話方塊的視窗程序就要從CDialog派生出主視窗類。

子視窗

  子視窗就是具有WS_CHILD風格的視窗,且一定有一個父視窗。所有的控制項都是子視窗。子視窗可以沒有邊框。子視窗被完全限制在父視窗內部。

父視窗

  父視窗就是擁有子視窗的視窗。

彈出式視窗

  具有WS_POPUP風格,它可以沒有父視窗。這種視窗幾乎什麼都沒有,可看作一個矩形地區。

2、視窗的建立

  視窗的建立分為兩步:第一步是用new建立一個C++的視窗對象,但是此時只是初始化視窗的資料成員,並沒有真正建立視窗(這一點與一般的對象有所不同)。

  2.1、第一步:建立一個C++對象,其中CMainFrame是從CFrameWnd派生的對象。

  CMainFrame* pMyFrame=new CMainFrame();//用new操作符建立視窗對象

  或

  CMainFrame MyFrame;//定義一個視窗對象,自動調用其建構函式

  2.2、第二步是建立視窗。CFrameWnd的Create成員函數把視窗給做出來,並將其HWND儲存在C++對象的公用資料成員m_hWnd中。

 //第二步:建立視窗

  pMyFrame->Create(NULL,“My Frame Window”);

  或

  MyFrame.Create(NULL,“My Frame Window”);

  Create函數的原形如下:

    BOOL Create( LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle = WS_OVERLAPPEDWINDOW, const RECT& rect = rectDefault, CWnd* pParentWnd = NULL, LPCTSTR lpszMenuName = NULL, DWORD dwExStyle = 0, CCreateContext* pContext = NULL );

  Create函數第一個參數為視窗註冊類名,它指定了視窗的表徵圖和類風格。這裡我們使用NULL做為其值,表明使用預設屬性。第二個參數為視窗標題。其餘幾個參數指定了視窗的風格、大小、父視窗、菜單名等。

  這個函數看起來比較複雜,對於CFrameWnd派生出來的視窗,我們可以使用LoadFrame從資源檔中建立視窗,它只需要一個參數。

  pMyFrame->LoadFrame(IDR_FRAME);

  LoadFrame使用該參數從資源中擷取許多預設值,包括主邊框視窗的標題、表徵圖、菜單、加速鍵等。但是,在使用LoadFrame時,必須確保標題字串、表徵圖、菜單、加速鍵等資源使用同一個ID標識符(在本例中,我們使用IDR_FRAME)。

    提示:在Hello程式的InitInstance中我們看不到建立視窗的過程。實際上,在

    pDocTemplate = new CSingleDocTemplate(IDR_MAINFRAME,RUNTIME_CLASS(CHelloDoc),RUNTIME_CLASS(CMainFrame), // main SDI frame window

     RUNTIME_CLASS(CHelloView));

     AddDocTemplate(pDocTemplate);
    程式片段中,我們看到,CSingleDocTemplate建構函式的第二個參數就是IDR_MAINFRAME。在建構函式內部,已經通過調用m_pMainWnd->LoadFrame(IDR_MAINFRAME),完成了應用程式主視窗的建立過程。

在InitInstance中,建立完視窗後,視窗調用ShowWindow成員函數來顯示視窗。ShowWindow帶一個參數,指示視窗以何種方式顯示(最大化、最小化或一般)。預設方式為SW_SHOW,但實際上我們經常希望應用程式啟動時視窗最大化,此時可以將該參數該為SW_SHOWMAXMIZED,即調用

 m_pMainWnd->ShowWindow(SW_SHOWMAXIMIZED);

在MainFrm.cpp中,我們還看到CMainFrame類有一個OnCreate方法。OnCreate成員函數定義如清單3.3。當調用Create或CreateEx時,作業系統會向視窗發送一條WM_CREATE訊息。這一函數就是用來響應WM_CREATE訊息的。

清單3.3 OnCreate成員函數定義

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)

{if (CFrameWnd::OnCreate(lpCreateStruct) == -1)return -1;

if (!m_wndToolBar.Create(this) ||!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))

{

  TRACE0("Failed to create toolbar/n");

  return -1; // fail to create

}

  if (!m_wndStatusBar.Create(this) || !m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT))){

  TRACE0("Failed to create status bar/n");

  return -1; // fail to create

}

  // TODO: Remove this if you don't want tool tips or a resizeable toolbar

  m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);

  // TODO: Delete these three lines if you don't want the toolbar to

  // be dockable

  m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);

  EnableDocking(CBRS_ALIGN_ANY);

  DockControlBar(&m_wndToolBar);

  return 0;

}

  在OnCreate函數中,首先調用CFrameWnd的預設處理方法OnCreate完成視窗建立工作。後面是應用程式主視窗的特定工作,在上面程式中,建立了工具條和狀態列(有關工具條和狀態列編程參見下一章有關內容)。可以在此處加入一些初始化工作,如從INI檔案中載入設定,顯示Splash Window(啟動畫面)等。

3 、註冊視窗

  在傳統的Windows C程式中,送給一個視窗的所有訊息是在它的視窗函數中處理的。把一個視窗同它的視窗函數聯絡起來的過程稱為註冊視窗類別。註冊視窗包括對視窗指定一個視窗函數(給出視窗函數的指標)以及設定視窗的游標、背景刷子等內容。一個註冊視窗類別可以被多個視窗共用。註冊視窗通過調用API函數RegisterClass來完成。

  在MFC下,架構提供了預設的自動視窗註冊過程。架構仍然使用傳統的註冊類,而且提供了幾個標準的註冊類,它們在標準的應用程式初始化函數中註冊。調用AfxRegisterWndClass全域函數就可以註冊附加的視窗類別,然後把已經註冊的類傳給CWnd的Create成員函數。使用者可以定製自己的註冊過程,以提供一些附加的特性。比如設定視窗的表徵圖、背景、游標等。下面是註冊視窗的例子。

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)

{ 

 // TODO: Modify the Window class or styles here by modifying

 // the CREATESTRUCT cs

 UINT ClassStyle=CS_VREDRAW|CS_HREDRAW;

 cs.style=cs.style&(~FWS_ADDTOTITLE);

 cs.lpszClass = AfxRegisterWndClass(ClassStyle,

 AfxGetApp()->LoadStandardCursor(IDC_ARROW), (HBRUSH)(COLOR_WINDOW+1),//for brush

 AfxGetApp()->LoadIcon(IDR_MAINFRAME));

 return TRUE;

}

  註冊視窗在CFrameWnd的PreCreateWnd方法中完成。從成員函數名字PreCreateWindow中就可以看出來,註冊視窗的工作必須在調用Create函數建立視窗之前完成。其中cs.style=cs.style&(~FWS_ADDTOTITLE)指定視窗標題風格,關閉自動添加文檔標題的功能。AfxRegisterWndClass指定視窗使用箭頭游標、背景刷子使用比視窗顏色標號大一的顏色、表徵圖使用IDR_MAINFRAME標識符指定的表徵圖(當然也可以使用其它表徵圖)。用上面的程式段替換Hello程式MainFrm.cpp中的PreCreateWindow成員函數定義,並重新編譯和運行程式。此時,視窗標題變成了Hello,原來令人討厭的“Untitled-”沒有了,因為視窗風格中關閉自動添加當前檔案名稱的風格。

4、 關閉和銷毀視窗

  架構視窗不僅維護視窗的建立,還管理著視窗的關閉和銷毀過程。關閉視窗時,作業系統依次向被關閉的視窗發送WM_CLOSE和WM_DESTROY訊息。WM_CLOSE訊息的預設處理函數OnClose將調用DestroyWindow,來銷毀視窗;最後,架構調用視窗的解構函式作清理工作並刪除C++視窗對象。

  不要使用C++的delete操作符來銷毀架構視窗,而應當採用CWnd的DestroyWindow成員函數來銷毀。DestroyWindow首先刪除子視窗,再刪除視窗本身。若視窗以變數方式產生(即在堆棧上分配記憶體),該視窗對象會被自動清除。若對象是用new操作符建立的(也就是在堆上分配記憶體的),則需要使用者自己處理。有關DestroyWindow問題在第五章對話方塊技術中還要作進一步解釋。

OnClose()常用功能:儲存視窗的一些狀態、工具條狀態,提示儲存未儲存的資料等等。

void CMainFrame::OnClose()

{

  SaveBarState( "MyDockState" );//儲存工具條狀態

  CFrameWnd::OnClose();

}

5、 視窗啟用

  使用中視窗必定是一個沒有父視窗的頂層視窗,包括架構視窗和對話方塊。當頂層視窗被啟用時,Windows向視窗發送WM_ACTIVATE訊息,對此訊息的預設處理是將使用中視窗設為有輸入焦點。

  輸入焦點用於表示哪個視窗有資格接收鍵盤輸入訊息。帶有輸入焦點的視窗或是一個使用中視窗,或者是該使用中視窗的子視窗。當一個頂層視窗獲得輸入焦點時,Windows向該視窗發送WM_SETFOCUS訊息,此視窗可將輸入焦點重定位到它的子視窗上。子視窗不會自動獲得輸入焦點。失去輸入焦點的視窗會收到WM_KILLFOCUS訊息。當子視窗擁有輸入焦點時,父視窗就不會處理鍵盤輸入了。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.