文檔與視圖的建立和關聯
發表于:2007-07-14來源:作者:點擊數:
標簽:
作者:王家文湖南大學計算機通信系 本文討論一下單文檔與多視的問題,主要介紹一下筆者在學習VC++6.0過程中探索出的一些個人經驗,并給出了實現它們的的主要程序框架。 一.建立文檔與視圖: 在Projects選MFC Appwizard(exe),鍵入工程名,點ok后選單文檔,
作者:王家文 湖南大學計算機通信系
本文討論一下單文檔與多視的問題,主要介紹一下筆者在學習VC++6.0過程中探索出的一些個人經驗,并給出了實現它們的的主要程序框架。
一.建立文檔與視圖:
在Projects選MFC Appwizard(exe),鍵入工程名,點ok后選單文檔,選擇默認值,在第四步時,在Advanced…里的Window Styles的Use split window前打√。
二.文檔多視:
當我們需要從不同的角度來看單文檔的內容時,這就要我們實現多視了,多視有兩種實
現方案:靜態的和動態的。這就根據不同的需要來實現了。動態的方法一般在程序中已經實現了,自動生成的源代碼:
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/,
CCreateContext* pContext)
{
return m_wndSplitter.Create(this,
2, 2, // TODO: adjust the number of rows, columns
CSize(10, 10), // TODO: adjust the minimum pane size
pContext);
}
而靜態的方法卻麻煩一點,下面我詳細介紹一下:
首先我們先插入一個類:class CMyView : public ClistView
在MainFrm.cpp加入#include "MyView.h"
在MainFrm.h中聲明CSplitterWnd m_splitterWnd; //聲明分隔器對象,已經自動生成了。
在函數CMainFrame::OnCreateClient里加入下面一些代碼,只要把原有的代碼刪去。
If(!m_splitterWnd.CreateStatic(this,1,2,WS_CHILD | WS_VISIBLE))//一行兩列
return false;//不成功就返回
else
{
m_splitterWnd.CreateView(0,1,RUNTIME_CLASS(CMyEditView),CSize(100,300),pContext);
m_splitterWnd.CreateView(0,0,RUNTIME_CLASS(CMyView),CSize(100,100),pContext);
// CmyEditView和CmyView是自己定義的視圖類。
//CSize(100,300)是初始化時的大小
return TRUE;//成功的返回值
}
這樣程序一旦執行就會把窗口分割成左右兩部分。
但是有時我們需要實現這樣的功能:一旦發出一個消息時,我要使其中的一個視圖消失,研究一下,可以刪去或者隱藏這個視圖,相對應恢復時就要重建或者顯示這個視圖,具體的實現在后面再詳解吧!
由于我們有時分割視圖,不是很有規則的1*2,3*1,2*2等形式,而是在分割的視圖里再分割,如圖:
視圖二CviewView2
視圖一CviewView1
視圖三CviewView3
聲明一個分隔器對象:在MainFrm.h中聲明CSplitterWnd m_splitterWnd1
代碼為:
m_wndSplitter1.CreateStatic( &m_wndSplitter,2, 1, WS_CHILD | WS_VISIBLE,
m_wndSplitter.IdFromRowCol(0, 0)); // 創建各個視
m_wndSplitter.CreateView(0, 1,RUNTIME_CLASS(CviewView1), CSize(0, 0), pContext);
m_wndSplitter1.CreateView(0, 0,RUNTIME_CLASS(CviewView2), CSize(0, 0), pContext);
m_wndSplitter1.CreateView(1, 0, RUNTIME_CLASS(CviewView3), CSize(0, 0), pContext);
......
如果使用自由定義的視圖大小來實現呢?
SIZE size;
CRect rect;
GetClientRect(&rect);
size.cx = rect.right/2 ;
size.cy = rect.bottom/2;
只要把size放在Csize(0,0)所在的位置就可以了,用這個方法我們就可以在程序中自由地實現視圖窗口大小的變化了。
到了這里又有一個問題提出來了,我們不光刪去或者隱藏視圖,有時只是簡單的視圖之間的切換,也可以說有的一個框架包含多個視,而在任何時刻只能顯示其中的一個視,還是后面再說吧!
三.獲得視圖指針。
獲取視圖的指針作為單獨的一點來講,自然有它的重要性,視圖與文檔,特別在文檔與不相干的視圖間,視圖與視圖間的消息傳遞及互相之間的調用函數或者變量的時候就要獲取視圖或文檔的指針。
a) 獲取文檔的指針:
與文檔相連的視圖可以直接獲取,這里就不說了,但與文檔一點關系都沒有的視圖要想獲取文檔的指針就不可以直接獲取了。
CMyEditDoc * pDoc=(class CMyEditDoc *)GetDocument();
上面就是獲取的方法。
b) 獲得視圖指針:
i. 在其中的一個視圖中獲取其它視圖的指針:
CMainFrame* MainFrame1=(CMainFrame*)this->GetParent()->GetParent();
CMyEditView* pView=(CMyEditView*)MainFrame1->m_wndSplitter.GetPane(0,1);
This指針代表當前視圖的指針,它通過獲取CmainFrame的指針再去獲取視圖的指針,這樣有個好處在于可以獲得任意視圖的指針。
還有一種方案可以實現,但是它有個缺點,你只能把獲得的指針作為Cview的指針看待,這樣你就不能實現特殊視圖的功能了。
CMyEditDoc * pDoc=GetDocument();
CView * pView;
POSITION pos=pDoc->GetFirstViewPosition();
if(pos!=NULL)
{
pView=pDoc->GetNextView(pos);
}
在{}里加入多個pView=pDoc->GetNextView(pos);(原因我也說不出來)就可以獲得你所要得視圖。
如果你在開始就定義你所接觸視圖的指針,只不過沒有初始化而已,就方面了好多。
m_pEditViewright =( CMyEditView* )m_splitterWnd.GetPane(0,1);// m_pEditViewright已定義。
ii. 在CmainFrame獲取視圖的指針:
CMyEditView* pView=(CMyEditView*)this->m_wndSplitter.GetPane(0,1);
這樣就行了,同樣在文檔里獲取視圖的方法也可以類似,這里就不重復了,但我強調一點要注意頭文件的加入。
四. 單文檔多視的實現:
a) 單框架窗口中的多視:
獲取你要處理的兩個視圖指針: pViewAdd,pViewRemove:
// 顯示活動視而隱藏非活動視
pViewAdd->ShowWindow(SW_SHOW);
pViewRemove->ShowWindow(SW_HIDE);
// 將新的活動視連接到文檔,并斷開原來的視與文檔的連接。
pDoc->AddView(pViewAdd);
pDoc->RemoveView(pViewRemove);
SetActiveView(pViewAdd);
RecalcLayout();//告知當前分割窗口,重新顯示。
方法二你可以把其中的一個視圖移到看不到的地方,活動視變換時就是移動的變換。
方法三你可以先刪去其中的一個視圖,需要時再重建,然后刪去那個視圖。
這兩個方案在下面再詳細說明吧!
b) 單文檔分割視圖的某分割區的多視的實現
i. 移動視圖
獲取視圖指針pView
pView->SetWindowPos(0, 0, 1000, 1500, 1000, SWP_SHOWWINDOW);
這樣移動后在當前窗口下你一般是看不到你移動的視圖,但當你改變窗口的大小時,有什么情況發生,沒有達到你的期望吧,行,你在OnSize這個函數里加入一些代碼,比較簡單我就不說了。
ii. 刪除視圖
m_wndSplitter1.DeleteView(0,0); // 刪除原來的視
運行一下程序還可以,發現我們刪除的視圖上點擊無事件發生,而且出現中斷程序。顯然只有這句話還不行的,這就要按照你的要求把窗口重新分割一下或者使用一個新的視圖來填充一下。
重新分割可以用CMainFrame::OnCreateClient里的代碼的方法實現它,在這里我就不重復了。
五.視圖間的通信:
在前面我已經把獲取視圖的指針作為一大點來解釋,其實在不要講很多大家就會明白的,因為你不管是關聯還是不關聯的視圖,你只要獲取它的指針就可以對它進行任意的操作,甚至它的私有函數(當然不能直接了)。下面我就舉一例。
在CviewView1里加入一個公共函數void simple();
如果你要實現的一些功能就可以寫在這個函數里
void CviewView1::simple()
{
CClientDC dc(this);
dc.TextOut(0,0,"我要實現視圖間的通信,我一定會成功的!");
}
void CviewView2::OnLButtonDblClk(UINT nFlags, CPoint point)
{
CMainFrame* MainFrame1=(CMainFrame*)this->GetParent()->GetParent();
CMyEditView* pView=(CMyEditView*)MainFrame1->m_wndSplitter.GetPane(0,1);
pView->simple();
}
六.CSplitterWnd的擴展:
為什么要擴展CSplitterWnd呢?這是從整個界面來想的,當你分割窗口之后就不希望拖動分割條時它位置的改變,你也需要在分割條上有希望的顏色的出現,這時就要擴展CSplitterWnd一下,其實擴展東西很多,我也只簡單說一下吧!
不希望通過拖動分割條來調節窗口的大小,你就要鎖定分割條,根據你的需要讓CWnd窗口蔽掉這些消息
void CSplitterWnd::OnMouseMove(UINT nFlags,CPoint point)
{
CWnd::OnMouseMove (nFlags,point);
}
七.使用視圖控件。
在VC++里的MFC提供了三個視圖控件:圖象列表控件,列表視圖控件,樹型視圖控件。
圖象列表控件和列表視圖控件都使用的類:CListCtrl。
樹型視圖控件使用的類:CTreeCtrl。
具體的控件功能不是本文所討論的范圍。
使用視圖控件我們可以不向上面那樣要分割窗口,你能夠在視圖里直接添加視圖控件,但是很顯然有個
缺陷:只有上面三個控件,這樣它的使用范圍很小,有時很難達到多種視圖的混用。
CListCtrl m_ListView;
CTreeCtrl m_TreeView;
在你向導生成的視圖里定義這兩個變量,然后在你需要的地方使用下面的函數:
BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );
const RECT& rect就是它的位置,選擇適當的地方就能實現分割條的一些功能。
其實這些方面不是難點,關鍵在于如何獲得它,產生消息映射是最為困難的地方。在這里你就無法使用以前的消息傳遞方法,首先我們來簡單地理解一下消息機制,由于不同的消息是由操作系統的不同部分或是有應用程序來控制的。當鼠標左鍵在單擊時,這個點擊區窗口就收到WM_LBUTTONDOWN消息并處理它,通常它產生一個消息發送給它的主窗口(包含它的窗口),報告“我被單擊了?!盬in32常用控件將WM_NOTIFY通知發送給它的父窗口,作為應答,MFC調用CWnd::OnNotify()方法來處理這些消息,可以為控件的所有者類重載CWnd::OnNotify()方法。
virtual BOOL CWnd::OnNotify( WPARAM wParam, LPARAM lParam,
LRESULT* pResult );
l wParam參數是發送消息的控件ID,如果不來自控件,那么該參數為NULL。
l LParam是指向通知消息結構(NMHDR)的指針,它包含當前的通知代碼和某些其他信息。
l PResult參數是指向LRESULT變量的指針,如果消息被處理,該變量用于存放結果代碼。
BOOL CView::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
// TODO: Add your specialized code here and/or call the base class
TV_DISPINFO *tv_dispinfo=(TV_DISPINFO *)lParam;
if (tv_dispinfo->hdr.code==NM_CLICK)
{
}
}
這就是一個這種消息機制的簡單的例子,你要實現的功能就放在if后的{}里。
八.文檔與視圖的一種常見出錯的處理:
error C2143: syntax error : missing ';' before '*'
error C2501: 'CMyViewDoc' : missing storage-class or type specifiers
error C2501: 'GetDocument' : missing storage-class or type specifiers
這種錯誤一般出現在視圖的公共變量函數的定義處:
CMyViewDoc* GetDocument();
我剛學VC不久,而且不習慣看英文,所以也不知道它出錯的原因在哪里,有誰知道不忘告訴我一聲了。
我一般這樣處理之后就好了,先在定義前面加“//”,把它屏蔽掉,按F7,再把“//”去掉,按F7,一看沒錯了,真的很神了。我在懷疑是不是VC++6.0的一個小小的BUG。
九.結束語:
終于在這里和大家說聲再見了,上面所寫是不算經驗的經驗,基于有感而發,有感而寫,當然錯誤在所難免的。我真心歡迎大家與我聯系。
E-MAIL:passmatlab@etang.com
青草園:http://passmatlab.myetang.com
原文轉自:http://www.kjueaiud.com