[前言:] 擁有美麗的外觀,軟體就成功了一半。界面由控件、工具欄、菜單、窗體等元素組成,對他們進行美化就能得到一個美麗的界面。
讓控件更醒目
在ComboBox中改變清單框的寬度
我們經常會使用到組合框,而組合框是是有2種功能的--下拉和清單。一般情況下,清單框的寬度和選擇框是一樣寬的,但是我們有些時候确實很需要把清單框的寬度變大,一便讓我們能更好的看我們要選的東西。
為了能有這個功能,我寫了下面的這個函數。首先得在你的對話框中添加一個的WM_CTLCOLOR的消息句柄,或者使用CComboBox的繼承類,而且在其中添加下面的代碼:
HBrush tvisualcombo::onctlcolor(CDC* pdc, CWND* pwnd, UINT nctlcolor) { HBrush hbr = ccombobox::onctlcolor(pdc, pwnd, nctlcolor); switch (nctlcolor) { case ctlcolor_edit: break; case ctlcolor_listbox: if (listwidth > 0) { // new width of a listbox is defined CRect rect; pwnd->GetWindowRect(&rect); if (rect.Width() != listwidth) { rect.right = rect.left + listwidth; pwnd->MoveWindow(&rect); } } break; } // todo: return a different brush if the default is not desired return hbr; } |
這樣之後還沒有完全好,你還得重新整理一下清單框,那樣才能随時根據清單框中的文本的長度,而改變清單框的寬度,要想這樣的話,你還得這樣,你必須掃描清單框中的條目,還得計算其中文本的長度(通過pdc),這樣你如果再重新整理清單框的話,才能一條目中比較長的來顯示。
上面的方法是通過WM_CTLCOLOR消息來實作的,後來才知道在MFC的CComboBox類中有一個函數也可以實作同樣的功能,就是:
CComboBox::SetDroppedWidth(int width); |
通過這個函數,你可以把寬度設成你自己喜歡的值,而它的實際的寬度是下面2個值中的最大值:
1.你所設定的值(就是通過上面的函數所設定的值)
2.清單框的值
如何擷取一個對話控件的指針
有兩種方法。其一,調用CWnd: : GetDlgItem,擷取一個CWnd*指針調用成員函數。下例調用GetDlgItem,将傳回值傳給一個CSpinButtonCtrl*以便調用CSpinButtonCtrl : : SetPos 函數:
BOOL CSampleDialog : : OnInitDialog ( )
{
CDialog : : OnInitDialog ( ) ;
//Get pointer to spin button .
CSpinButtonCtrl * pSpin - ( CSpinButtonCtrl *) GetDlgItem (IDC_SPIN) ;
ASSERT _ VALID (pSpin) ;
//Set spin buttons default position .
pSpin —> SetPos (10) ;
return TRUE ;
}
其二, 可以使用ClassWizard将控件和成員變量聯系起來。在ClassWizard中簡單地選擇Member Variables标簽,然後選擇Add Variable …按鈕。如果在對話資源編輯器中,按下Ctrl鍵并輕按兩下控件即可轉到Add Member Variable對話。
VC中如何改變對框中控件的顔色
在VC中,當我們大量的運用控件時,往往會為改變控件的顔色所煩惱。因為VC不象VB那樣,可以友善地改變對話框及各個控件的顔色,要改變一個控件的顔色比較煩瑣。本文所介紹的就是如何改變在一個對框上的控件的顔色。步驟如下:
① 先建立一個基于對話框的工程,命名為test,然後在對話框上加入一個ListBox控件。
② 在testDlg.h中加入一個成員變量:CBrush m_brush;
③ 在OnInitDialog()中,加入m_brush.CreateSolidBrush( RGB(0,0,0 );此處設定的RGB值可以改變ListBox的背景色。為了觀看ListBox中字的顔色變化,我們給ListBox加入幾個字:利用Class Wizard給ListBox加入一個Control類型的成員變量m_ctrlListBox,然後在OnInitDialog()加入如下所示的代碼:
m_ctrlListBox.AddString("1号選手");
m_ctrlListBox.AddString("2号選手");
④ 點選Class Wizard,給testDlg加入WM_CTLCOLOR事件,單擊Edit Code按鈕,然後把改函數的内容替換為如下代碼:
if(nCtlColor== CTLCOLOR_LISTBOX)
{
pDC- >SetBkMode(TRANSPARENT);
pDC- >SetTextColor(RGB(255,255,255));
//此處設定字型的顔色
return (HBRUSH)m_brush.GetSafeHandle();
}
else
return CDialog::OnCtlColor (pDC, pWnd, nCtlColor);
現在編譯并運作改程式,可以看到清單框已經變成黑色而其中的字已經變為白色了!
工具欄和狀态條設計
在VC++下實作高彩色工具條
引言
一些Windows系統自帶程式如資料總管、Internet Explorer等程式的工具條看上去和其他一些程式的工具條不太一樣,在顔色上要漂亮許多。其實這些程式的工具條上的圖示均為256色,而普通應用程式在工具欄上所顯示圖示的顔色通常隻有16色,這就決定了後者在視覺上遠沒有前者美觀。由于Windows随系統而帶的程式也是由開發人員編寫的應用程式,這就說明通過程式編碼可以實作256色甚至更多色彩的圖示在工具欄上的顯示。為此筆者經過摸索,通過MFC程式設計在應用程式中實作了高彩色工具條。現将實作的主要方法介紹如下,以飨廣大讀者。
基本設計思路
在實作高彩色工具條之前,先研究一下普通16色的工具條的實作過程,并從中總結出改進方法。在VC的資源視圖中工具條是一個資源名為IDR_MAINFRAME的Toolbar型資源,并可通過在編輯按鈕上的圖示來完成工具條上圖示的繪制。雖然在資源視圖中工具條上各按鈕的圖示是互相獨立的,但在存儲時并非像圖示一樣儲存為ico格式檔案而是以bmp位圖格式儲存在磁盤上的。該位圖是一個由工具條上的按鈕圖示組成的長條型位圖圖像,中間沒有任何縫隙,在程式運作和在資源視圖對工具條進行編輯時該圖像首先裝載到一個圖像清單中,然後工具欄根據索引依次從圖像清單中将圖像顯示到工具條的各個按鈕上。由于VC限制工具欄上的圖示不能超出16色,是以不論是在資源視圖直接編輯位圖還是用複制粘貼等手段均無法擷取超出256色的工具條(注:用複制粘貼的方法雖然在編輯視圖中可以暫時顯示出256色的圖示,但在程式運作時仍會退化成16色)。
由于不能在資源視圖中通過編輯Toolbar資源實作16色以上的圖示,加之工具條在顯示時有并不直接從Toolbar擷取圖示而是從圖像清單中讀取,是以可以通過其他一些圖像處理軟體做好類似于工具條的bmp圖像(僅顔色比普通工具條bmp圖像豐富,其餘完全一樣),并以位圖的形式加入到程式資源。在使用時,先将其讀取到圖像清單,這樣圖像清單中用于顯示到工具條上的圖示的顔色就可以是256、24位、甚至32位色的了。由于工具條預設時将直接加載資源名為IDR_MAINFRAME的Toolbar型資源作為圖示的來源,是以還必須通過SetImageList()函數将含有高彩色工具條位圖的圖像清單指定為工具條的圖示來源。
真彩工具條的實作
由于工具條的建立是在主架構類的OnCreate()函數中完成的,是以高彩色圖像的裝載和圖像清單的替換工作必須也在此進行。在進行程式設計之前,需要做好各種準備工作,比如高彩色工具條位圖的繪制、高彩色位圖加入到資源等。繪制工具條位圖時,必須控制好圖像的尺寸,如需要有N個邊長為 M的圖示,那麼需要繪制的位圖尺寸為長=N*M;寬=M。真彩位圖在加入到工程之後就不能再在VC的資源視圖中進行編輯了。由于這個彩色位圖僅起到美化界面的作用,是以具體對的事件響應等工作還要通過設定原有的Toolbar資源來完成。
準備工作就緒後,先要把工具條位圖裝載到圖像清單,這樣才能被工具條做擷取。在作這一步時,必須用::LoadImage()函數去加載工具條位圖,并通過宏MAKEINTRESOURCE()來指定具體要加載哪一個資源位圖:
HBITMAP hbm = (HBITMAP)::LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_TOOLBAR), //加載IDB_TOOLBAR IMAGE_BITMAP, //按位圖格式 0,0, // cx,cy LR_CREATEDIBSECTION | LR_LOADMAP3DCOLORS ); LoadImage傳回一個位圖句柄HBITMAP,但在MFC下使用CBitmaps會更加友善,可建立一個 CBitmap對象并用Attach()成員函數将它與位圖句柄聯系起來: CBitmap bm; bm.Attach(hbm); |
MFC加載工具欄位圖時使用了一個内部函數AfxLoadSysColorBitmap()将預設顔色設定為16色,是以為了顯示16色以上的圖像,必須在調用圖像清單類CImageList的Create()函數建立圖像清單時對圖像清單做進一步的處理:
m_ilToolBar.Create(32,32,ILC_COLOR8, 4, 4); m_ilToolBar.Add(&bm,(CBitmap*)NULL); |
這裡用ILC_COLOR8标明了建立的圖像清單是256色的,在VC的commctrl.h中對其有定義,并且還提供有其他幾種顔色位深度的預定義:
#define ILC_COLOR4 0x0004 //16色 #define ILC_COLOR8 0x0008 //256色 #define ILC_COLOR16 0x0010 //16位色 #define ILC_COLOR24 0x0018 //24位色 #define ILC_COLOR32 0x0020 //32位色 |
如果使用的工具條位圖隻有256色(對于多數程式這樣已經足夠),則顯然沒有必要再使用更進階别的位深度定義。最後一步,也是最關鍵的一步,必須通過SetImageList()函數指定工具條m_wndToolBar的圖示來源不再是原來預設的圖像清單而是含有高彩色位圖的圖像清單m_ilToolBar:
m_wndToolBar.GetToolBarCtrl().SetImageList(&m_ilToolBar); |
到此為止就可以通過MFC在自己編寫的程式中實作類似于IE等軟體的漂亮的工具條了。下圖就是筆者用上述方法得到的程式界面:
小結
本文通過對作為工具條圖示來源的圖像清單的替換,實作了在普通MFC應用程式中具備了以往隻有Windows系統自帶程式才具備的高彩色工具條。較好地美化了程式的界面。本文程式在Windows 98下,由Microsoft Visual C++ 6.0編譯通過。
用VC制作非常酷的工具條
自微軟推出Windows 95後,一大批全新的控件使我們的應用程式更加美觀,使用也更加友善。其中一個顯著的變化就是工具條不再是一個突出的3D小方框,而是變成了平面的狀态,但 是隻要把滑鼠移動到上面,它就會自動地浮出來,大大友善了使用者。
筆者經過一段時間摸索,終于找到了制作這種工具條的方法。原來Windows 95封裝了許多常用的控件,大都被放在Comctrl32.dll中,其中Toolbar控件是用于制作工具條的。下面 簡要介紹一下如何在VC5.0中添加一個Toolbar。
衆所周知,所有的控件都是某一類型的視窗,是以制作Toolbar也要從制作視窗開始。由于MFC的Toolbar類并不支援新的功能,是以我們隻好用SDK方法,通過API調用來完成整個過程 ,該過程與制作一個傳統的工具條類似。
Toolbar是屬于comctrl32.dll的擴充功能,是以要先調用InitCommonControlsEx()的函 數。該函數有一個重要的參數決定了對Toolbar的支援,它的主要作用是注冊Toolbar視窗,以 便在以後的程式中制作這種視窗,而普通的工具條則要調用InitCommandControls()。需要注意的是這兩個函數的寫法。
INITCOMMONCONTROLSEX icex; DWORD dwStyle; icex.dwSize = sizeof(INITCOMMONCONTROLSEX); //注意下面這兩個參數決定了是否注冊Toolbar icex.dwICC=ICC_COOL_CLASSES|ICC_BAR_ CLASSES; InitCommonControlsEx(&icex); |
然後就可以調用CreateWindowEx這個函數來建立Toolbar視窗:
HWND hwndTB = CreateWindowEx( WS_EX_TOOLWINDOW, //擴充工具條風格 TOOLBARCLASSNAME, //Toolbar類名 NULL, WS_CHILD|WS_VISIBLE|TBSTYLE_FLAT, //視窗風格 0,0,0,0, //大小 AfxGetApp()->GetMainWnd(), //父視窗名 NULL, AfxGetInstanceHandle(), //執行個體 NULL); |
判斷一下視窗句柄,如果不為空,就表示視窗建立成功。此時的Toolbar不過是一個空空的視窗,我們可以根據需要向裡面添加按鈕。向Toolbar中添加按鈕是通過向它發送消息來 實作的,以下過程與制作傳統的工具條基本一緻。首先,建立一個ImageList控件,然後通過定義按鈕的資料結構來确定每個按鈕的類型。
// 建立一個Imagelist 控件, HWND himl; //MYICON_CX,MYICON_CY是每個按鈕的大小 himl= ImageList_Create(MYICON_CX,MYICON_CY,ILC_COLOR4,0,4); //加入事先作好的工具條位圖IDB_BITMAP2 ImageList_Add( himl, LoadBitmap(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDB_BITMAP2)),NULL); //通過消息把位圖加入到Toolbar中 SendMessage(hwndTB, TB_SETIMAGELIST, 0, (LPARAM)himl); |
下面加入5個普通的按鈕:
TBBUTTON tbArray[5]; //按鈕的資料結構 for(i=0;i<5;i++){ tbArray[i].iBitmap = i; //第i個位圖 tbArray[i].idCommand = IDM_BUTTONSTART+i; //指令ID tbArray[i].fsState = TBSTATE_ENABLED; tbArray[i].fsStyle = TBSTYLE_BUTTON; //按鈕風格 tbArray[i].dwData = 0; tbArray[i].iString = i; //顯示的字元串 } //設定按鈕結構的大小 ::SendMessage(hwndTB,TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0); //添加按鈕到Toolbar中 ::SendMessage(hwndTB,TB_ADDBUTTONS,(UINT)5, (LPARAM)tbArray); |
至此,一個很酷的工具條基本上制作完成,最後再調用函數ShowWindow()即可: ShowWindow(hwndTB, SW_SHOWMAXIMIZED);
當點選按鈕時,Toolbar就把消息傳送到父視窗中,由父視窗響應消息。Toolbar中按鈕的ID包含在消息函數的wParam參數中,可以設定它來排程不同的子產品。這時可以重載父視窗的O nCommand()函數,根據wParam參數判斷點選了哪個按鈕。假定父視窗是主視窗架構,代碼如下:
BOOL CMainFrame::OnCommand(WPARAM wParam,LPARAM lParam) { switch(wParam){ case IDM_BUTTONSTART+0: AfxMessageBox(“你點中了第一個按鈕!!", MB_ICONINFORMATION); break; case IDM_BUTTONSTART+1: AfxMessageBox(“你點中了第二個按鈕!!",MB_ICONINFORMATION); break; case IDM_BUTTONSTART+2: AfxMessageBox(“你點中了第三個按鈕!!", MB_ICONINFORMATION); break; } return CMainFrame::OnCommand(wParam,lParam); } |
Visual C++ 版本6中工具條的新特色
微軟在www.microsoft.com/visualc已經推出Visual C++6.0預覽版幾個月了。正式版預計到今年年底釋出。同時,預覽版顯示出版本6将包含大量的改進和提高,包括支援Internet控件,例如扁平工具條等。雖然改進的控件包與Internet無關,但它首先出現在Internet Explorer中,是以它就被取做這個名字了。事實上,官方釋出的預覽版的标題是“針對Internet Explorer 4.0的Visual C++ 5.0技術預覽”。
在以前關于MFC工具條類的讨論專題中,我曾答應提供一個在版本6中工具條的外觀示範。有一個很好的消息,那就是你現在用CToolBar所作的所有工作在新的版本中都是有效的,包括那些在以前的欄目中所描述的一些擴充功能。是以,你将很容易修改現存的程式以獲得象Internet Explorer和Visual Studio中那樣“酷”的界面。此外,并沒有什麼壞消息。
工具條的新特色
早在版本4中,CToolBar就已被MFC庫完全實作了。一旦公用控件動态連結庫(命名為comctl32.dll)變得無所不在了,CToolBar就成了如今已包含在作業系統中的工具條控件的代名詞了。然而,CToolBar并沒有揭示公用工具條控件的所有能力。如今,通過CreateEx()函數,它成功了。
公用控件動态連結庫現在包含了至少三類風格:最初的、在Internet Explorer3.0中加入的以及在Internet Explorer 4.0中加入的。雖然這些版本理論上是向下相容的,但某些專業人員曾寫出一些不能在後來版本中正常運作的應用程式,這可能是這些程式采用了一些沒有公開的功能,而這些功能并沒有被包含在所有的版本中。
Visual C++程式員沒有這樣的經曆,因為在Visual C++4.0或5.0中comctl32.dll并不是一個可以再分發的元件,它在安裝Internet Explorer時被更新,是以MFC程式員無法依靠最新版本的某些功能來用于他們的程式。這就是CToolBar僅僅具有最初的DLL的有限功能的原因。CToolBar能夠實作最新的特色意味着微軟将在Visual C++6.0中包含最新的DLL并将其作為一個可以再分發的元件。
絕大多數新特色将由在調用CreateEx()和其它CToolBar成員函數時指定的新的風格标志來确定。下面是commctrl.h的一部分,它定義了TBSTYLE類辨別符:
#define TBSTYLE_BUTTON 0x0000
#define TBSTYLE_SEP 0x0001
#define TBSTYLE_CHECK 0x0002
#define TBSTYLE_GROUP 0x0004
#define TBSTYLE_CHECKGROU TBSTYLE_GROUP | TBSTYLE_CHECK)
#if (_WIN32_IE $#@62;= 0x0300)
#define TBSTYLE_DROPDOWN 0x0008
#endif
#if (_WIN32_IE $#@62;= 0x0400)
#define TBSTYLE_AUTOSIZE 0x0010
#define TBSTYLE_NOPREFIX 0x0020
#endif
#define TBSTYLE_TOOLTIPS 0x0100
#define TBSTYLE_WRAPABLE 0x0200
#define TBSTYLE_ALTDRAG 0x0400
#if (_WIN32_IE $#@62;= 0x0300)
#define TBSTYLE_FLAT 0x0800
#define TBSTYLE_LIST 0x1000
#define TBSTYLE_CUSTOMERASE 0x2000
#endif
#if (_WIN32_IE $#@62;= 0x0400)
#define TBSTYLE_REGISTERDROP 0x4000
#define TBSTYLE_TRANSPARENT 0x8000
#define TBSTYLE_EX_DRAWDDARROWS 0x00000001
#endif
你會注意到其中的一些采用了條件編譯,依賴于_WIN32_IE的值,它預設指的是Internet Explorer 4.0(即取值為0x0400)。對于Internet Explorer 3.0(即取值為0x0300)以前的版本,大多數的TBSTYLE辨別符指的是按鈕或是一組按鈕。Internet Explorer3.0引入了扁平鈕、文本标簽、下拉清單和自定義繪制。Internet Explorer 4.0增強了下拉清單和自定義繪制功能,并且增加了支援OLE拖動目标到一個工具條。
扁平鈕和把手
在過去的18個月中我常常被問及該如何獲得象Internet Explorer和Visual Studio中的工具條一樣不使用浮雕按鈕而是用扁平鈕并且帶有便于移動和定位的把手那樣酷的界面。這些特色并不被MFC所支援,是以最簡單擷取的方法就是購買一個擴充庫。而對于Visual C++ 6.0來說卻無須多此一舉,因為它使得CToolBar類實作了對扁平鈕、把手和其它新的視覺效果的支援。
在預覽版中,AppWizard并不會自動包括這些新特色,但它們卻很容易被加入。表1顯示了AppWizard建立的主架構視窗的OnCreate()函數,表2顯示了需要做哪些修改以獲得具有扁平鈕和把手的工具條。圖1顯示了表1建立出的工具條,而圖2顯示出了表2實作的工具條。
表 1: CMainFrame::OnCreate as generated by AppWizard
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if(CMDIFrameWnd::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)))
圖1
表2: Adding flat buttons and the gripper
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if(CMDIFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
if(!m_wndToolBar.CreateEx(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 dont want tool tips or a resizeable toolbar
m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
CBRS_GRIPPER | CBRS_BORDER_3D | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
// TODO: Delete these three lines if you dont want the toolbar to
// be dockable
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);
return 0;
}
圖2
為了作出扁平按鈕我必須使用CreateEx()來代替Create()。這個新的函數在afxext.h中聲明:
BOOL CreateEx
(
CWnd* pParentWnd, // parent window
DWORD dwCtrlStyle = TBSTYLE_FLAT, // extended style
DWORD dwStyle = // style
WS_CHILD | WS_VISIBLE | CBRS_ALIGN_TOP,
CRect rcBorders = CRect(0,0,0,0), // border rectangle
UINT nID = AFX_IDW_TOOLBAR // identifier
);
因為擴充風格預設指的就是TBSTYLE_FLAT,是以我要得到扁平按鈕就隻需要簡單地将AppWizard形成的代碼中的Create()改為CreateEx()即可。我将在後面實作其它的擴充風格。為了獲得把手,我必須在調用SetBarStyle()函數時包含CBRS_GRIPPER标志,參看表2。這是CControlBar類的一個新風格,而CToolBar類是從它繼承而來的。請注意到我也加入了CBRS_BORDER_3D标志,這是為了修正一個未知的繪制問題,該問題将會在工具條的邊緣繪制一些多餘的點。這也許意味着預覽版确實有這個問題,因為一旦我将3D标志加入就立即解決了并且也似乎沒有影響到别的什麼。
上面所作的兩個簡單的改變是使得一個已存程式獲得酷界面的最省力的方法。在一個程式具有了扁平鈕和把手的同時,它也不會發生不應有的其它改變。(未完)
标題欄上添加按鈕
自從Windows作業系統出現以後,在應用程式中進行人機互動的思想和手段便發生了根本性的改變,計算機的界面變得友好精彩。一個程式的好壞很大程度上決定于人機互動的友善程度。目前,大多數程式的标題欄都千篇一律,如何讓自己的程式與衆不同是每個程式員的夢想,但改變标題欄的内容的确有相當的難度。該篇文章向你介紹如何在标題欄上添加圖示按鈕,而且當滑鼠經過和點選該圖示時,滑鼠将有不同的反應。請按照下面的步驟實作。
第一步:打開VC程式設計環境,生成一個新的基于單文檔的工程temp,所有的選項都取預設值,下面,我們就在此工程的标題欄上生成三個按鈕圖示。
第二步:下載下傳資源檔案,共有三個檔案:CaptionButton.cpp、CaptionButton.h和Thunk.h。将這三個檔案添加到工程中(添加方法不必細說了吧)。
第三步:在Mainfrm.h中定義變量CCaptionButton cbExtra;,當然要包含頭檔案#include "CaptionButton.h"。
第四步:為工程加載位圖資源,ID号分别為IDB_BITMAP1、IDB_BITMAP2、IDB_BITMAP3、IDB_BITMAP6、IDB_BITMAP7。這些位圖将顯示在标題欄上,至于用什麼樣的位圖就看你的喜好了。
第五步:在Mainfrm.cpp的OnCreate函數中添加如下的代碼:
//初識化,m_hWnd是我們要處理的視窗句柄 cbExtra.Init(m_hWnd); // // 設定标題欄上的原來的按鈕(最大化、最小化和關閉)保留的數目 cbExtra.SetNumOfDefaultCaptions(3); // 設定位圖的透明顔色 COLORREF crTransparent = RGB(255,0,255); cbExtra.SetTransparentColor(crTransparent); // 滑鼠選中一個位圖後該位圖的樣子 cbExtra.SetSelectionBitmap((HBITMAP)LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_BITMAP7), IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS|LR_DEFAULTCOLOR)); // 滑鼠移動到一個位圖後,該位圖的變化 HBITMAP hMouseOverBitmap = (HBITMAP)LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_BITMAP2), IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS|LR_DEFAULTCOLOR); // 設定位圖1 HBITMAP hCaptionAMBitmap = (HBITMAP)LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_BITMAP3), IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS|LR_DEFAULTCOLOR); HBITMAP hCaptionAMBitmapHilite = CCaptionButton::CombineBitmaps(hCaptionAMBitmap, hMouseOverBitmap, crTransparent); // 設定位圖2 HBITMAP hCaption2Bitmap = (HBITMAP)LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_BITMAP6), IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS|LR_DEFAULTCOLOR); HBITMAP hCaption2BitmapHilite = CCaptionButton::CombineBitmaps(hCaption2Bitmap, hMouseOverBitmap,crTransparent); // 設定位圖三 HBITMAP hCaption3Bitmap = (HBITMAP)LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_BITMAP1), IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS|LR_DEFAULTCOLOR); HBITMAP hCaption3BitmapHilite = CCaptionButton::CombineBitmaps(hCaption3Bitmap, hMouseOverBitmap,crTransparent); // 利用上面的定義建立标題欄上的位圖,寶庫位圖的ID号,滑鼠經過時的 file://變換位圖,滑鼠選擇時的變換位圖和提示文字。 cbExtra.New(1,hCaptionAMBitmapHilite,hCaptionAMBitmap,"guan"); cbExtra.New(2,hCaption2BitmapHilite,hCaption2Bitmap,"xi"); cbExtra.New(3,hCaption3BitmapHilite,hCaption3Bitmap,"ming"); |
第六步:現在我們可以先停下來看看我們的成果如何。編譯我們的工程,運作,我們可以發現,我們的标題欄上增加了三個按鈕,滑鼠在上面移動或點選的時候,位圖會發生變化。隻是我們還沒有添加在位圖上點選是的處理函數,不要急,接下來我們就添加相應的處理函數。
第七步:當我們在标題欄上點選圖示時,将有一個消息WM_CBLBUTTONCLICKED産生,參數WPARAM是指點選圖示的ID号。
在mainfrm.h中定義消息映射函數afx_msg LRESULT Hit(WPARAM wParam, LPARAM lParam);
在mainfrm.cpp中定義函數實作:
ON_MESSAGE(WM_CBLBUTTONCLICKED, Hit) LRESULT CMainFrame::Hit(WPARAM wParam, LPARAM lParam) { switch(wParam) {// begin wParam case 1: AfxMessageBox("第一個CAPtion"); break; case 2: AfxMessageBox("第二個Caption"); break; case 3: AfxMessageBox("第三個Caption"); break; } return 1; } |
這樣,當我們單擊圖示時将彈出不同的提示對話框,這隻是一個例子,至于實作什麼樣的功能随你的便了。
第八步:标題欄的動态改變。在程式的執行過程中如果你要改變标題欄的樣子你同樣可以實作,下面分别給出如何删除一個圖示和更改一個圖示的樣子。
void CMainFrame::OnDelete() { cbExtra.Delete(1); } void CMainFrame::OnChange() { cbExtra.Replace(1, 1, hCaption4BitmapHilite, hCaption4Bitmap, "pNewToolTipText"); } |
好了,功能實作了,還算滿意吧,希望對你有用。
通過例程分析狀态條用法
狀态條是一個包含資訊的控制條,通常用于資訊和狀态提示,這裡資訊是有關菜單指令或工具指令的提示字元串以及其它訓示/幫助資訊,而狀态是用來訓示SCROLL LOCK 和NUM LOCK等一些鍵的狀态。狀态條通常架構視窗的底部。狀态條的資訊行能顯示有關的程式狀态或滑鼠指向的工具按鈕或菜單項的資訊。狀态條既不能接受使用者輸入,也不産生指令資訊。
實際上,從程式設計人員的角度出發(至少是那些使用AppWizard建立應用程式的程式設計人員),狀态條是如此普通,它們并不像工具條那樣允許使用者編輯的資源。在建立程式架構時,使用者隻需告訴AppWizard為應用程式包含一個狀态條,可以說,此時,使用者的工作就完成了。但是,我們如果巧妙使用工具條,我們會發現它可以幫我們實作很多功能。
按以下做法我們實作在狀态條上顯示滾動字元串,滑鼠的坐标,動态時鐘。
第一步:
運作AppWizard生成一個工程mystatus,接受所有的預設設定,除了下面兩步:在step 1中選Single Document ;step 4 中去掉Docking Bar 前的對鈎,然後點選 Advanced 按鈕,選擇Window Styles 中的 Maximized選項。點選Finish按鈕,此時我們生成了一個工程。運作我們可以發現程式預設生成的狀态條,接下來我們要對這個狀态條進行修改。
第二步:
在這一步我們将實作把狀态條移到菜單的下邊。在MainFrm.cpp中我們可以看到狀态條的定義部分
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 } 我們所要做的是将上面的定義改為 if(!m_wndStatusBar.Create(this, WS_CHILD|WS_VISIBLE|CBRS_TOP,AFX_IDW_STATUS_BAR) || !m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT))) { TRACE0("Failed to create status bar\n"); return -1; // fail to create } |
運作程式,這時我們可以發現,狀态條已經移到了上邊。
第三步:
在這一步,我們實作在狀态條實作滾動文字。
< 1 > 在MainFrm.cpp中,我們可以發現如下的定義
static UINT indicators[] = { ID_SEPARATOR, ID_INDICATOR_CAPS, ID_INDICATOR_NUM, ID_INDICATOR_SCRL, }; 我們将其改為 static UINT indicators[] = { ID_SEPARATOR, ID_STATUS1, ID_STATUS2, ID_STATUS3, ID_INDICATOR_CAPS, ID_INDICATOR_NUM, ID_INDICATOR_SCRL, }; |
<2>在菜單VIEW中選擇Resource Symbols , 在對話框中選擇NEW, 在NAME中輸入ID_STATUS1,VALUE中取預設值,同樣方法定義ID_STATUS2,ID_STATUS3。
<3>定義字元串資源,在ResouceView中選擇String Table,在其中為ID_STATUS1定義字元串資源為"me",同樣方法定義ID_STATUS2,ID_STATUS3。
<4>在MainFrm.h中定義如下變量:
public:
CString str,str1;
<5>在MainFrm.cpp中修改OnCreate函數如下:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; if(!m_wndStatusBar.Create (this,WS_CHILD|WS_VISIBLE|CBRS_TOP,AFX_IDW_STATUS_BAR) || !m_wndStatusBar.SetIndicators(indicators,sizeof(indicators)/sizeof(UINT))) { TRACE0("Failed to create status bar\n"); return -1; // fail to create } m_wndStatusBar.SetPaneInfo(1,ID_STATUS1,SBPS_POPOUT,320); m_wndStatusBar.SetPaneInfo(2,ID_STATUS2,SBPS_POPOUT,100); m_wndStatusBar.SetPaneInfo(3,ID_STATUS3,SBPS_POPOUT,100); str1=str="你好,歡迎使用本程式,祝你合家幸福,事業有成!"; SetTimer(1,200,NULL); SetTimer(2,10,NULL); return 0; } |
<6>在類CmainFrame中重載函數OnTimer(),并添加如下代碼:
void CMainFrame::OnTimer(UINT nIDEvent) { file:// TODO: Add your message handler code here and/or call default if(nIDEvent==1){ if(str.IsEmpty()) str=str1; str=str.Right(str.GetLength()-2); m_wndStatusBar.SetPaneText(1,str); } if(nIDEvent==2){ SYSTEMTIME t; ::GetLocalTime(&t); CString str2; str2.Format("%d:%d:%d:%d",t.wHour,t.wMinute,t.wSecond,t.wMilliseconds); m_wndStatusBar.SetPaneText(3,str2); } CFrameWnd::OnTimer(nIDEvent); } |
<7>将MainFrm.h中,定義m_wndStatusBar之前的 protected: 改為public:
<8>通過類向導在類CmystatusView中重載WM_MOUSEMOVE,并在實作函數中添加如下代碼:
void CMystatusView::OnMouseMove(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CString str3; str3.Format("X:%d,Y:%d",point.x,point.y); ((CMainFrame*)AfxGetMainWnd())->m_wndStatusBar.SetPaneText(2,str3); CView::OnMouseMove(nFlags, point); } |
好了,到這裡我們所有的功能就都實作了,快編譯運作一下吧!怎麼樣?還滿意嗎?
讓标題欄文字居中
|添加以下子產品:
PublicSubCenterC(frmAsForm)
DimSpcFAsIntegerHowmanyspacescanfit
DimclenAsIntegercaptionlength
DimoldcAsStringoldcaption
DimiAsIntegernotimportant
removeanyspacesattheendsofthecaption
veryeasyifyoureaditcarefully
oldc=frm.Caption
DoWhileLeft(oldc,1)=Space(1)
DoEvents
oldc=Right(oldc,Len(oldc)-1)
Loop
DoWhileRight(oldc,1)=Space(1)
DoEvents
oldc=Left(oldc,Len(oldc)-1)
Loop
clen=Len(oldc)
IfInStr(oldc,"!")$#@60;$#@62;0Then
IfInStr(oldc,"")$#@60;$#@62;0Then
clen=clen*1.5
Else
clen=clen*1.4
EndIf
Else
IfInStr(oldc,"")$#@60;$#@62;0Then
clen=clen*1.4
Else
clen=clen*1.3
EndIf
EndIf
seehowmanycharacterscanfit
SpcF=frm.Width/61.2244howmanyspacecanfit itthecaption
SpcF=SpcF-clenHowmanyspacescanfit-Howmuch spacethe
tiontakesup
Nowthetrickypart
IfSpcF$#@62;1Then
DoEventsspeeduptheprogram
frm.Caption=Space(Int(SpcF/2))+oldc
Elseiftheformistoosmallforspaces
frm.Caption=oldc
EndIf
EndSub
|在窗體中添加以下代碼:
DimoldsizeAsLong
PrivateSubForm_Resize()
IfMe.Width=oldsizeThenifthewidthhasnt changed
ExitSubthendontmesswithit
Else
CenterCMe
oldsize=Me.Width
EndIf
EndSub
PrivateSubForm_Load()
CenterCMe
oldsize=Me.Width
EndSub
設計漂亮實用的菜單
談在VC中動态改變菜單
大部分Windows應用程式都使用下拉式菜單實作自己特定的函數,它使程式設計更加友善,不需要在程式中增加多個按鈕以完成這些操作。大多數情況下,我們的程式編譯生成後,菜單就确定了,不能再修改。然而,在很多情況下,程式要根據使用者的自己設定産生不同的菜單以适應不同使用者的要求,這就需要我們動态的改變菜單。接下來我們就分析如何動态的生成不同的菜單。
第一步:
運作AppWizard生成一個工程mymenu,接受所有的預設設定,除了下面一步:在step 1中選Single Document ,點選Finish按鈕,此時我們生成了一個工程。編譯運作,我們可以發現程式預設生成的菜單,接下來我們要對這個菜單進行修改。
第二步:
添加一個菜單資源,按如下步驟:菜單中選擇InsertàResouceàMenuàNew ,我們可以看到添加了一個ID号為IDR_MENU1的菜單資源,至于菜單中的各項你就随便添加了,為了我們下面的程式設計,請你添加幾項,第一列要包含幾個子菜單。
第三步:
添加一個對話框資源,按如下步驟:菜單中選擇InsertàResouceàDialogàNew ,我們可以看到添加了一個ID号為IDD_DIALOG1的對話框資源,添加六個按鈕,如下圖:
ID号依次為ID_CLEAR, ID_GOONE ,ID_GOTWO, ID_ADD, ID_ADDITEM, ID_EDIT ,為對話框定義類名為CChangeMenu, 輕按兩下添加的六個按鈕增加處理函數,用預設函數名。
第四步:
在IDR_MAINFRAME菜單中添加一項,ID号為ID_SET,名為"彈出設定對話框",為其添加處理函數,添加如下代碼:
CChangeMenu dlg;
dlg.DoModal();
當然,别忘了在檔案的開頭添加#include "changemenu . h"
第五步:
找到添加的六個按鈕的處理函數,依次添加如下的代碼:
void CChangeMenu::OnClear()
{
AfxGetMainWnd()->SetMenu(NULL);
AfxGetMainWnd()->DrawMenuBar();
}
void CChangeMenu::OnGoone()
{
if(!menu1){
menu1.LoadMenu(IDR_MENU1);
AfxGetMainWnd()->SetMenu(&menu1);
AfxGetMainWnd()->DrawMenuBar();
menu1.Detach();
}
}
void CChangeMenu::OnGotwo()
{
if(!menu2){
menu2.LoadMenu(IDR_MAINFRAME);
AfxGetMainWnd()->SetMenu(&menu2);
AfxGetMainWnd()->DrawMenuBar();
menu2.Detach();
}
}
void CChangeMenu::OnAdd()
{
static int x=400;
AfxGetMainWnd()->GetMenu()->AppendMenu(MF_STRING,x,"mynew");
AfxGetMainWnd()->DrawMenuBar();
x++;
}
void CChangeMenu::OnAdditem()
{
static int y=500;
AfxGetMainWnd()->GetMenu()->GetSubMenu(0)->InsertMenu(1,MF_BYPOSITION,y,"mynewitem");
AfxGetMainWnd()->DrawMenuBar();
y++;
}
void CChangeMenu::OnEdit()
{
AfxGetMainWnd()->GetMenu()->ModifyMenu(0,MF_BYPOSITION,402,"dfd");
AfxGetMainWnd()->DrawMenuBar();
}
好了,到這裡我們所有的功能就都實作了,快編譯運作一下吧!怎麼樣?還滿意嗎
談在VC中動态改變菜單
如何用VC++5在菜單中增加位圖或圖示
我們在使用Windows 95時,可以注意到在“開始”組中的菜單項前都有一個圖示,而且在Word 97中的菜單項前也有一個圖示。這些圖示不但讓我們清楚地了解到螢幕上的各種工具按鈕與各個菜單項之間的聯系,而且還增加了應用程式界面的美觀。那麼,請問如何用Visual C++ 5.0在應用程式菜單中增加圖示?
MFC的CMenu類有一個成員函數SetMenuItemBitmaps,可以用于往菜單中增加圖示。具體方法如下:
在應用程式的資源檔案中添加想要增加的位圖,并将其ID命名為IDB_OPEN1和IDB_OPEN2;---在應用程式的視圖類中添加CBitmap類的對象,不妨取名為bm_open1和bm_open2。在視圖類的構造函數中添加以下代碼:
bm_open1.LoadBitmap(IDB_OPEN1);bm_open2.LoadBitmap(IDB_OPEN2);(函數LoadBitmap用于加載位圖到CBitmap類的對象)在視圖類的成員函數OnDraw()中添加以下代碼:
CWnd*parent=GetParent();CMenu*pmenubar=parent-$#@62;GetMenu();
CMenu*pmenu=pmenubar-$#@62;GetSubMenu(2);
pmenu-$#@62;SetMenuItemBitmaps(1,MF_BYPOSITION,&bm_open1,&bm_open2);
前三行得到指向菜單的指針,第四行調用函數SetMenuItemBitmaps往菜單中增加圖示,具體參數的含義可參見有關介紹MFC及其成員函數的書即可。
自繪菜單
在這裡提供一個C++類(CCustomMenu),該類是CMenu的子類,并且擁有自繪能力。它可以向你提供以下的功能:
- 設定字型顔色。
- 設定高亮度顔色。
- 設定高亮度時的風格。
- 設定選中時和在普通狀态下的菜單顯示的圖示。
- 設定顯示圖示大小。
在CCustomMenu中定義了結構MENUDATA,你必須根據你的需要填充該結構,并且在增加菜單時提供該結構的指針(調用AppendMenu,InsertMenu)。下面是一個例子:
1、定義CCustomMenu的執行個體,和MENUDATA結構變量。
CCustomMenu m_cCustomMenu;
MENUDATA menuData [8];
// as many menu items are present ,
//You should be able to use
//new and do the same
2、調用CreateMenu()設定有關參數。
m_customMenu.CreateMenu ();
m_customMenu.SetIconSize (25,25);
//This is to set the size of the Icon.
// This should be used only once for any menu
// in order to resize it, destroy and create the menu again with different size.
m_customMenu.SetHighlightStyle (Normal); //Or TextOnly, if you want the
// background color to remain the same
// and the Text color to change to the Highlight color.
// The following setXXXColor sets the menu colors.
//If you dont want to change any, Dont call these member functions.
m_customMenu.SetTextColor (RGB (255,0,0));
m_customMenu.SetBackColor (RGB (255,255,255));
m_customMenu.SetHighlightColor (RGB (0,0,255));
3、設定MENUDATA變量,并增加菜單項。
lstrcpy (menuData[0].menuText , "text1");
menuData[0].menuIconNormal= IDI_ICON1;
m_customMenu.AppendMenu (MF_OWNERDRAW,3,(LPCTSTR)menuData);
3、在你的視窗中重載OnMeasureItem(...)函數。
void CMyView::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
if ( lpMeasureItemStruct->CtlType == ODT_MENU &&
IsMenu((HMENU)lpMeasureItemStruct->i ID) &&
(lpMeasureItemStruct->itemID == (UINT)m_hMenuSub) )
{
m_customMenu.MeasureItem (lpMeasureItemStruct);
}
else
// let MFCs self-drawing handle it
CView::OnMeasureItem(nIDCtl, lpMeasureItemStruct);
}
下面的函數将幫助你設定菜單屬性。
void SetTextColor (COLORREF );
void SetBackColor (COLORREF);
void SetHighlightColor (COLORREF);
void SetIconSize (int, int);
void SetHighlightStyle (HIGHLIGHTSTYLE );
// HIGHLIGHTSTYLE : enum {Normal, TextOnly}
void SetHighlightTextColor (COLORREF);
下面是檔案代碼:
//*************************************************************************
// CustomMenu.h : header file
//
#if
!defined(AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_)
#define AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
class MENUDATA
{
public:
MENUDATA () { menuIconNormal = -1; menuIconSelected = -1;};
char menuText[32];
UINT menuIconNormal;
UINT menuIconSelected;
};
typedef enum {Normal,TextOnly} HIGHLIGHTSTYLE;
///
//
// CCustomMenu window
class CCustomMenu : public CMenu
{
// Construction
public:
CCustomMenu();
// Attributes
public:
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CCustomMenu)
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CCustomMenu();
virtual void DrawItem( LPDRAWITEMSTRUCT);
virtual void MeasureItem( LPMEASUREITEMSTRUCT );
void SetTextColor (COLORREF );
void SetBackColor (COLORREF);
void SetHighlightColor (COLORREF);
void SetIconSize (int, int);
void SetHighlightStyle (HIGHLIGHTSTYLE );
void SetHighlightTextColor (COLORREF);
// Generated message map functions
protected:
COLORREF m_crText;
COLORREF m_clrBack;
COLORREF m_clrText;
COLORREF m_clrHilight;
COLORREF m_clrHilightText;
LOGFONT m_lf;
CFont m_fontMenu;
UINT m_iMenuHeight;
BOOL m_bLBtnDown;
CBrush m_brBackground,m_brSelect;
CPen m_penBack;
int m_iconX,m_iconY;
HIGHLIGHTSTYLE m_hilightStyle;
//{{AFX_MSG(CCustomMenu)
// NOTE - the ClassWizard will add and remove member functions here.
//}}AFX_MSG
};
///
//
//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
#endif //!defined(AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_)
//*************************************************************************
// CustomMenu.cpp : implementation file
//
#include "stdafx.h"
#include "CustomMenu.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
///
//
// CCustomMenu
CCustomMenu::CCustomMenu()
{
m_clrText = GetSysColor (COLOR_MENUTEXT);
m_clrBack = GetSysColor (COLOR_MENU);
m_brBackground.CreateSolidBrush (m_clrBack);
m_penBack.CreatePen (PS_SOLID,0,m_clrBack);
m_crText = m_clrText;
m_bLBtnDown = FALSE;
m_iconX = GetSystemMetrics ( SM_CXMENUCHECK);
m_iconY = GetSystemMetrics (SM_CYMENUCHECK );
m_clrHilight = GetSysColor (COLOR_HIGHLIGHT);
m_brSelect.CreateSolidBrush (m_clrHilight);
m_clrHilightText = GetSysColor (COLOR_HIGHLIGHTTEXT);
ZeroMemory ((PVOID) &m_lf,sizeof (LOGFONT));
NONCLIENTMETRICS nm;
nm.cbSize = sizeof (NONCLIENTMETRICS);
//Get the system metrics for the Captionfromhere
VERIFY (SystemParametersInfo (SPI_GETNONCLIENTMETRICS,0,&nm,0));
m_lf = nm.lfMenuFont;
m_iMenuHeight = nm.iMenuHeight;
m_fontMenu.CreateFontIndirect (&m_lf);
}
CCustomMenu::~CCustomMenu()
{
if ((HBRUSH) m_brBackground != NULL)
m_brBackground.DeleteObject ();
if ((HFONT)m_fontMenu !=NULL)
m_fontMenu.DeleteObject ();
if ((HBRUSH)m_brSelect != NULL)
m_brSelect.DeleteObject ();
}
///
//
// CCustomMenu message handlers
void CCustomMenu::DrawItem (LPDRAWITEMSTRUCT lpDIS)
{
ASSERT(lpDIS != NULL);
CDC* pDC = CDC::FromHandle(lpDIS->hDC);
CRect rect;
HICON hIcon;
COLORREF crText = m_crText;
// draw the colored rectangle portion
rect.CopyRect(&lpDIS->rcItem);
// draw the up/down/focused/disabled state
UINT action = lpDIS->itemAction;
UINT state = lpDIS->itemState;
CString strText;
LOGFONT lf;
lf = m_lf;
CFont dispFont;
CFont *pFont;
//GetWindowText(strText);
if (lpDIS->itemData != NULL)
{
strText = (((MENUDATA*) (lpDIS->itemData))->menuText);
if ((((MENUDATA *)(lpDIS->itemData))->menuIconNormal) == -1)
hIcon = NULL;
else if (state & ODS_SELECTED)
{
if ((((MENUDATA *)(lpDIS->itemData))->menuIconSelected) != -1)
hIcon = AfxGetApp ()->LoadIcon ((
(MENUDATA *)(lpDIS->itemData))->menuIconSelected);
else
hIcon = AfxGetApp()->LoadIcon ((
(MENUDATA*)(lpDIS->itemData))->menuIconNormal);
}
else
hIcon = AfxGetApp()->LoadIcon (
((MENUDATA*)(lpDIS->itemData))->menuIconNormal);
TRACE1 ("Draw for %s\n", strText);
}
else
{
strText.Empty();
hIcon = NULL;
}
if ( (state & ODS_SELECTED) )
{
// draw the down edges
CPen *pOldPen = pDC->SelectObject (&m_penBack);
//You need only Text highlight and thats what you get
if (m_hilightStyle != Normal)
{
pDC->FillRect (rect,&m_brBackground);
}
else
{
pDC->FillRect (rect,&m_brSelect);
}
pDC->SelectObject (pOldPen);
pDC->Draw3dRect (rect,GetSysColor (COLOR_3DHILIGHT),GetSysColor(COLOR_3DSHADOW));
lf.lfWeight = FW_BOLD;
if ((HFONT)dispFont != NULL)
dispFont.DeleteObject ();
dispFont.CreateFontIndirect (&lf);
crText = m_clrHilightText;
//While selected move the text a bit
TRACE0 ("SELECT,SELECTED\n");
}
else
{
CPen *pOldPen = pDC->SelectObject (&m_penBack);
pDC->FillRect (rect,&m_brBackground);
pDC->SelectObject (pOldPen);
// draw the up edges
pDC->Draw3dRect (rect,m_clrBack,m_clrBack);
if ((HFONT)dispFont != NULL)
dispFont.DeleteObject ();
dispFont.CreateFontIndirect (&lf); //Normal
TRACE0 ("SELECT, NORMAL\n");
}
// draw the text if there is any
//We have to paint the text only if the image is nonexistant
if (hIcon != NULL)
{
if(DrawIconEx (pDC->GetSafeHdc(),rect.left,rect.top,hIcon,
(m_iconX)?m_iconX:32,(m_iconY)?m_iconY:32,0,NULL,DI_NORMAL))
TRACE0("Wrote the icon successfully\n");
else
TRACE0 ("SORRY.NOGO\n");
}
//This is needed always so that we can have the space for check marks
rect.left = rect.left +((m_iconX)?m_iconX:32);
if ( !strText.IsEmpty())
{
// pFont->GetLogFont (&lf);
int iOldMode = pDC->GetBkMode();
pDC->SetBkMode( TRANSPARENT);
pDC->SetTextColor( crText);
pFont = pDC->SelectObject (&dispFont);
TRACE1( "About To DrawText %s\n",strText);
pDC->DrawText (strText,rect,DT_LEFT|DT_SINGLELINE|DT_VCENTER);
TRACE0("Done\n");
pDC->SetBkMode( iOldMode );
pDC->SelectObject (pFont); //set it to the old font
}
dispFont.DeleteObject ();
}
void CCustomMenu::MeasureItem( LPMEASUREITEMSTRUCT lpMIS )
{
CDC *pDC = AfxGetApp()->m_pMainWnd->GetDC();
CFont* pFont = pDC->SelectObject (&m_fontMenu);
int iconX = 0,iconY= 0;
TEXTMETRIC tm;
pDC->GetTextMetrics (&tm);
pDC->SelectObject (pFont);
AfxGetApp()->m_pMainWnd->ReleaseDC (pDC);
if (m_iconX)
iconX = m_iconX;
if (m_iconY)
iconY = m_iconY;
lpMIS->itemWidth = iconX + tm.tmAveCharWidth
* lstrlen(((MENUDATA*)(lpMIS->itemData))->menuText) +10;
lpMIS->itemHeight = (iconY > (m_iMenuHeight+1))?iconY:m_iMenuHeight + 1;
}
void CCustomMenu::SetIconSize (int width, int height)
{
m_iconX = width;
m_iconY = height;
}
void CCustomMenu::SetTextColor (COLORREF clrText)
{
m_crText = clrText;
}
void CCustomMenu::SetBackColor (COLORREF clrBack)
{
m_clrBack = clrBack;
if ((HBRUSH)m_brBackground != NULL)
m_brBackground.DeleteObject ();
m_brBackground.CreateSolidBrush (clrBack);
}
void CCustomMenu::SetHighlightColor (COLORREF clrHilight)
{
m_clrHilight = clrHilight;
if ((HBRUSH)m_brSelect != NULL)
m_brSelect.DeleteObject ();
m_brSelect.CreateSolidBrush (clrHilight);
}
void CCustomMenu::SetHighlightTextColor (COLORREF clrHilightText)
{
m_clrHilightText = clrHilightText;
}
void CCustomMenu::SetHighlightStyle (HIGHLIGHTSTYLE hilightStyle)
{
m_hilightStyle = hilightStyle;
}
//*************************************************************************
在系統菜單中加子菜單
系統菜單與其它菜單類似,你可以添加或删除項目,這需要使用CMenu 類的成員函數。下面的代碼在你的系統菜單後面添加一個新菜單項:
CMenu *sysmenu;
sysmenu = m_pMainWnd->GetSystemMenu(FALSE);
sysmenu->AppendMenu(MF_STRING, 1000, "xxx");
參見MFC 幫助檔案中的CMenu 類
在系統菜單中加子菜單
系統菜單與其它菜單類似,你可以添加或删除項目,這需要使用CMenu 類的成員函數。下面的代碼在你的系統菜單後面添加一個新菜單項:
CMenu *sysmenu;
sysmenu = m_pMainWnd->GetSystemMenu(FALSE);
sysmenu->AppendMenu(MF_STRING, 1000, "xxx");
參見MFC 幫助檔案中的CMenu 類
MFC的CMenu類有一個成員函數SetMenuItemBitmaps,可以用于往菜單中增加圖示。具體方法如下:
在應用程式的資源檔案中添加想要增加的位圖,并将其ID命名為IDB_OPEN1和IDB_OPEN2;---在應用程式的視圖類中添加CBitmap類的對象,不妨取名為bm_open1和bm_open2。在視圖類的構造函數中添加以下代碼:
bm_open1.LoadBitmap(IDB_OPEN1);bm_open2.LoadBitmap(IDB_OPEN2);(函數LoadBitmap用于加載位圖到CBitmap類的對象)在視圖類的成員函數OnDraw()中添加以下代碼:
CWnd*parent=GetParent();CMenu*pmenubar=parent-$#@62;GetMenu();
CMenu*pmenu=pmenubar-$#@62;GetSubMenu(2);
pmenu-$#@62;SetMenuItemBitmaps(1,MF_BYPOSITION,&bm_open1,&bm_open2);
前三行得到指向菜單的指針,第四行調用函數SetMenuItemBitmaps往菜單中增加圖示,具體參數的含義可參見有關介紹MFC及其成員函數的書即可。
自繪菜單
在這裡提供一個C++類(CCustomMenu),該類是CMenu的子類,并且擁有自繪能力。它可以向你提供以下的功能:
- 設定字型顔色。
- 設定高亮度顔色。
- 設定高亮度時的風格。
- 設定選中時和在普通狀态下的菜單顯示的圖示。
- 設定顯示圖示大小。
在CCustomMenu中定義了結構MENUDATA,你必須根據你的需要填充該結構,并且在增加菜單時提供該結構的指針(調用AppendMenu,InsertMenu)。下面是一個例子:
1、定義CCustomMenu的執行個體,和MENUDATA結構變量。
CCustomMenu m_cCustomMenu;
MENUDATA menuData [8];
// as many menu items are present ,
//You should be able to use
//new and do the same
2、調用CreateMenu()設定有關參數。
m_customMenu.CreateMenu ();
m_customMenu.SetIconSize (25,25);
//This is to set the size of the Icon.
// This should be used only once for any menu
// in order to resize it, destroy and create the menu again with different size.
m_customMenu.SetHighlightStyle (Normal); //Or TextOnly, if you want the
// background color to remain the same
// and the Text color to change to the Highlight color.
// The following setXXXColor sets the menu colors.
//If you dont want to change any, Dont call these member functions.
m_customMenu.SetTextColor (RGB (255,0,0));
m_customMenu.SetBackColor (RGB (255,255,255));
m_customMenu.SetHighlightColor (RGB (0,0,255));
3、設定MENUDATA變量,并增加菜單項。
lstrcpy (menuData[0].menuText , "text1");
menuData[0].menuIconNormal= IDI_ICON1;
m_customMenu.AppendMenu (MF_OWNERDRAW,3,(LPCTSTR)menuData);
3、在你的視窗中重載OnMeasureItem(...)函數。
void CMyView::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
if ( lpMeasureItemStruct->CtlType == ODT_MENU &&
IsMenu((HMENU)lpMeasureItemStruct->i ID) &&
(lpMeasureItemStruct->itemID == (UINT)m_hMenuSub) )
{
m_customMenu.MeasureItem (lpMeasureItemStruct);
}
else
// let MFCs self-drawing handle it
CView::OnMeasureItem(nIDCtl, lpMeasureItemStruct);
}
下面的函數将幫助你設定菜單屬性。
void SetTextColor (COLORREF );
void SetBackColor (COLORREF);
void SetHighlightColor (COLORREF);
void SetIconSize (int, int);
void SetHighlightStyle (HIGHLIGHTSTYLE );
// HIGHLIGHTSTYLE : enum {Normal, TextOnly}
void SetHighlightTextColor (COLORREF);
下面是檔案代碼:
//*************************************************************************
// CustomMenu.h : header file
//
#if
!defined(AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_)
#define AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
class MENUDATA
{
public:
MENUDATA () { menuIconNormal = -1; menuIconSelected = -1;};
char menuText[32];
UINT menuIconNormal;
UINT menuIconSelected;
};
typedef enum {Normal,TextOnly} HIGHLIGHTSTYLE;
///
//
// CCustomMenu window
class CCustomMenu : public CMenu
{
// Construction
public:
CCustomMenu();
// Attributes
public:
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CCustomMenu)
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CCustomMenu();
virtual void DrawItem( LPDRAWITEMSTRUCT);
virtual void MeasureItem( LPMEASUREITEMSTRUCT );
void SetTextColor (COLORREF );
void SetBackColor (COLORREF);
void SetHighlightColor (COLORREF);
void SetIconSize (int, int);
void SetHighlightStyle (HIGHLIGHTSTYLE );
void SetHighlightTextColor (COLORREF);
// Generated message map functions
protected:
COLORREF m_crText;
COLORREF m_clrBack;
COLORREF m_clrText;
COLORREF m_clrHilight;
COLORREF m_clrHilightText;
LOGFONT m_lf;
CFont m_fontMenu;
UINT m_iMenuHeight;
BOOL m_bLBtnDown;
CBrush m_brBackground,m_brSelect;
CPen m_penBack;
int m_iconX,m_iconY;
HIGHLIGHTSTYLE m_hilightStyle;
//{{AFX_MSG(CCustomMenu)
// NOTE - the ClassWizard will add and remove member functions here.
//}}AFX_MSG
};
///
//
//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
#endif //!defined(AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_)
//*************************************************************************
// CustomMenu.cpp : implementation file
//
#include "stdafx.h"
#include "CustomMenu.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
///
//
// CCustomMenu
CCustomMenu::CCustomMenu()
{
m_clrText = GetSysColor (COLOR_MENUTEXT);
m_clrBack = GetSysColor (COLOR_MENU);
m_brBackground.CreateSolidBrush (m_clrBack);
m_penBack.CreatePen (PS_SOLID,0,m_clrBack);
m_crText = m_clrText;
m_bLBtnDown = FALSE;
m_iconX = GetSystemMetrics ( SM_CXMENUCHECK);
m_iconY = GetSystemMetrics (SM_CYMENUCHECK );
m_clrHilight = GetSysColor (COLOR_HIGHLIGHT);
m_brSelect.CreateSolidBrush (m_clrHilight);
m_clrHilightText = GetSysColor (COLOR_HIGHLIGHTTEXT);
ZeroMemory ((PVOID) &m_lf,sizeof (LOGFONT));
NONCLIENTMETRICS nm;
nm.cbSize = sizeof (NONCLIENTMETRICS);
//Get the system metrics for the Captionfromhere
VERIFY (SystemParametersInfo (SPI_GETNONCLIENTMETRICS,0,&nm,0));
m_lf = nm.lfMenuFont;
m_iMenuHeight = nm.iMenuHeight;
m_fontMenu.CreateFontIndirect (&m_lf);
}
CCustomMenu::~CCustomMenu()
{
if ((HBRUSH) m_brBackground != NULL)
m_brBackground.DeleteObject ();
if ((HFONT)m_fontMenu !=NULL)
m_fontMenu.DeleteObject ();
if ((HBRUSH)m_brSelect != NULL)
m_brSelect.DeleteObject ();
}
///
//
// CCustomMenu message handlers
void CCustomMenu::DrawItem (LPDRAWITEMSTRUCT lpDIS)
{
ASSERT(lpDIS != NULL);
CDC* pDC = CDC::FromHandle(lpDIS->hDC);
CRect rect;
HICON hIcon;
COLORREF crText = m_crText;
// draw the colored rectangle portion
rect.CopyRect(&lpDIS->rcItem);
// draw the up/down/focused/disabled state
UINT action = lpDIS->itemAction;
UINT state = lpDIS->itemState;
CString strText;
LOGFONT lf;
lf = m_lf;
CFont dispFont;
CFont *pFont;
//GetWindowText(strText);
if (lpDIS->itemData != NULL)
{
strText = (((MENUDATA*) (lpDIS->itemData))->menuText);
if ((((MENUDATA *)(lpDIS->itemData))->menuIconNormal) == -1)
hIcon = NULL;
else if (state & ODS_SELECTED)
{
if ((((MENUDATA *)(lpDIS->itemData))->menuIconSelected) != -1)
hIcon = AfxGetApp ()->LoadIcon ((
(MENUDATA *)(lpDIS->itemData))->menuIconSelected);
else
hIcon = AfxGetApp()->LoadIcon ((
(MENUDATA*)(lpDIS->itemData))->menuIconNormal);
}
else
hIcon = AfxGetApp()->LoadIcon (
((MENUDATA*)(lpDIS->itemData))->menuIconNormal);
TRACE1 ("Draw for %s\n", strText);
}
else
{
strText.Empty();
hIcon = NULL;
}
if ( (state & ODS_SELECTED) )
{
// draw the down edges
CPen *pOldPen = pDC->SelectObject (&m_penBack);
//You need only Text highlight and thats what you get
if (m_hilightStyle != Normal)
{
pDC->FillRect (rect,&m_brBackground);
}
else
{
pDC->FillRect (rect,&m_brSelect);
}
pDC->SelectObject (pOldPen);
pDC->Draw3dRect (rect,GetSysColor (COLOR_3DHILIGHT),GetSysColor(COLOR_3DSHADOW));
lf.lfWeight = FW_BOLD;
if ((HFONT)dispFont != NULL)
dispFont.DeleteObject ();
dispFont.CreateFontIndirect (&lf);
crText = m_clrHilightText;
//While selected move the text a bit
TRACE0 ("SELECT,SELECTED\n");
}
else
{
CPen *pOldPen = pDC->SelectObject (&m_penBack);
pDC->FillRect (rect,&m_brBackground);
pDC->SelectObject (pOldPen);
// draw the up edges
pDC->Draw3dRect (rect,m_clrBack,m_clrBack);
if ((HFONT)dispFont != NULL)
dispFont.DeleteObject ();
dispFont.CreateFontIndirect (&lf); //Normal
TRACE0 ("SELECT, NORMAL\n");
}
// draw the text if there is any
//We have to paint the text only if the image is nonexistant
if (hIcon != NULL)
{
if(DrawIconEx (pDC->GetSafeHdc(),rect.left,rect.top,hIcon,
(m_iconX)?m_iconX:32,(m_iconY)?m_iconY:32,0,NULL,DI_NORMAL))
TRACE0("Wrote the icon successfully\n");
else
TRACE0 ("SORRY.NOGO\n");
}
//This is needed always so that we can have the space for check marks
rect.left = rect.left +((m_iconX)?m_iconX:32);
if ( !strText.IsEmpty())
{
// pFont->GetLogFont (&lf);
int iOldMode = pDC->GetBkMode();
pDC->SetBkMode( TRANSPARENT);
pDC->SetTextColor( crText);
pFont = pDC->SelectObject (&dispFont);
TRACE1( "About To DrawText %s\n",strText);
pDC->DrawText (strText,rect,DT_LEFT|DT_SINGLELINE|DT_VCENTER);
TRACE0("Done\n");
pDC->SetBkMode( iOldMode );
pDC->SelectObject (pFont); //set it to the old font
}
dispFont.DeleteObject ();
}
void CCustomMenu::MeasureItem( LPMEASUREITEMSTRUCT lpMIS )
{
CDC *pDC = AfxGetApp()->m_pMainWnd->GetDC();
CFont* pFont = pDC->SelectObject (&m_fontMenu);
int iconX = 0,iconY= 0;
TEXTMETRIC tm;
pDC->GetTextMetrics (&tm);
pDC->SelectObject (pFont);
AfxGetApp()->m_pMainWnd->ReleaseDC (pDC);
if (m_iconX)
iconX = m_iconX;
if (m_iconY)
iconY = m_iconY;
lpMIS->itemWidth = iconX + tm.tmAveCharWidth
* lstrlen(((MENUDATA*)(lpMIS->itemData))->menuText) +10;
lpMIS->itemHeight = (iconY > (m_iMenuHeight+1))?iconY:m_iMenuHeight + 1;
}
void CCustomMenu::SetIconSize (int width, int height)
{
m_iconX = width;
m_iconY = height;
}
void CCustomMenu::SetTextColor (COLORREF clrText)
{
m_crText = clrText;
}
void CCustomMenu::SetBackColor (COLORREF clrBack)
{
m_clrBack = clrBack;
if ((HBRUSH)m_brBackground != NULL)
m_brBackground.DeleteObject ();
m_brBackground.CreateSolidBrush (clrBack);
}
void CCustomMenu::SetHighlightColor (COLORREF clrHilight)
{
m_clrHilight = clrHilight;
if ((HBRUSH)m_brSelect != NULL)
m_brSelect.DeleteObject ();
m_brSelect.CreateSolidBrush (clrHilight);
}
void CCustomMenu::SetHighlightTextColor (COLORREF clrHilightText)
{
m_clrHilightText = clrHilightText;
}
void CCustomMenu::SetHighlightStyle (HIGHLIGHTSTYLE hilightStyle)
{
m_hilightStyle = hilightStyle;
}
//*************************************************************************
在系統菜單中加子菜單
系統菜單與其它菜單類似,你可以添加或删除項目,這需要使用CMenu 類的成員函數。下面的代碼在你的系統菜單後面添加一個新菜單項:
CMenu *sysmenu;
sysmenu = m_pMainWnd->GetSystemMenu(FALSE);
sysmenu->AppendMenu(MF_STRING, 1000, "xxx");
參見MFC 幫助檔案中的CMenu 類
在系統菜單中加子菜單
系統菜單與其它菜單類似,你可以添加或删除項目,這需要使用CMenu 類的成員函數。下面的代碼在你的系統菜單後面添加一個新菜單項:
CMenu *sysmenu;
sysmenu = m_pMainWnd->GetSystemMenu(FALSE);
sysmenu->AppendMenu(MF_STRING, 1000, "xxx");
參見MFC 幫助檔案中的CMenu 類
MFC的CMenu類有一個成員函數SetMenuItemBitmaps,可以用于往菜單中增加圖示。具體方法如下:
在應用程式的資源檔案中添加想要增加的位圖,并将其ID命名為IDB_OPEN1和IDB_OPEN2;---在應用程式的視圖類中添加CBitmap類的對象,不妨取名為bm_open1和bm_open2。在視圖類的構造函數中添加以下代碼:
bm_open1.LoadBitmap(IDB_OPEN1);bm_open2.LoadBitmap(IDB_OPEN2);(函數LoadBitmap用于加載位圖到CBitmap類的對象)在視圖類的成員函數OnDraw()中添加以下代碼:
CWnd*parent=GetParent();CMenu*pmenubar=parent-$#@62;GetMenu();
CMenu*pmenu=pmenubar-$#@62;GetSubMenu(2);
pmenu-$#@62;SetMenuItemBitmaps(1,MF_BYPOSITION,&bm_open1,&bm_open2);
前三行得到指向菜單的指針,第四行調用函數SetMenuItemBitmaps往菜單中增加圖示,具體參數的含義可參見有關介紹MFC及其成員函數的書即可。
自繪菜單
在這裡提供一個C++類(CCustomMenu),該類是CMenu的子類,并且擁有自繪能力。它可以向你提供以下的功能:
- 設定字型顔色。
- 設定高亮度顔色。
- 設定高亮度時的風格。
- 設定選中時和在普通狀态下的菜單顯示的圖示。
- 設定顯示圖示大小。
在CCustomMenu中定義了結構MENUDATA,你必須根據你的需要填充該結構,并且在增加菜單時提供該結構的指針(調用AppendMenu,InsertMenu)。下面是一個例子:
1、定義CCustomMenu的執行個體,和MENUDATA結構變量。
CCustomMenu m_cCustomMenu;
MENUDATA menuData [8];
// as many menu items are present ,
//You should be able to use
//new and do the same
2、調用CreateMenu()設定有關參數。
m_customMenu.CreateMenu ();
m_customMenu.SetIconSize (25,25);
//This is to set the size of the Icon.
// This should be used only once for any menu
// in order to resize it, destroy and create the menu again with different size.
m_customMenu.SetHighlightStyle (Normal); //Or TextOnly, if you want the
// background color to remain the same
// and the Text color to change to the Highlight color.
// The following setXXXColor sets the menu colors.
//If you dont want to change any, Dont call these member functions.
m_customMenu.SetTextColor (RGB (255,0,0));
m_customMenu.SetBackColor (RGB (255,255,255));
m_customMenu.SetHighlightColor (RGB (0,0,255));
3、設定MENUDATA變量,并增加菜單項。
lstrcpy (menuData[0].menuText , "text1");
menuData[0].menuIconNormal= IDI_ICON1;
m_customMenu.AppendMenu (MF_OWNERDRAW,3,(LPCTSTR)menuData);
3、在你的視窗中重載OnMeasureItem(...)函數。
void CMyView::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
if ( lpMeasureItemStruct->CtlType == ODT_MENU &&
IsMenu((HMENU)lpMeasureItemStruct->i ID) &&
(lpMeasureItemStruct->itemID == (UINT)m_hMenuSub) )
{
m_customMenu.MeasureItem (lpMeasureItemStruct);
}
else
// let MFCs self-drawing handle it
CView::OnMeasureItem(nIDCtl, lpMeasureItemStruct);
}
下面的函數将幫助你設定菜單屬性。
void SetTextColor (COLORREF );
void SetBackColor (COLORREF);
void SetHighlightColor (COLORREF);
void SetIconSize (int, int);
void SetHighlightStyle (HIGHLIGHTSTYLE );
// HIGHLIGHTSTYLE : enum {Normal, TextOnly}
void SetHighlightTextColor (COLORREF);
下面是檔案代碼:
//*************************************************************************
// CustomMenu.h : header file
//
#if
!defined(AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_)
#define AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
class MENUDATA
{
public:
MENUDATA () { menuIconNormal = -1; menuIconSelected = -1;};
char menuText[32];
UINT menuIconNormal;
UINT menuIconSelected;
};
typedef enum {Normal,TextOnly} HIGHLIGHTSTYLE;
///
//
// CCustomMenu window
class CCustomMenu : public CMenu
{
// Construction
public:
CCustomMenu();
// Attributes
public:
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CCustomMenu)
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CCustomMenu();
virtual void DrawItem( LPDRAWITEMSTRUCT);
virtual void MeasureItem( LPMEASUREITEMSTRUCT );
void SetTextColor (COLORREF );
void SetBackColor (COLORREF);
void SetHighlightColor (COLORREF);
void SetIconSize (int, int);
void SetHighlightStyle (HIGHLIGHTSTYLE );
void SetHighlightTextColor (COLORREF);
// Generated message map functions
protected:
COLORREF m_crText;
COLORREF m_clrBack;
COLORREF m_clrText;
COLORREF m_clrHilight;
COLORREF m_clrHilightText;
LOGFONT m_lf;
CFont m_fontMenu;
UINT m_iMenuHeight;
BOOL m_bLBtnDown;
CBrush m_brBackground,m_brSelect;
CPen m_penBack;
int m_iconX,m_iconY;
HIGHLIGHTSTYLE m_hilightStyle;
//{{AFX_MSG(CCustomMenu)
// NOTE - the ClassWizard will add and remove member functions here.
//}}AFX_MSG
};
///
//
//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
#endif //!defined(AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_)
//*************************************************************************
// CustomMenu.cpp : implementation file
//
#include "stdafx.h"
#include "CustomMenu.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
///
//
// CCustomMenu
CCustomMenu::CCustomMenu()
{
m_clrText = GetSysColor (COLOR_MENUTEXT);
m_clrBack = GetSysColor (COLOR_MENU);
m_brBackground.CreateSolidBrush (m_clrBack);
m_penBack.CreatePen (PS_SOLID,0,m_clrBack);
m_crText = m_clrText;
m_bLBtnDown = FALSE;
m_iconX = GetSystemMetrics ( SM_CXMENUCHECK);
m_iconY = GetSystemMetrics (SM_CYMENUCHECK );
m_clrHilight = GetSysColor (COLOR_HIGHLIGHT);
m_brSelect.CreateSolidBrush (m_clrHilight);
m_clrHilightText = GetSysColor (COLOR_HIGHLIGHTTEXT);
ZeroMemory ((PVOID) &m_lf,sizeof (LOGFONT));
NONCLIENTMETRICS nm;
nm.cbSize = sizeof (NONCLIENTMETRICS);
//Get the system metrics for the Captionfromhere
VERIFY (SystemParametersInfo (SPI_GETNONCLIENTMETRICS,0,&nm,0));
m_lf = nm.lfMenuFont;
m_iMenuHeight = nm.iMenuHeight;
m_fontMenu.CreateFontIndirect (&m_lf);
}
CCustomMenu::~CCustomMenu()
{
if ((HBRUSH) m_brBackground != NULL)
m_brBackground.DeleteObject ();
if ((HFONT)m_fontMenu !=NULL)
m_fontMenu.DeleteObject ();
if ((HBRUSH)m_brSelect != NULL)
m_brSelect.DeleteObject ();
}
///
//
// CCustomMenu message handlers
void CCustomMenu::DrawItem (LPDRAWITEMSTRUCT lpDIS)
{
ASSERT(lpDIS != NULL);
CDC* pDC = CDC::FromHandle(lpDIS->hDC);
CRect rect;
HICON hIcon;
COLORREF crText = m_crText;
// draw the colored rectangle portion
rect.CopyRect(&lpDIS->rcItem);
// draw the up/down/focused/disabled state
UINT action = lpDIS->itemAction;
UINT state = lpDIS->itemState;
CString strText;
LOGFONT lf;
lf = m_lf;
CFont dispFont;
CFont *pFont;
//GetWindowText(strText);
if (lpDIS->itemData != NULL)
{
strText = (((MENUDATA*) (lpDIS->itemData))->menuText);
if ((((MENUDATA *)(lpDIS->itemData))->menuIconNormal) == -1)
hIcon = NULL;
else if (state & ODS_SELECTED)
{
if ((((MENUDATA *)(lpDIS->itemData))->menuIconSelected) != -1)
hIcon = AfxGetApp ()->LoadIcon ((
(MENUDATA *)(lpDIS->itemData))->menuIconSelected);
else
hIcon = AfxGetApp()->LoadIcon ((
(MENUDATA*)(lpDIS->itemData))->menuIconNormal);
}
else
hIcon = AfxGetApp()->LoadIcon (
((MENUDATA*)(lpDIS->itemData))->menuIconNormal);
TRACE1 ("Draw for %s\n", strText);
}
else
{
strText.Empty();
hIcon = NULL;
}
if ( (state & ODS_SELECTED) )
{
// draw the down edges
CPen *pOldPen = pDC->SelectObject (&m_penBack);
//You need only Text highlight and thats what you get
if (m_hilightStyle != Normal)
{
pDC->FillRect (rect,&m_brBackground);
}
else
{
pDC->FillRect (rect,&m_brSelect);
}
pDC->SelectObject (pOldPen);
pDC->Draw3dRect (rect,GetSysColor (COLOR_3DHILIGHT),GetSysColor(COLOR_3DSHADOW));
lf.lfWeight = FW_BOLD;
if ((HFONT)dispFont != NULL)
dispFont.DeleteObject ();
dispFont.CreateFontIndirect (&lf);
crText = m_clrHilightText;
//While selected move the text a bit
TRACE0 ("SELECT,SELECTED\n");
}
else
{
CPen *pOldPen = pDC->SelectObject (&m_penBack);
pDC->FillRect (rect,&m_brBackground);
pDC->SelectObject (pOldPen);
// draw the up edges
pDC->Draw3dRect (rect,m_clrBack,m_clrBack);
if ((HFONT)dispFont != NULL)
dispFont.DeleteObject ();
dispFont.CreateFontIndirect (&lf); //Normal
TRACE0 ("SELECT, NORMAL\n");
}
// draw the text if there is any
//We have to paint the text only if the image is nonexistant
if (hIcon != NULL)
{
if(DrawIconEx (pDC->GetSafeHdc(),rect.left,rect.top,hIcon,
(m_iconX)?m_iconX:32,(m_iconY)?m_iconY:32,0,NULL,DI_NORMAL))
TRACE0("Wrote the icon successfully\n");
else
TRACE0 ("SORRY.NOGO\n");
}
//This is needed always so that we can have the space for check marks
rect.left = rect.left +((m_iconX)?m_iconX:32);
if ( !strText.IsEmpty())
{
// pFont->GetLogFont (&lf);
int iOldMode = pDC->GetBkMode();
pDC->SetBkMode( TRANSPARENT);
pDC->SetTextColor( crText);
pFont = pDC->SelectObject (&dispFont);
TRACE1( "About To DrawText %s\n",strText);
pDC->DrawText (strText,rect,DT_LEFT|DT_SINGLELINE|DT_VCENTER);
TRACE0("Done\n");
pDC->SetBkMode( iOldMode );
pDC->SelectObject (pFont); //set it to the old font
}
dispFont.DeleteObject ();
}
void CCustomMenu::MeasureItem( LPMEASUREITEMSTRUCT lpMIS )
{
CDC *pDC = AfxGetApp()->m_pMainWnd->GetDC();
CFont* pFont = pDC->SelectObject (&m_fontMenu);
int iconX = 0,iconY= 0;
TEXTMETRIC tm;
pDC->GetTextMetrics (&tm);
pDC->SelectObject (pFont);
AfxGetApp()->m_pMainWnd->ReleaseDC (pDC);
if (m_iconX)
iconX = m_iconX;
if (m_iconY)
iconY = m_iconY;
lpMIS->itemWidth = iconX + tm.tmAveCharWidth
* lstrlen(((MENUDATA*)(lpMIS->itemData))->menuText) +10;
lpMIS->itemHeight = (iconY > (m_iMenuHeight+1))?iconY:m_iMenuHeight + 1;
}
void CCustomMenu::SetIconSize (int width, int height)
{
m_iconX = width;
m_iconY = height;
}
void CCustomMenu::SetTextColor (COLORREF clrText)
{
m_crText = clrText;
}
void CCustomMenu::SetBackColor (COLORREF clrBack)
{
m_clrBack = clrBack;
if ((HBRUSH)m_brBackground != NULL)
m_brBackground.DeleteObject ();
m_brBackground.CreateSolidBrush (clrBack);
}
void CCustomMenu::SetHighlightColor (COLORREF clrHilight)
{
m_clrHilight = clrHilight;
if ((HBRUSH)m_brSelect != NULL)
m_brSelect.DeleteObject ();
m_brSelect.CreateSolidBrush (clrHilight);
}
void CCustomMenu::SetHighlightTextColor (COLORREF clrHilightText)
{
m_clrHilightText = clrHilightText;
}
void CCustomMenu::SetHighlightStyle (HIGHLIGHTSTYLE hilightStyle)
{
m_hilightStyle = hilightStyle;
}
//*************************************************************************
在系統菜單中加子菜單
系統菜單與其它菜單類似,你可以添加或删除項目,這需要使用CMenu 類的成員函數。下面的代碼在你的系統菜單後面添加一個新菜單項:
CMenu *sysmenu;
sysmenu = m_pMainWnd->GetSystemMenu(FALSE);
sysmenu->AppendMenu(MF_STRING, 1000, "xxx");
參見MFC 幫助檔案中的CMenu 類
在系統菜單中加子菜單
系統菜單與其它菜單類似,你可以添加或删除項目,這需要使用CMenu 類的成員函數。下面的代碼在你的系統菜單後面添加一個新菜單項:
CMenu *sysmenu;
sysmenu = m_pMainWnd->GetSystemMenu(FALSE);
sysmenu->AppendMenu(MF_STRING, 1000, "xxx");
參見MFC 幫助檔案中的CMenu 類
PublicSubCenterC(frmAsForm)
DimSpcFAsIntegerHowmanyspacescanfit
DimclenAsIntegercaptionlength
DimoldcAsStringoldcaption
DimiAsIntegernotimportant
removeanyspacesattheendsofthecaption
veryeasyifyoureaditcarefully
oldc=frm.Caption
DoWhileLeft(oldc,1)=Space(1)
DoEvents
oldc=Right(oldc,Len(oldc)-1)
Loop
DoWhileRight(oldc,1)=Space(1)
DoEvents
oldc=Left(oldc,Len(oldc)-1)
Loop
clen=Len(oldc)
IfInStr(oldc,"!")$#@60;$#@62;0Then
IfInStr(oldc,"")$#@60;$#@62;0Then
clen=clen*1.5
Else
clen=clen*1.4
EndIf
Else
IfInStr(oldc,"")$#@60;$#@62;0Then
clen=clen*1.4
Else
clen=clen*1.3
EndIf
EndIf
seehowmanycharacterscanfit
SpcF=frm.Width/61.2244howmanyspacecanfit itthecaption
SpcF=SpcF-clenHowmanyspacescanfit-Howmuch spacethe
tiontakesup
Nowthetrickypart
IfSpcF$#@62;1Then
DoEventsspeeduptheprogram
frm.Caption=Space(Int(SpcF/2))+oldc
Elseiftheformistoosmallforspaces
frm.Caption=oldc
EndIf
EndSub
|在窗體中添加以下代碼:
DimoldsizeAsLong
PrivateSubForm_Resize()
IfMe.Width=oldsizeThenifthewidthhasnt changed
ExitSubthendontmesswithit
Else
CenterCMe
oldsize=Me.Width
EndIf
EndSub
PrivateSubForm_Load()
CenterCMe
oldsize=Me.Width
EndSub
設計漂亮實用的菜單
談在VC中動态改變菜單
大部分Windows應用程式都使用下拉式菜單實作自己特定的函數,它使程式設計更加友善,不需要在程式中增加多個按鈕以完成這些操作。大多數情況下,我們的程式編譯生成後,菜單就确定了,不能再修改。然而,在很多情況下,程式要根據使用者的自己設定産生不同的菜單以适應不同使用者的要求,這就需要我們動态的改變菜單。接下來我們就分析如何動态的生成不同的菜單。
第一步:
運作AppWizard生成一個工程mymenu,接受所有的預設設定,除了下面一步:在step 1中選Single Document ,點選Finish按鈕,此時我們生成了一個工程。編譯運作,我們可以發現程式預設生成的菜單,接下來我們要對這個菜單進行修改。
第二步:
添加一個菜單資源,按如下步驟:菜單中選擇InsertàResouceàMenuàNew ,我們可以看到添加了一個ID号為IDR_MENU1的菜單資源,至于菜單中的各項你就随便添加了,為了我們下面的程式設計,請你添加幾項,第一列要包含幾個子菜單。
第三步:
添加一個對話框資源,按如下步驟:菜單中選擇InsertàResouceàDialogàNew ,我們可以看到添加了一個ID号為IDD_DIALOG1的對話框資源,添加六個按鈕,如下圖:
ID号依次為ID_CLEAR, ID_GOONE ,ID_GOTWO, ID_ADD, ID_ADDITEM, ID_EDIT ,為對話框定義類名為CChangeMenu, 輕按兩下添加的六個按鈕增加處理函數,用預設函數名。
第四步:
在IDR_MAINFRAME菜單中添加一項,ID号為ID_SET,名為"彈出設定對話框",為其添加處理函數,添加如下代碼:
CChangeMenu dlg;
dlg.DoModal();
當然,别忘了在檔案的開頭添加#include "changemenu . h"
第五步:
找到添加的六個按鈕的處理函數,依次添加如下的代碼:
void CChangeMenu::OnClear()
{
AfxGetMainWnd()->SetMenu(NULL);
AfxGetMainWnd()->DrawMenuBar();
}
void CChangeMenu::OnGoone()
{
if(!menu1){
menu1.LoadMenu(IDR_MENU1);
AfxGetMainWnd()->SetMenu(&menu1);
AfxGetMainWnd()->DrawMenuBar();
menu1.Detach();
}
}
void CChangeMenu::OnGotwo()
{
if(!menu2){
menu2.LoadMenu(IDR_MAINFRAME);
AfxGetMainWnd()->SetMenu(&menu2);
AfxGetMainWnd()->DrawMenuBar();
menu2.Detach();
}
}
void CChangeMenu::OnAdd()
{
static int x=400;
AfxGetMainWnd()->GetMenu()->AppendMenu(MF_STRING,x,"mynew");
AfxGetMainWnd()->DrawMenuBar();
x++;
}
void CChangeMenu::OnAdditem()
{
static int y=500;
AfxGetMainWnd()->GetMenu()->GetSubMenu(0)->InsertMenu(1,MF_BYPOSITION,y,"mynewitem");
AfxGetMainWnd()->DrawMenuBar();
y++;
}
void CChangeMenu::OnEdit()
{
AfxGetMainWnd()->GetMenu()->ModifyMenu(0,MF_BYPOSITION,402,"dfd");
AfxGetMainWnd()->DrawMenuBar();
}
好了,到這裡我們所有的功能就都實作了,快編譯運作一下吧!怎麼樣?還滿意嗎
談在VC中動态改變菜單
如何用VC++5在菜單中增加位圖或圖示
我們在使用Windows 95時,可以注意到在“開始”組中的菜單項前都有一個圖示,而且在Word 97中的菜單項前也有一個圖示。這些圖示不但讓我們清楚地了解到螢幕上的各種工具按鈕與各個菜單項之間的聯系,而且還增加了應用程式界面的美觀。那麼,請問如何用Visual C++ 5.0在應用程式菜單中增加圖示?
MFC的CMenu類有一個成員函數SetMenuItemBitmaps,可以用于往菜單中增加圖示。具體方法如下:
在應用程式的資源檔案中添加想要增加的位圖,并将其ID命名為IDB_OPEN1和IDB_OPEN2;---在應用程式的視圖類中添加CBitmap類的對象,不妨取名為bm_open1和bm_open2。在視圖類的構造函數中添加以下代碼:
bm_open1.LoadBitmap(IDB_OPEN1);bm_open2.LoadBitmap(IDB_OPEN2);(函數LoadBitmap用于加載位圖到CBitmap類的對象)在視圖類的成員函數OnDraw()中添加以下代碼:
CWnd*parent=GetParent();CMenu*pmenubar=parent-$#@62;GetMenu();
CMenu*pmenu=pmenubar-$#@62;GetSubMenu(2);
pmenu-$#@62;SetMenuItemBitmaps(1,MF_BYPOSITION,&bm_open1,&bm_open2);
前三行得到指向菜單的指針,第四行調用函數SetMenuItemBitmaps往菜單中增加圖示,具體參數的含義可參見有關介紹MFC及其成員函數的書即可。
自繪菜單
在這裡提供一個C++類(CCustomMenu),該類是CMenu的子類,并且擁有自繪能力。它可以向你提供以下的功能:
- 設定字型顔色。
- 設定高亮度顔色。
- 設定高亮度時的風格。
- 設定選中時和在普通狀态下的菜單顯示的圖示。
- 設定顯示圖示大小。
在CCustomMenu中定義了結構MENUDATA,你必須根據你的需要填充該結構,并且在增加菜單時提供該結構的指針(調用AppendMenu,InsertMenu)。下面是一個例子:
1、定義CCustomMenu的執行個體,和MENUDATA結構變量。
CCustomMenu m_cCustomMenu;
MENUDATA menuData [8];
// as many menu items are present ,
//You should be able to use
//new and do the same
2、調用CreateMenu()設定有關參數。
m_customMenu.CreateMenu ();
m_customMenu.SetIconSize (25,25);
//This is to set the size of the Icon.
// This should be used only once for any menu
// in order to resize it, destroy and create the menu again with different size.
m_customMenu.SetHighlightStyle (Normal); //Or TextOnly, if you want the
// background color to remain the same
// and the Text color to change to the Highlight color.
// The following setXXXColor sets the menu colors.
//If you dont want to change any, Dont call these member functions.
m_customMenu.SetTextColor (RGB (255,0,0));
m_customMenu.SetBackColor (RGB (255,255,255));
m_customMenu.SetHighlightColor (RGB (0,0,255));
3、設定MENUDATA變量,并增加菜單項。
lstrcpy (menuData[0].menuText , "text1");
menuData[0].menuIconNormal= IDI_ICON1;
m_customMenu.AppendMenu (MF_OWNERDRAW,3,(LPCTSTR)menuData);
3、在你的視窗中重載OnMeasureItem(...)函數。
void CMyView::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
if ( lpMeasureItemStruct->CtlType == ODT_MENU &&
IsMenu((HMENU)lpMeasureItemStruct->i ID) &&
(lpMeasureItemStruct->itemID == (UINT)m_hMenuSub) )
{
m_customMenu.MeasureItem (lpMeasureItemStruct);
}
else
// let MFCs self-drawing handle it
CView::OnMeasureItem(nIDCtl, lpMeasureItemStruct);
}
下面的函數将幫助你設定菜單屬性。
void SetTextColor (COLORREF );
void SetBackColor (COLORREF);
void SetHighlightColor (COLORREF);
void SetIconSize (int, int);
void SetHighlightStyle (HIGHLIGHTSTYLE );
// HIGHLIGHTSTYLE : enum {Normal, TextOnly}
void SetHighlightTextColor (COLORREF);
下面是檔案代碼:
//*************************************************************************
// CustomMenu.h : header file
//
#if
!defined(AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_)
#define AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
class MENUDATA
{
public:
MENUDATA () { menuIconNormal = -1; menuIconSelected = -1;};
char menuText[32];
UINT menuIconNormal;
UINT menuIconSelected;
};
typedef enum {Normal,TextOnly} HIGHLIGHTSTYLE;
///
//
// CCustomMenu window
class CCustomMenu : public CMenu
{
// Construction
public:
CCustomMenu();
// Attributes
public:
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CCustomMenu)
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CCustomMenu();
virtual void DrawItem( LPDRAWITEMSTRUCT);
virtual void MeasureItem( LPMEASUREITEMSTRUCT );
void SetTextColor (COLORREF );
void SetBackColor (COLORREF);
void SetHighlightColor (COLORREF);
void SetIconSize (int, int);
void SetHighlightStyle (HIGHLIGHTSTYLE );
void SetHighlightTextColor (COLORREF);
// Generated message map functions
protected:
COLORREF m_crText;
COLORREF m_clrBack;
COLORREF m_clrText;
COLORREF m_clrHilight;
COLORREF m_clrHilightText;
LOGFONT m_lf;
CFont m_fontMenu;
UINT m_iMenuHeight;
BOOL m_bLBtnDown;
CBrush m_brBackground,m_brSelect;
CPen m_penBack;
int m_iconX,m_iconY;
HIGHLIGHTSTYLE m_hilightStyle;
//{{AFX_MSG(CCustomMenu)
// NOTE - the ClassWizard will add and remove member functions here.
//}}AFX_MSG
};
///
//
//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
#endif //!defined(AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_)
//*************************************************************************
// CustomMenu.cpp : implementation file
//
#include "stdafx.h"
#include "CustomMenu.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
///
//
// CCustomMenu
CCustomMenu::CCustomMenu()
{
m_clrText = GetSysColor (COLOR_MENUTEXT);
m_clrBack = GetSysColor (COLOR_MENU);
m_brBackground.CreateSolidBrush (m_clrBack);
m_penBack.CreatePen (PS_SOLID,0,m_clrBack);
m_crText = m_clrText;
m_bLBtnDown = FALSE;
m_iconX = GetSystemMetrics ( SM_CXMENUCHECK);
m_iconY = GetSystemMetrics (SM_CYMENUCHECK );
m_clrHilight = GetSysColor (COLOR_HIGHLIGHT);
m_brSelect.CreateSolidBrush (m_clrHilight);
m_clrHilightText = GetSysColor (COLOR_HIGHLIGHTTEXT);
ZeroMemory ((PVOID) &m_lf,sizeof (LOGFONT));
NONCLIENTMETRICS nm;
nm.cbSize = sizeof (NONCLIENTMETRICS);
//Get the system metrics for the Captionfromhere
VERIFY (SystemParametersInfo (SPI_GETNONCLIENTMETRICS,0,&nm,0));
m_lf = nm.lfMenuFont;
m_iMenuHeight = nm.iMenuHeight;
m_fontMenu.CreateFontIndirect (&m_lf);
}
CCustomMenu::~CCustomMenu()
{
if ((HBRUSH) m_brBackground != NULL)
m_brBackground.DeleteObject ();
if ((HFONT)m_fontMenu !=NULL)
m_fontMenu.DeleteObject ();
if ((HBRUSH)m_brSelect != NULL)
m_brSelect.DeleteObject ();
}
///
//
// CCustomMenu message handlers
void CCustomMenu::DrawItem (LPDRAWITEMSTRUCT lpDIS)
{
ASSERT(lpDIS != NULL);
CDC* pDC = CDC::FromHandle(lpDIS->hDC);
CRect rect;
HICON hIcon;
COLORREF crText = m_crText;
// draw the colored rectangle portion
rect.CopyRect(&lpDIS->rcItem);
// draw the up/down/focused/disabled state
UINT action = lpDIS->itemAction;
UINT state = lpDIS->itemState;
CString strText;
LOGFONT lf;
lf = m_lf;
CFont dispFont;
CFont *pFont;
//GetWindowText(strText);
if (lpDIS->itemData != NULL)
{
strText = (((MENUDATA*) (lpDIS->itemData))->menuText);
if ((((MENUDATA *)(lpDIS->itemData))->menuIconNormal) == -1)
hIcon = NULL;
else if (state & ODS_SELECTED)
{
if ((((MENUDATA *)(lpDIS->itemData))->menuIconSelected) != -1)
hIcon = AfxGetApp ()->LoadIcon ((
(MENUDATA *)(lpDIS->itemData))->menuIconSelected);
else
hIcon = AfxGetApp()->LoadIcon ((
(MENUDATA*)(lpDIS->itemData))->menuIconNormal);
}
else
hIcon = AfxGetApp()->LoadIcon (
((MENUDATA*)(lpDIS->itemData))->menuIconNormal);
TRACE1 ("Draw for %s\n", strText);
}
else
{
strText.Empty();
hIcon = NULL;
}
if ( (state & ODS_SELECTED) )
{
// draw the down edges
CPen *pOldPen = pDC->SelectObject (&m_penBack);
//You need only Text highlight and thats what you get
if (m_hilightStyle != Normal)
{
pDC->FillRect (rect,&m_brBackground);
}
else
{
pDC->FillRect (rect,&m_brSelect);
}
pDC->SelectObject (pOldPen);
pDC->Draw3dRect (rect,GetSysColor (COLOR_3DHILIGHT),GetSysColor(COLOR_3DSHADOW));
lf.lfWeight = FW_BOLD;
if ((HFONT)dispFont != NULL)
dispFont.DeleteObject ();
dispFont.CreateFontIndirect (&lf);
crText = m_clrHilightText;
//While selected move the text a bit
TRACE0 ("SELECT,SELECTED\n");
}
else
{
CPen *pOldPen = pDC->SelectObject (&m_penBack);
pDC->FillRect (rect,&m_brBackground);
pDC->SelectObject (pOldPen);
// draw the up edges
pDC->Draw3dRect (rect,m_clrBack,m_clrBack);
if ((HFONT)dispFont != NULL)
dispFont.DeleteObject ();
dispFont.CreateFontIndirect (&lf); //Normal
TRACE0 ("SELECT, NORMAL\n");
}
// draw the text if there is any
//We have to paint the text only if the image is nonexistant
if (hIcon != NULL)
{
if(DrawIconEx (pDC->GetSafeHdc(),rect.left,rect.top,hIcon,
(m_iconX)?m_iconX:32,(m_iconY)?m_iconY:32,0,NULL,DI_NORMAL))
TRACE0("Wrote the icon successfully\n");
else
TRACE0 ("SORRY.NOGO\n");
}
//This is needed always so that we can have the space for check marks
rect.left = rect.left +((m_iconX)?m_iconX:32);
if ( !strText.IsEmpty())
{
// pFont->GetLogFont (&lf);
int iOldMode = pDC->GetBkMode();
pDC->SetBkMode( TRANSPARENT);
pDC->SetTextColor( crText);
pFont = pDC->SelectObject (&dispFont);
TRACE1( "About To DrawText %s\n",strText);
pDC->DrawText (strText,rect,DT_LEFT|DT_SINGLELINE|DT_VCENTER);
TRACE0("Done\n");
pDC->SetBkMode( iOldMode );
pDC->SelectObject (pFont); //set it to the old font
}
dispFont.DeleteObject ();
}
void CCustomMenu::MeasureItem( LPMEASUREITEMSTRUCT lpMIS )
{
CDC *pDC = AfxGetApp()->m_pMainWnd->GetDC();
CFont* pFont = pDC->SelectObject (&m_fontMenu);
int iconX = 0,iconY= 0;
TEXTMETRIC tm;
pDC->GetTextMetrics (&tm);
pDC->SelectObject (pFont);
AfxGetApp()->m_pMainWnd->ReleaseDC (pDC);
if (m_iconX)
iconX = m_iconX;
if (m_iconY)
iconY = m_iconY;
lpMIS->itemWidth = iconX + tm.tmAveCharWidth
* lstrlen(((MENUDATA*)(lpMIS->itemData))->menuText) +10;
lpMIS->itemHeight = (iconY > (m_iMenuHeight+1))?iconY:m_iMenuHeight + 1;
}
void CCustomMenu::SetIconSize (int width, int height)
{
m_iconX = width;
m_iconY = height;
}
void CCustomMenu::SetTextColor (COLORREF clrText)
{
m_crText = clrText;
}
void CCustomMenu::SetBackColor (COLORREF clrBack)
{
m_clrBack = clrBack;
if ((HBRUSH)m_brBackground != NULL)
m_brBackground.DeleteObject ();
m_brBackground.CreateSolidBrush (clrBack);
}
void CCustomMenu::SetHighlightColor (COLORREF clrHilight)
{
m_clrHilight = clrHilight;
if ((HBRUSH)m_brSelect != NULL)
m_brSelect.DeleteObject ();
m_brSelect.CreateSolidBrush (clrHilight);
}
void CCustomMenu::SetHighlightTextColor (COLORREF clrHilightText)
{
m_clrHilightText = clrHilightText;
}
void CCustomMenu::SetHighlightStyle (HIGHLIGHTSTYLE hilightStyle)
{
m_hilightStyle = hilightStyle;
}
//*************************************************************************
在系統菜單中加子菜單
系統菜單與其它菜單類似,你可以添加或删除項目,這需要使用CMenu 類的成員函數。下面的代碼在你的系統菜單後面添加一個新菜單項:
CMenu *sysmenu;
sysmenu = m_pMainWnd->GetSystemMenu(FALSE);
sysmenu->AppendMenu(MF_STRING, 1000, "xxx");
參見MFC 幫助檔案中的CMenu 類
在系統菜單中加子菜單
系統菜單與其它菜單類似,你可以添加或删除項目,這需要使用CMenu 類的成員函數。下面的代碼在你的系統菜單後面添加一個新菜單項:
CMenu *sysmenu;
sysmenu = m_pMainWnd->GetSystemMenu(FALSE);
sysmenu->AppendMenu(MF_STRING, 1000, "xxx");
參見MFC 幫助檔案中的CMenu 類
MFC的CMenu類有一個成員函數SetMenuItemBitmaps,可以用于往菜單中增加圖示。具體方法如下:
在應用程式的資源檔案中添加想要增加的位圖,并将其ID命名為IDB_OPEN1和IDB_OPEN2;---在應用程式的視圖類中添加CBitmap類的對象,不妨取名為bm_open1和bm_open2。在視圖類的構造函數中添加以下代碼:
bm_open1.LoadBitmap(IDB_OPEN1);bm_open2.LoadBitmap(IDB_OPEN2);(函數LoadBitmap用于加載位圖到CBitmap類的對象)在視圖類的成員函數OnDraw()中添加以下代碼:
CWnd*parent=GetParent();CMenu*pmenubar=parent-$#@62;GetMenu();
CMenu*pmenu=pmenubar-$#@62;GetSubMenu(2);
pmenu-$#@62;SetMenuItemBitmaps(1,MF_BYPOSITION,&bm_open1,&bm_open2);
前三行得到指向菜單的指針,第四行調用函數SetMenuItemBitmaps往菜單中增加圖示,具體參數的含義可參見有關介紹MFC及其成員函數的書即可。
自繪菜單
在這裡提供一個C++類(CCustomMenu),該類是CMenu的子類,并且擁有自繪能力。它可以向你提供以下的功能:
- 設定字型顔色。
- 設定高亮度顔色。
- 設定高亮度時的風格。
- 設定選中時和在普通狀态下的菜單顯示的圖示。
- 設定顯示圖示大小。
在CCustomMenu中定義了結構MENUDATA,你必須根據你的需要填充該結構,并且在增加菜單時提供該結構的指針(調用AppendMenu,InsertMenu)。下面是一個例子:
1、定義CCustomMenu的執行個體,和MENUDATA結構變量。
CCustomMenu m_cCustomMenu;
MENUDATA menuData [8];
// as many menu items are present ,
//You should be able to use
//new and do the same
2、調用CreateMenu()設定有關參數。
m_customMenu.CreateMenu ();
m_customMenu.SetIconSize (25,25);
//This is to set the size of the Icon.
// This should be used only once for any menu
// in order to resize it, destroy and create the menu again with different size.
m_customMenu.SetHighlightStyle (Normal); //Or TextOnly, if you want the
// background color to remain the same
// and the Text color to change to the Highlight color.
// The following setXXXColor sets the menu colors.
//If you dont want to change any, Dont call these member functions.
m_customMenu.SetTextColor (RGB (255,0,0));
m_customMenu.SetBackColor (RGB (255,255,255));
m_customMenu.SetHighlightColor (RGB (0,0,255));
3、設定MENUDATA變量,并增加菜單項。
lstrcpy (menuData[0].menuText , "text1");
menuData[0].menuIconNormal= IDI_ICON1;
m_customMenu.AppendMenu (MF_OWNERDRAW,3,(LPCTSTR)menuData);
3、在你的視窗中重載OnMeasureItem(...)函數。
void CMyView::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
if ( lpMeasureItemStruct->CtlType == ODT_MENU &&
IsMenu((HMENU)lpMeasureItemStruct->i ID) &&
(lpMeasureItemStruct->itemID == (UINT)m_hMenuSub) )
{
m_customMenu.MeasureItem (lpMeasureItemStruct);
}
else
// let MFCs self-drawing handle it
CView::OnMeasureItem(nIDCtl, lpMeasureItemStruct);
}
下面的函數将幫助你設定菜單屬性。
void SetTextColor (COLORREF );
void SetBackColor (COLORREF);
void SetHighlightColor (COLORREF);
void SetIconSize (int, int);
void SetHighlightStyle (HIGHLIGHTSTYLE );
// HIGHLIGHTSTYLE : enum {Normal, TextOnly}
void SetHighlightTextColor (COLORREF);
下面是檔案代碼:
//*************************************************************************
// CustomMenu.h : header file
//
#if
!defined(AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_)
#define AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
class MENUDATA
{
public:
MENUDATA () { menuIconNormal = -1; menuIconSelected = -1;};
char menuText[32];
UINT menuIconNormal;
UINT menuIconSelected;
};
typedef enum {Normal,TextOnly} HIGHLIGHTSTYLE;
///
//
// CCustomMenu window
class CCustomMenu : public CMenu
{
// Construction
public:
CCustomMenu();
// Attributes
public:
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CCustomMenu)
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CCustomMenu();
virtual void DrawItem( LPDRAWITEMSTRUCT);
virtual void MeasureItem( LPMEASUREITEMSTRUCT );
void SetTextColor (COLORREF );
void SetBackColor (COLORREF);
void SetHighlightColor (COLORREF);
void SetIconSize (int, int);
void SetHighlightStyle (HIGHLIGHTSTYLE );
void SetHighlightTextColor (COLORREF);
// Generated message map functions
protected:
COLORREF m_crText;
COLORREF m_clrBack;
COLORREF m_clrText;
COLORREF m_clrHilight;
COLORREF m_clrHilightText;
LOGFONT m_lf;
CFont m_fontMenu;
UINT m_iMenuHeight;
BOOL m_bLBtnDown;
CBrush m_brBackground,m_brSelect;
CPen m_penBack;
int m_iconX,m_iconY;
HIGHLIGHTSTYLE m_hilightStyle;
//{{AFX_MSG(CCustomMenu)
// NOTE - the ClassWizard will add and remove member functions here.
//}}AFX_MSG
};
///
//
//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
#endif //!defined(AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_)
//*************************************************************************
// CustomMenu.cpp : implementation file
//
#include "stdafx.h"
#include "CustomMenu.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
///
//
// CCustomMenu
CCustomMenu::CCustomMenu()
{
m_clrText = GetSysColor (COLOR_MENUTEXT);
m_clrBack = GetSysColor (COLOR_MENU);
m_brBackground.CreateSolidBrush (m_clrBack);
m_penBack.CreatePen (PS_SOLID,0,m_clrBack);
m_crText = m_clrText;
m_bLBtnDown = FALSE;
m_iconX = GetSystemMetrics ( SM_CXMENUCHECK);
m_iconY = GetSystemMetrics (SM_CYMENUCHECK );
m_clrHilight = GetSysColor (COLOR_HIGHLIGHT);
m_brSelect.CreateSolidBrush (m_clrHilight);
m_clrHilightText = GetSysColor (COLOR_HIGHLIGHTTEXT);
ZeroMemory ((PVOID) &m_lf,sizeof (LOGFONT));
NONCLIENTMETRICS nm;
nm.cbSize = sizeof (NONCLIENTMETRICS);
//Get the system metrics for the Captionfromhere
VERIFY (SystemParametersInfo (SPI_GETNONCLIENTMETRICS,0,&nm,0));
m_lf = nm.lfMenuFont;
m_iMenuHeight = nm.iMenuHeight;
m_fontMenu.CreateFontIndirect (&m_lf);
}
CCustomMenu::~CCustomMenu()
{
if ((HBRUSH) m_brBackground != NULL)
m_brBackground.DeleteObject ();
if ((HFONT)m_fontMenu !=NULL)
m_fontMenu.DeleteObject ();
if ((HBRUSH)m_brSelect != NULL)
m_brSelect.DeleteObject ();
}
///
//
// CCustomMenu message handlers
void CCustomMenu::DrawItem (LPDRAWITEMSTRUCT lpDIS)
{
ASSERT(lpDIS != NULL);
CDC* pDC = CDC::FromHandle(lpDIS->hDC);
CRect rect;
HICON hIcon;
COLORREF crText = m_crText;
// draw the colored rectangle portion
rect.CopyRect(&lpDIS->rcItem);
// draw the up/down/focused/disabled state
UINT action = lpDIS->itemAction;
UINT state = lpDIS->itemState;
CString strText;
LOGFONT lf;
lf = m_lf;
CFont dispFont;
CFont *pFont;
//GetWindowText(strText);
if (lpDIS->itemData != NULL)
{
strText = (((MENUDATA*) (lpDIS->itemData))->menuText);
if ((((MENUDATA *)(lpDIS->itemData))->menuIconNormal) == -1)
hIcon = NULL;
else if (state & ODS_SELECTED)
{
if ((((MENUDATA *)(lpDIS->itemData))->menuIconSelected) != -1)
hIcon = AfxGetApp ()->LoadIcon ((
(MENUDATA *)(lpDIS->itemData))->menuIconSelected);
else
hIcon = AfxGetApp()->LoadIcon ((
(MENUDATA*)(lpDIS->itemData))->menuIconNormal);
}
else
hIcon = AfxGetApp()->LoadIcon (
((MENUDATA*)(lpDIS->itemData))->menuIconNormal);
TRACE1 ("Draw for %s\n", strText);
}
else
{
strText.Empty();
hIcon = NULL;
}
if ( (state & ODS_SELECTED) )
{
// draw the down edges
CPen *pOldPen = pDC->SelectObject (&m_penBack);
//You need only Text highlight and thats what you get
if (m_hilightStyle != Normal)
{
pDC->FillRect (rect,&m_brBackground);
}
else
{
pDC->FillRect (rect,&m_brSelect);
}
pDC->SelectObject (pOldPen);
pDC->Draw3dRect (rect,GetSysColor (COLOR_3DHILIGHT),GetSysColor(COLOR_3DSHADOW));
lf.lfWeight = FW_BOLD;
if ((HFONT)dispFont != NULL)
dispFont.DeleteObject ();
dispFont.CreateFontIndirect (&lf);
crText = m_clrHilightText;
//While selected move the text a bit
TRACE0 ("SELECT,SELECTED\n");
}
else
{
CPen *pOldPen = pDC->SelectObject (&m_penBack);
pDC->FillRect (rect,&m_brBackground);
pDC->SelectObject (pOldPen);
// draw the up edges
pDC->Draw3dRect (rect,m_clrBack,m_clrBack);
if ((HFONT)dispFont != NULL)
dispFont.DeleteObject ();
dispFont.CreateFontIndirect (&lf); //Normal
TRACE0 ("SELECT, NORMAL\n");
}
// draw the text if there is any
//We have to paint the text only if the image is nonexistant
if (hIcon != NULL)
{
if(DrawIconEx (pDC->GetSafeHdc(),rect.left,rect.top,hIcon,
(m_iconX)?m_iconX:32,(m_iconY)?m_iconY:32,0,NULL,DI_NORMAL))
TRACE0("Wrote the icon successfully\n");
else
TRACE0 ("SORRY.NOGO\n");
}
//This is needed always so that we can have the space for check marks
rect.left = rect.left +((m_iconX)?m_iconX:32);
if ( !strText.IsEmpty())
{
// pFont->GetLogFont (&lf);
int iOldMode = pDC->GetBkMode();
pDC->SetBkMode( TRANSPARENT);
pDC->SetTextColor( crText);
pFont = pDC->SelectObject (&dispFont);
TRACE1( "About To DrawText %s\n",strText);
pDC->DrawText (strText,rect,DT_LEFT|DT_SINGLELINE|DT_VCENTER);
TRACE0("Done\n");
pDC->SetBkMode( iOldMode );
pDC->SelectObject (pFont); //set it to the old font
}
dispFont.DeleteObject ();
}
void CCustomMenu::MeasureItem( LPMEASUREITEMSTRUCT lpMIS )
{
CDC *pDC = AfxGetApp()->m_pMainWnd->GetDC();
CFont* pFont = pDC->SelectObject (&m_fontMenu);
int iconX = 0,iconY= 0;
TEXTMETRIC tm;
pDC->GetTextMetrics (&tm);
pDC->SelectObject (pFont);
AfxGetApp()->m_pMainWnd->ReleaseDC (pDC);
if (m_iconX)
iconX = m_iconX;
if (m_iconY)
iconY = m_iconY;
lpMIS->itemWidth = iconX + tm.tmAveCharWidth
* lstrlen(((MENUDATA*)(lpMIS->itemData))->menuText) +10;
lpMIS->itemHeight = (iconY > (m_iMenuHeight+1))?iconY:m_iMenuHeight + 1;
}
void CCustomMenu::SetIconSize (int width, int height)
{
m_iconX = width;
m_iconY = height;
}
void CCustomMenu::SetTextColor (COLORREF clrText)
{
m_crText = clrText;
}
void CCustomMenu::SetBackColor (COLORREF clrBack)
{
m_clrBack = clrBack;
if ((HBRUSH)m_brBackground != NULL)
m_brBackground.DeleteObject ();
m_brBackground.CreateSolidBrush (clrBack);
}
void CCustomMenu::SetHighlightColor (COLORREF clrHilight)
{
m_clrHilight = clrHilight;
if ((HBRUSH)m_brSelect != NULL)
m_brSelect.DeleteObject ();
m_brSelect.CreateSolidBrush (clrHilight);
}
void CCustomMenu::SetHighlightTextColor (COLORREF clrHilightText)
{
m_clrHilightText = clrHilightText;
}
void CCustomMenu::SetHighlightStyle (HIGHLIGHTSTYLE hilightStyle)
{
m_hilightStyle = hilightStyle;
}
//*************************************************************************
在系統菜單中加子菜單
系統菜單與其它菜單類似,你可以添加或删除項目,這需要使用CMenu 類的成員函數。下面的代碼在你的系統菜單後面添加一個新菜單項:
CMenu *sysmenu;
sysmenu = m_pMainWnd->GetSystemMenu(FALSE);
sysmenu->AppendMenu(MF_STRING, 1000, "xxx");
參見MFC 幫助檔案中的CMenu 類
在系統菜單中加子菜單
系統菜單與其它菜單類似,你可以添加或删除項目,這需要使用CMenu 類的成員函數。下面的代碼在你的系統菜單後面添加一個新菜單項:
CMenu *sysmenu;
sysmenu = m_pMainWnd->GetSystemMenu(FALSE);
sysmenu->AppendMenu(MF_STRING, 1000, "xxx");
參見MFC 幫助檔案中的CMenu 類
MFC的CMenu類有一個成員函數SetMenuItemBitmaps,可以用于往菜單中增加圖示。具體方法如下:
在應用程式的資源檔案中添加想要增加的位圖,并将其ID命名為IDB_OPEN1和IDB_OPEN2;---在應用程式的視圖類中添加CBitmap類的對象,不妨取名為bm_open1和bm_open2。在視圖類的構造函數中添加以下代碼:
bm_open1.LoadBitmap(IDB_OPEN1);bm_open2.LoadBitmap(IDB_OPEN2);(函數LoadBitmap用于加載位圖到CBitmap類的對象)在視圖類的成員函數OnDraw()中添加以下代碼:
CWnd*parent=GetParent();CMenu*pmenubar=parent-$#@62;GetMenu();
CMenu*pmenu=pmenubar-$#@62;GetSubMenu(2);
pmenu-$#@62;SetMenuItemBitmaps(1,MF_BYPOSITION,&bm_open1,&bm_open2);
前三行得到指向菜單的指針,第四行調用函數SetMenuItemBitmaps往菜單中增加圖示,具體參數的含義可參見有關介紹MFC及其成員函數的書即可。
自繪菜單
在這裡提供一個C++類(CCustomMenu),該類是CMenu的子類,并且擁有自繪能力。它可以向你提供以下的功能:
- 設定字型顔色。
- 設定高亮度顔色。
- 設定高亮度時的風格。
- 設定選中時和在普通狀态下的菜單顯示的圖示。
- 設定顯示圖示大小。
在CCustomMenu中定義了結構MENUDATA,你必須根據你的需要填充該結構,并且在增加菜單時提供該結構的指針(調用AppendMenu,InsertMenu)。下面是一個例子:
1、定義CCustomMenu的執行個體,和MENUDATA結構變量。
CCustomMenu m_cCustomMenu;
MENUDATA menuData [8];
// as many menu items are present ,
//You should be able to use
//new and do the same
2、調用CreateMenu()設定有關參數。
m_customMenu.CreateMenu ();
m_customMenu.SetIconSize (25,25);
//This is to set the size of the Icon.
// This should be used only once for any menu
// in order to resize it, destroy and create the menu again with different size.
m_customMenu.SetHighlightStyle (Normal); //Or TextOnly, if you want the
// background color to remain the same
// and the Text color to change to the Highlight color.
// The following setXXXColor sets the menu colors.
//If you dont want to change any, Dont call these member functions.
m_customMenu.SetTextColor (RGB (255,0,0));
m_customMenu.SetBackColor (RGB (255,255,255));
m_customMenu.SetHighlightColor (RGB (0,0,255));
3、設定MENUDATA變量,并增加菜單項。
lstrcpy (menuData[0].menuText , "text1");
menuData[0].menuIconNormal= IDI_ICON1;
m_customMenu.AppendMenu (MF_OWNERDRAW,3,(LPCTSTR)menuData);
3、在你的視窗中重載OnMeasureItem(...)函數。
void CMyView::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
if ( lpMeasureItemStruct->CtlType == ODT_MENU &&
IsMenu((HMENU)lpMeasureItemStruct->i ID) &&
(lpMeasureItemStruct->itemID == (UINT)m_hMenuSub) )
{
m_customMenu.MeasureItem (lpMeasureItemStruct);
}
else
// let MFCs self-drawing handle it
CView::OnMeasureItem(nIDCtl, lpMeasureItemStruct);
}
下面的函數将幫助你設定菜單屬性。
void SetTextColor (COLORREF );
void SetBackColor (COLORREF);
void SetHighlightColor (COLORREF);
void SetIconSize (int, int);
void SetHighlightStyle (HIGHLIGHTSTYLE );
// HIGHLIGHTSTYLE : enum {Normal, TextOnly}
void SetHighlightTextColor (COLORREF);
下面是檔案代碼:
//*************************************************************************
// CustomMenu.h : header file
//
#if
!defined(AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_)
#define AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
class MENUDATA
{
public:
MENUDATA () { menuIconNormal = -1; menuIconSelected = -1;};
char menuText[32];
UINT menuIconNormal;
UINT menuIconSelected;
};
typedef enum {Normal,TextOnly} HIGHLIGHTSTYLE;
///
//
// CCustomMenu window
class CCustomMenu : public CMenu
{
// Construction
public:
CCustomMenu();
// Attributes
public:
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CCustomMenu)
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CCustomMenu();
virtual void DrawItem( LPDRAWITEMSTRUCT);
virtual void MeasureItem( LPMEASUREITEMSTRUCT );
void SetTextColor (COLORREF );
void SetBackColor (COLORREF);
void SetHighlightColor (COLORREF);
void SetIconSize (int, int);
void SetHighlightStyle (HIGHLIGHTSTYLE );
void SetHighlightTextColor (COLORREF);
// Generated message map functions
protected:
COLORREF m_crText;
COLORREF m_clrBack;
COLORREF m_clrText;
COLORREF m_clrHilight;
COLORREF m_clrHilightText;
LOGFONT m_lf;
CFont m_fontMenu;
UINT m_iMenuHeight;
BOOL m_bLBtnDown;
CBrush m_brBackground,m_brSelect;
CPen m_penBack;
int m_iconX,m_iconY;
HIGHLIGHTSTYLE m_hilightStyle;
//{{AFX_MSG(CCustomMenu)
// NOTE - the ClassWizard will add and remove member functions here.
//}}AFX_MSG
};
///
//
//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
#endif //!defined(AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_)
//*************************************************************************
// CustomMenu.cpp : implementation file
//
#include "stdafx.h"
#include "CustomMenu.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
///
//
// CCustomMenu
CCustomMenu::CCustomMenu()
{
m_clrText = GetSysColor (COLOR_MENUTEXT);
m_clrBack = GetSysColor (COLOR_MENU);
m_brBackground.CreateSolidBrush (m_clrBack);
m_penBack.CreatePen (PS_SOLID,0,m_clrBack);
m_crText = m_clrText;
m_bLBtnDown = FALSE;
m_iconX = GetSystemMetrics ( SM_CXMENUCHECK);
m_iconY = GetSystemMetrics (SM_CYMENUCHECK );
m_clrHilight = GetSysColor (COLOR_HIGHLIGHT);
m_brSelect.CreateSolidBrush (m_clrHilight);
m_clrHilightText = GetSysColor (COLOR_HIGHLIGHTTEXT);
ZeroMemory ((PVOID) &m_lf,sizeof (LOGFONT));
NONCLIENTMETRICS nm;
nm.cbSize = sizeof (NONCLIENTMETRICS);
//Get the system metrics for the Captionfromhere
VERIFY (SystemParametersInfo (SPI_GETNONCLIENTMETRICS,0,&nm,0));
m_lf = nm.lfMenuFont;
m_iMenuHeight = nm.iMenuHeight;
m_fontMenu.CreateFontIndirect (&m_lf);
}
CCustomMenu::~CCustomMenu()
{
if ((HBRUSH) m_brBackground != NULL)
m_brBackground.DeleteObject ();
if ((HFONT)m_fontMenu !=NULL)
m_fontMenu.DeleteObject ();
if ((HBRUSH)m_brSelect != NULL)
m_brSelect.DeleteObject ();
}
///
//
// CCustomMenu message handlers
void CCustomMenu::DrawItem (LPDRAWITEMSTRUCT lpDIS)
{
ASSERT(lpDIS != NULL);
CDC* pDC = CDC::FromHandle(lpDIS->hDC);
CRect rect;
HICON hIcon;
COLORREF crText = m_crText;
// draw the colored rectangle portion
rect.CopyRect(&lpDIS->rcItem);
// draw the up/down/focused/disabled state
UINT action = lpDIS->itemAction;
UINT state = lpDIS->itemState;
CString strText;
LOGFONT lf;
lf = m_lf;
CFont dispFont;
CFont *pFont;
//GetWindowText(strText);
if (lpDIS->itemData != NULL)
{
strText = (((MENUDATA*) (lpDIS->itemData))->menuText);
if ((((MENUDATA *)(lpDIS->itemData))->menuIconNormal) == -1)
hIcon = NULL;
else if (state & ODS_SELECTED)
{
if ((((MENUDATA *)(lpDIS->itemData))->menuIconSelected) != -1)
hIcon = AfxGetApp ()->LoadIcon ((
(MENUDATA *)(lpDIS->itemData))->menuIconSelected);
else
hIcon = AfxGetApp()->LoadIcon ((
(MENUDATA*)(lpDIS->itemData))->menuIconNormal);
}
else
hIcon = AfxGetApp()->LoadIcon (
((MENUDATA*)(lpDIS->itemData))->menuIconNormal);
TRACE1 ("Draw for %s\n", strText);
}
else
{
strText.Empty();
hIcon = NULL;
}
if ( (state & ODS_SELECTED) )
{
// draw the down edges
CPen *pOldPen = pDC->SelectObject (&m_penBack);
//You need only Text highlight and thats what you get
if (m_hilightStyle != Normal)
{
pDC->FillRect (rect,&m_brBackground);
}
else
{
pDC->FillRect (rect,&m_brSelect);
}
pDC->SelectObject (pOldPen);
pDC->Draw3dRect (rect,GetSysColor (COLOR_3DHILIGHT),GetSysColor(COLOR_3DSHADOW));
lf.lfWeight = FW_BOLD;
if ((HFONT)dispFont != NULL)
dispFont.DeleteObject ();
dispFont.CreateFontIndirect (&lf);
crText = m_clrHilightText;
//While selected move the text a bit
TRACE0 ("SELECT,SELECTED\n");
}
else
{
CPen *pOldPen = pDC->SelectObject (&m_penBack);
pDC->FillRect (rect,&m_brBackground);
pDC->SelectObject (pOldPen);
// draw the up edges
pDC->Draw3dRect (rect,m_clrBack,m_clrBack);
if ((HFONT)dispFont != NULL)
dispFont.DeleteObject ();
dispFont.CreateFontIndirect (&lf); //Normal
TRACE0 ("SELECT, NORMAL\n");
}
// draw the text if there is any
//We have to paint the text only if the image is nonexistant
if (hIcon != NULL)
{
if(DrawIconEx (pDC->GetSafeHdc(),rect.left,rect.top,hIcon,
(m_iconX)?m_iconX:32,(m_iconY)?m_iconY:32,0,NULL,DI_NORMAL))
TRACE0("Wrote the icon successfully\n");
else
TRACE0 ("SORRY.NOGO\n");
}
//This is needed always so that we can have the space for check marks
rect.left = rect.left +((m_iconX)?m_iconX:32);
if ( !strText.IsEmpty())
{
// pFont->GetLogFont (&lf);
int iOldMode = pDC->GetBkMode();
pDC->SetBkMode( TRANSPARENT);
pDC->SetTextColor( crText);
pFont = pDC->SelectObject (&dispFont);
TRACE1( "About To DrawText %s\n",strText);
pDC->DrawText (strText,rect,DT_LEFT|DT_SINGLELINE|DT_VCENTER);
TRACE0("Done\n");
pDC->SetBkMode( iOldMode );
pDC->SelectObject (pFont); //set it to the old font
}
dispFont.DeleteObject ();
}
void CCustomMenu::MeasureItem( LPMEASUREITEMSTRUCT lpMIS )
{
CDC *pDC = AfxGetApp()->m_pMainWnd->GetDC();
CFont* pFont = pDC->SelectObject (&m_fontMenu);
int iconX = 0,iconY= 0;
TEXTMETRIC tm;
pDC->GetTextMetrics (&tm);
pDC->SelectObject (pFont);
AfxGetApp()->m_pMainWnd->ReleaseDC (pDC);
if (m_iconX)
iconX = m_iconX;
if (m_iconY)
iconY = m_iconY;
lpMIS->itemWidth = iconX + tm.tmAveCharWidth
* lstrlen(((MENUDATA*)(lpMIS->itemData))->menuText) +10;
lpMIS->itemHeight = (iconY > (m_iMenuHeight+1))?iconY:m_iMenuHeight + 1;
}
void CCustomMenu::SetIconSize (int width, int height)
{
m_iconX = width;
m_iconY = height;
}
void CCustomMenu::SetTextColor (COLORREF clrText)
{
m_crText = clrText;
}
void CCustomMenu::SetBackColor (COLORREF clrBack)
{
m_clrBack = clrBack;
if ((HBRUSH)m_brBackground != NULL)
m_brBackground.DeleteObject ();
m_brBackground.CreateSolidBrush (clrBack);
}
void CCustomMenu::SetHighlightColor (COLORREF clrHilight)
{
m_clrHilight = clrHilight;
if ((HBRUSH)m_brSelect != NULL)
m_brSelect.DeleteObject ();
m_brSelect.CreateSolidBrush (clrHilight);
}
void CCustomMenu::SetHighlightTextColor (COLORREF clrHilightText)
{
m_clrHilightText = clrHilightText;
}
void CCustomMenu::SetHighlightStyle (HIGHLIGHTSTYLE hilightStyle)
{
m_hilightStyle = hilightStyle;
}
//*************************************************************************
在系統菜單中加子菜單
系統菜單與其它菜單類似,你可以添加或删除項目,這需要使用CMenu 類的成員函數。下面的代碼在你的系統菜單後面添加一個新菜單項:
CMenu *sysmenu;
sysmenu = m_pMainWnd->GetSystemMenu(FALSE);
sysmenu->AppendMenu(MF_STRING, 1000, "xxx");
參見MFC 幫助檔案中的CMenu 類
在系統菜單中加子菜單
系統菜單與其它菜單類似,你可以添加或删除項目,這需要使用CMenu 類的成員函數。下面的代碼在你的系統菜單後面添加一個新菜單項:
CMenu *sysmenu;
sysmenu = m_pMainWnd->GetSystemMenu(FALSE);
sysmenu->AppendMenu(MF_STRING, 1000, "xxx");
參見MFC 幫助檔案中的CMenu 類
PublicSubCenterC(frmAsForm)
DimSpcFAsIntegerHowmanyspacescanfit
DimclenAsIntegercaptionlength
DimoldcAsStringoldcaption
DimiAsIntegernotimportant
removeanyspacesattheendsofthecaption
veryeasyifyoureaditcarefully
oldc=frm.Caption
DoWhileLeft(oldc,1)=Space(1)
DoEvents
oldc=Right(oldc,Len(oldc)-1)
Loop
DoWhileRight(oldc,1)=Space(1)
DoEvents
oldc=Left(oldc,Len(oldc)-1)
Loop
clen=Len(oldc)
IfInStr(oldc,"!")$#@60;$#@62;0Then
IfInStr(oldc,"")$#@60;$#@62;0Then
clen=clen*1.5
Else
clen=clen*1.4
EndIf
Else
IfInStr(oldc,"")$#@60;$#@62;0Then
clen=clen*1.4
Else
clen=clen*1.3
EndIf
EndIf
seehowmanycharacterscanfit
SpcF=frm.Width/61.2244howmanyspacecanfit itthecaption
SpcF=SpcF-clenHowmanyspacescanfit-Howmuch spacethe
tiontakesup
Nowthetrickypart
IfSpcF$#@62;1Then
DoEventsspeeduptheprogram
frm.Caption=Space(Int(SpcF/2))+oldc
Elseiftheformistoosmallforspaces
frm.Caption=oldc
EndIf
EndSub
|在窗體中添加以下代碼:
DimoldsizeAsLong
PrivateSubForm_Resize()
IfMe.Width=oldsizeThenifthewidthhasnt changed
ExitSubthendontmesswithit
Else
CenterCMe
oldsize=Me.Width
EndIf
EndSub
PrivateSubForm_Load()
CenterCMe
oldsize=Me.Width
EndSub
設計漂亮實用的菜單
談在VC中動态改變菜單
大部分Windows應用程式都使用下拉式菜單實作自己特定的函數,它使程式設計更加友善,不需要在程式中增加多個按鈕以完成這些操作。大多數情況下,我們的程式編譯生成後,菜單就确定了,不能再修改。然而,在很多情況下,程式要根據使用者的自己設定産生不同的菜單以适應不同使用者的要求,這就需要我們動态的改變菜單。接下來我們就分析如何動态的生成不同的菜單。
第一步:
運作AppWizard生成一個工程mymenu,接受所有的預設設定,除了下面一步:在step 1中選Single Document ,點選Finish按鈕,此時我們生成了一個工程。編譯運作,我們可以發現程式預設生成的菜單,接下來我們要對這個菜單進行修改。
第二步:
添加一個菜單資源,按如下步驟:菜單中選擇InsertàResouceàMenuàNew ,我們可以看到添加了一個ID号為IDR_MENU1的菜單資源,至于菜單中的各項你就随便添加了,為了我們下面的程式設計,請你添加幾項,第一列要包含幾個子菜單。
第三步:
添加一個對話框資源,按如下步驟:菜單中選擇InsertàResouceàDialogàNew ,我們可以看到添加了一個ID号為IDD_DIALOG1的對話框資源,添加六個按鈕,如下圖:
ID号依次為ID_CLEAR, ID_GOONE ,ID_GOTWO, ID_ADD, ID_ADDITEM, ID_EDIT ,為對話框定義類名為CChangeMenu, 輕按兩下添加的六個按鈕增加處理函數,用預設函數名。
第四步:
在IDR_MAINFRAME菜單中添加一項,ID号為ID_SET,名為"彈出設定對話框",為其添加處理函數,添加如下代碼:
CChangeMenu dlg;
dlg.DoModal();
當然,别忘了在檔案的開頭添加#include "changemenu . h"
第五步:
找到添加的六個按鈕的處理函數,依次添加如下的代碼:
void CChangeMenu::OnClear()
{
AfxGetMainWnd()->SetMenu(NULL);
AfxGetMainWnd()->DrawMenuBar();
}
void CChangeMenu::OnGoone()
{
if(!menu1){
menu1.LoadMenu(IDR_MENU1);
AfxGetMainWnd()->SetMenu(&menu1);
AfxGetMainWnd()->DrawMenuBar();
menu1.Detach();
}
}
void CChangeMenu::OnGotwo()
{
if(!menu2){
menu2.LoadMenu(IDR_MAINFRAME);
AfxGetMainWnd()->SetMenu(&menu2);
AfxGetMainWnd()->DrawMenuBar();
menu2.Detach();
}
}
void CChangeMenu::OnAdd()
{
static int x=400;
AfxGetMainWnd()->GetMenu()->AppendMenu(MF_STRING,x,"mynew");
AfxGetMainWnd()->DrawMenuBar();
x++;
}
void CChangeMenu::OnAdditem()
{
static int y=500;
AfxGetMainWnd()->GetMenu()->GetSubMenu(0)->InsertMenu(1,MF_BYPOSITION,y,"mynewitem");
AfxGetMainWnd()->DrawMenuBar();
y++;
}
void CChangeMenu::OnEdit()
{
AfxGetMainWnd()->GetMenu()->ModifyMenu(0,MF_BYPOSITION,402,"dfd");
AfxGetMainWnd()->DrawMenuBar();
}
好了,到這裡我們所有的功能就都實作了,快編譯運作一下吧!怎麼樣?還滿意嗎
談在VC中動态改變菜單
如何用VC++5在菜單中增加位圖或圖示
我們在使用Windows 95時,可以注意到在“開始”組中的菜單項前都有一個圖示,而且在Word 97中的菜單項前也有一個圖示。這些圖示不但讓我們清楚地了解到螢幕上的各種工具按鈕與各個菜單項之間的聯系,而且還增加了應用程式界面的美觀。那麼,請問如何用Visual C++ 5.0在應用程式菜單中增加圖示?
MFC的CMenu類有一個成員函數SetMenuItemBitmaps,可以用于往菜單中增加圖示。具體方法如下:
在應用程式的資源檔案中添加想要增加的位圖,并将其ID命名為IDB_OPEN1和IDB_OPEN2;---在應用程式的視圖類中添加CBitmap類的對象,不妨取名為bm_open1和bm_open2。在視圖類的構造函數中添加以下代碼:
bm_open1.LoadBitmap(IDB_OPEN1);bm_open2.LoadBitmap(IDB_OPEN2);(函數LoadBitmap用于加載位圖到CBitmap類的對象)在視圖類的成員函數OnDraw()中添加以下代碼:
CWnd*parent=GetParent();CMenu*pmenubar=parent-$#@62;GetMenu();
CMenu*pmenu=pmenubar-$#@62;GetSubMenu(2);
pmenu-$#@62;SetMenuItemBitmaps(1,MF_BYPOSITION,&bm_open1,&bm_open2);
前三行得到指向菜單的指針,第四行調用函數SetMenuItemBitmaps往菜單中增加圖示,具體參數的含義可參見有關介紹MFC及其成員函數的書即可。
自繪菜單
在這裡提供一個C++類(CCustomMenu),該類是CMenu的子類,并且擁有自繪能力。它可以向你提供以下的功能:
- 設定字型顔色。
- 設定高亮度顔色。
- 設定高亮度時的風格。
- 設定選中時和在普通狀态下的菜單顯示的圖示。
- 設定顯示圖示大小。
在CCustomMenu中定義了結構MENUDATA,你必須根據你的需要填充該結構,并且在增加菜單時提供該結構的指針(調用AppendMenu,InsertMenu)。下面是一個例子:
1、定義CCustomMenu的執行個體,和MENUDATA結構變量。
CCustomMenu m_cCustomMenu;
MENUDATA menuData [8];
// as many menu items are present ,
//You should be able to use
//new and do the same
2、調用CreateMenu()設定有關參數。
m_customMenu.CreateMenu ();
m_customMenu.SetIconSize (25,25);
//This is to set the size of the Icon.
// This should be used only once for any menu
// in order to resize it, destroy and create the menu again with different size.
m_customMenu.SetHighlightStyle (Normal); //Or TextOnly, if you want the
// background color to remain the same
// and the Text color to change to the Highlight color.
// The following setXXXColor sets the menu colors.
//If you dont want to change any, Dont call these member functions.
m_customMenu.SetTextColor (RGB (255,0,0));
m_customMenu.SetBackColor (RGB (255,255,255));
m_customMenu.SetHighlightColor (RGB (0,0,255));
3、設定MENUDATA變量,并增加菜單項。
lstrcpy (menuData[0].menuText , "text1");
menuData[0].menuIconNormal= IDI_ICON1;
m_customMenu.AppendMenu (MF_OWNERDRAW,3,(LPCTSTR)menuData);
3、在你的視窗中重載OnMeasureItem(...)函數。
void CMyView::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
if ( lpMeasureItemStruct->CtlType == ODT_MENU &&
IsMenu((HMENU)lpMeasureItemStruct->i ID) &&
(lpMeasureItemStruct->itemID == (UINT)m_hMenuSub) )
{
m_customMenu.MeasureItem (lpMeasureItemStruct);
}
else
// let MFCs self-drawing handle it
CView::OnMeasureItem(nIDCtl, lpMeasureItemStruct);
}
下面的函數将幫助你設定菜單屬性。
void SetTextColor (COLORREF );
void SetBackColor (COLORREF);
void SetHighlightColor (COLORREF);
void SetIconSize (int, int);
void SetHighlightStyle (HIGHLIGHTSTYLE );
// HIGHLIGHTSTYLE : enum {Normal, TextOnly}
void SetHighlightTextColor (COLORREF);
下面是檔案代碼:
//*************************************************************************
// CustomMenu.h : header file
//
#if
!defined(AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_)
#define AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
class MENUDATA
{
public:
MENUDATA () { menuIconNormal = -1; menuIconSelected = -1;};
char menuText[32];
UINT menuIconNormal;
UINT menuIconSelected;
};
typedef enum {Normal,TextOnly} HIGHLIGHTSTYLE;
///
//
// CCustomMenu window
class CCustomMenu : public CMenu
{
// Construction
public:
CCustomMenu();
// Attributes
public:
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CCustomMenu)
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CCustomMenu();
virtual void DrawItem( LPDRAWITEMSTRUCT);
virtual void MeasureItem( LPMEASUREITEMSTRUCT );
void SetTextColor (COLORREF );
void SetBackColor (COLORREF);
void SetHighlightColor (COLORREF);
void SetIconSize (int, int);
void SetHighlightStyle (HIGHLIGHTSTYLE );
void SetHighlightTextColor (COLORREF);
// Generated message map functions
protected:
COLORREF m_crText;
COLORREF m_clrBack;
COLORREF m_clrText;
COLORREF m_clrHilight;
COLORREF m_clrHilightText;
LOGFONT m_lf;
CFont m_fontMenu;
UINT m_iMenuHeight;
BOOL m_bLBtnDown;
CBrush m_brBackground,m_brSelect;
CPen m_penBack;
int m_iconX,m_iconY;
HIGHLIGHTSTYLE m_hilightStyle;
//{{AFX_MSG(CCustomMenu)
// NOTE - the ClassWizard will add and remove member functions here.
//}}AFX_MSG
};
///
//
//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
#endif //!defined(AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_)
//*************************************************************************
// CustomMenu.cpp : implementation file
//
#include "stdafx.h"
#include "CustomMenu.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
///
//
// CCustomMenu
CCustomMenu::CCustomMenu()
{
m_clrText = GetSysColor (COLOR_MENUTEXT);
m_clrBack = GetSysColor (COLOR_MENU);
m_brBackground.CreateSolidBrush (m_clrBack);
m_penBack.CreatePen (PS_SOLID,0,m_clrBack);
m_crText = m_clrText;
m_bLBtnDown = FALSE;
m_iconX = GetSystemMetrics ( SM_CXMENUCHECK);
m_iconY = GetSystemMetrics (SM_CYMENUCHECK );
m_clrHilight = GetSysColor (COLOR_HIGHLIGHT);
m_brSelect.CreateSolidBrush (m_clrHilight);
m_clrHilightText = GetSysColor (COLOR_HIGHLIGHTTEXT);
ZeroMemory ((PVOID) &m_lf,sizeof (LOGFONT));
NONCLIENTMETRICS nm;
nm.cbSize = sizeof (NONCLIENTMETRICS);
//Get the system metrics for the Captionfromhere
VERIFY (SystemParametersInfo (SPI_GETNONCLIENTMETRICS,0,&nm,0));
m_lf = nm.lfMenuFont;
m_iMenuHeight = nm.iMenuHeight;
m_fontMenu.CreateFontIndirect (&m_lf);
}
CCustomMenu::~CCustomMenu()
{
if ((HBRUSH) m_brBackground != NULL)
m_brBackground.DeleteObject ();
if ((HFONT)m_fontMenu !=NULL)
m_fontMenu.DeleteObject ();
if ((HBRUSH)m_brSelect != NULL)
m_brSelect.DeleteObject ();
}
///
//
// CCustomMenu message handlers
void CCustomMenu::DrawItem (LPDRAWITEMSTRUCT lpDIS)
{
ASSERT(lpDIS != NULL);
CDC* pDC = CDC::FromHandle(lpDIS->hDC);
CRect rect;
HICON hIcon;
COLORREF crText = m_crText;
// draw the colored rectangle portion
rect.CopyRect(&lpDIS->rcItem);
// draw the up/down/focused/disabled state
UINT action = lpDIS->itemAction;
UINT state = lpDIS->itemState;
CString strText;
LOGFONT lf;
lf = m_lf;
CFont dispFont;
CFont *pFont;
//GetWindowText(strText);
if (lpDIS->itemData != NULL)
{
strText = (((MENUDATA*) (lpDIS->itemData))->menuText);
if ((((MENUDATA *)(lpDIS->itemData))->menuIconNormal) == -1)
hIcon = NULL;
else if (state & ODS_SELECTED)
{
if ((((MENUDATA *)(lpDIS->itemData))->menuIconSelected) != -1)
hIcon = AfxGetApp ()->LoadIcon ((
(MENUDATA *)(lpDIS->itemData))->menuIconSelected);
else
hIcon = AfxGetApp()->LoadIcon ((
(MENUDATA*)(lpDIS->itemData))->menuIconNormal);
}
else
hIcon = AfxGetApp()->LoadIcon (
((MENUDATA*)(lpDIS->itemData))->menuIconNormal);
TRACE1 ("Draw for %s\n", strText);
}
else
{
strText.Empty();
hIcon = NULL;
}
if ( (state & ODS_SELECTED) )
{
// draw the down edges
CPen *pOldPen = pDC->SelectObject (&m_penBack);
//You need only Text highlight and thats what you get
if (m_hilightStyle != Normal)
{
pDC->FillRect (rect,&m_brBackground);
}
else
{
pDC->FillRect (rect,&m_brSelect);
}
pDC->SelectObject (pOldPen);
pDC->Draw3dRect (rect,GetSysColor (COLOR_3DHILIGHT),GetSysColor(COLOR_3DSHADOW));
lf.lfWeight = FW_BOLD;
if ((HFONT)dispFont != NULL)
dispFont.DeleteObject ();
dispFont.CreateFontIndirect (&lf);
crText = m_clrHilightText;
//While selected move the text a bit
TRACE0 ("SELECT,SELECTED\n");
}
else
{
CPen *pOldPen = pDC->SelectObject (&m_penBack);
pDC->FillRect (rect,&m_brBackground);
pDC->SelectObject (pOldPen);
// draw the up edges
pDC->Draw3dRect (rect,m_clrBack,m_clrBack);
if ((HFONT)dispFont != NULL)
dispFont.DeleteObject ();
dispFont.CreateFontIndirect (&lf); //Normal
TRACE0 ("SELECT, NORMAL\n");
}
// draw the text if there is any
//We have to paint the text only if the image is nonexistant
if (hIcon != NULL)
{
if(DrawIconEx (pDC->GetSafeHdc(),rect.left,rect.top,hIcon,
(m_iconX)?m_iconX:32,(m_iconY)?m_iconY:32,0,NULL,DI_NORMAL))
TRACE0("Wrote the icon successfully\n");
else
TRACE0 ("SORRY.NOGO\n");
}
//This is needed always so that we can have the space for check marks
rect.left = rect.left +((m_iconX)?m_iconX:32);
if ( !strText.IsEmpty())
{
// pFont->GetLogFont (&lf);
int iOldMode = pDC->GetBkMode();
pDC->SetBkMode( TRANSPARENT);
pDC->SetTextColor( crText);
pFont = pDC->SelectObject (&dispFont);
TRACE1( "About To DrawText %s\n",strText);
pDC->DrawText (strText,rect,DT_LEFT|DT_SINGLELINE|DT_VCENTER);
TRACE0("Done\n");
pDC->SetBkMode( iOldMode );
pDC->SelectObject (pFont); //set it to the old font
}
dispFont.DeleteObject ();
}
void CCustomMenu::MeasureItem( LPMEASUREITEMSTRUCT lpMIS )
{
CDC *pDC = AfxGetApp()->m_pMainWnd->GetDC();
CFont* pFont = pDC->SelectObject (&m_fontMenu);
int iconX = 0,iconY= 0;
TEXTMETRIC tm;
pDC->GetTextMetrics (&tm);
pDC->SelectObject (pFont);
AfxGetApp()->m_pMainWnd->ReleaseDC (pDC);
if (m_iconX)
iconX = m_iconX;
if (m_iconY)
iconY = m_iconY;
lpMIS->itemWidth = iconX + tm.tmAveCharWidth
* lstrlen(((MENUDATA*)(lpMIS->itemData))->menuText) +10;
lpMIS->itemHeight = (iconY > (m_iMenuHeight+1))?iconY:m_iMenuHeight + 1;
}
void CCustomMenu::SetIconSize (int width, int height)
{
m_iconX = width;
m_iconY = height;
}
void CCustomMenu::SetTextColor (COLORREF clrText)
{
m_crText = clrText;
}
void CCustomMenu::SetBackColor (COLORREF clrBack)
{
m_clrBack = clrBack;
if ((HBRUSH)m_brBackground != NULL)
m_brBackground.DeleteObject ();
m_brBackground.CreateSolidBrush (clrBack);
}
void CCustomMenu::SetHighlightColor (COLORREF clrHilight)
{
m_clrHilight = clrHilight;
if ((HBRUSH)m_brSelect != NULL)
m_brSelect.DeleteObject ();
m_brSelect.CreateSolidBrush (clrHilight);
}
void CCustomMenu::SetHighlightTextColor (COLORREF clrHilightText)
{
m_clrHilightText = clrHilightText;
}
void CCustomMenu::SetHighlightStyle (HIGHLIGHTSTYLE hilightStyle)
{
m_hilightStyle = hilightStyle;
}
//*************************************************************************
在系統菜單中加子菜單
系統菜單與其它菜單類似,你可以添加或删除項目,這需要使用CMenu 類的成員函數。下面的代碼在你的系統菜單後面添加一個新菜單項:
CMenu *sysmenu;
sysmenu = m_pMainWnd->GetSystemMenu(FALSE);
sysmenu->AppendMenu(MF_STRING, 1000, "xxx");
參見MFC 幫助檔案中的CMenu 類
在系統菜單中加子菜單
系統菜單與其它菜單類似,你可以添加或删除項目,這需要使用CMenu 類的成員函數。下面的代碼在你的系統菜單後面添加一個新菜單項:
CMenu *sysmenu;
sysmenu = m_pMainWnd->GetSystemMenu(FALSE);
sysmenu->AppendMenu(MF_STRING, 1000, "xxx");
參見MFC 幫助檔案中的CMenu 類
MFC的CMenu類有一個成員函數SetMenuItemBitmaps,可以用于往菜單中增加圖示。具體方法如下:
在應用程式的資源檔案中添加想要增加的位圖,并将其ID命名為IDB_OPEN1和IDB_OPEN2;---在應用程式的視圖類中添加CBitmap類的對象,不妨取名為bm_open1和bm_open2。在視圖類的構造函數中添加以下代碼:
bm_open1.LoadBitmap(IDB_OPEN1);bm_open2.LoadBitmap(IDB_OPEN2);(函數LoadBitmap用于加載位圖到CBitmap類的對象)在視圖類的成員函數OnDraw()中添加以下代碼:
CWnd*parent=GetParent();CMenu*pmenubar=parent-$#@62;GetMenu();
CMenu*pmenu=pmenubar-$#@62;GetSubMenu(2);
pmenu-$#@62;SetMenuItemBitmaps(1,MF_BYPOSITION,&bm_open1,&bm_open2);
前三行得到指向菜單的指針,第四行調用函數SetMenuItemBitmaps往菜單中增加圖示,具體參數的含義可參見有關介紹MFC及其成員函數的書即可。
自繪菜單
在這裡提供一個C++類(CCustomMenu),該類是CMenu的子類,并且擁有自繪能力。它可以向你提供以下的功能:
- 設定字型顔色。
- 設定高亮度顔色。
- 設定高亮度時的風格。
- 設定選中時和在普通狀态下的菜單顯示的圖示。
- 設定顯示圖示大小。
在CCustomMenu中定義了結構MENUDATA,你必須根據你的需要填充該結構,并且在增加菜單時提供該結構的指針(調用AppendMenu,InsertMenu)。下面是一個例子:
1、定義CCustomMenu的執行個體,和MENUDATA結構變量。
CCustomMenu m_cCustomMenu;
MENUDATA menuData [8];
// as many menu items are present ,
//You should be able to use
//new and do the same
2、調用CreateMenu()設定有關參數。
m_customMenu.CreateMenu ();
m_customMenu.SetIconSize (25,25);
//This is to set the size of the Icon.
// This should be used only once for any menu
// in order to resize it, destroy and create the menu again with different size.
m_customMenu.SetHighlightStyle (Normal); //Or TextOnly, if you want the
// background color to remain the same
// and the Text color to change to the Highlight color.
// The following setXXXColor sets the menu colors.
//If you dont want to change any, Dont call these member functions.
m_customMenu.SetTextColor (RGB (255,0,0));
m_customMenu.SetBackColor (RGB (255,255,255));
m_customMenu.SetHighlightColor (RGB (0,0,255));
3、設定MENUDATA變量,并增加菜單項。
lstrcpy (menuData[0].menuText , "text1");
menuData[0].menuIconNormal= IDI_ICON1;
m_customMenu.AppendMenu (MF_OWNERDRAW,3,(LPCTSTR)menuData);
3、在你的視窗中重載OnMeasureItem(...)函數。
void CMyView::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
if ( lpMeasureItemStruct->CtlType == ODT_MENU &&
IsMenu((HMENU)lpMeasureItemStruct->i ID) &&
(lpMeasureItemStruct->itemID == (UINT)m_hMenuSub) )
{
m_customMenu.MeasureItem (lpMeasureItemStruct);
}
else
// let MFCs self-drawing handle it
CView::OnMeasureItem(nIDCtl, lpMeasureItemStruct);
}
下面的函數将幫助你設定菜單屬性。
void SetTextColor (COLORREF );
void SetBackColor (COLORREF);
void SetHighlightColor (COLORREF);
void SetIconSize (int, int);
void SetHighlightStyle (HIGHLIGHTSTYLE );
// HIGHLIGHTSTYLE : enum {Normal, TextOnly}
void SetHighlightTextColor (COLORREF);
下面是檔案代碼:
//*************************************************************************
// CustomMenu.h : header file
//
#if
!defined(AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_)
#define AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
class MENUDATA
{
public:
MENUDATA () { menuIconNormal = -1; menuIconSelected = -1;};
char menuText[32];
UINT menuIconNormal;
UINT menuIconSelected;
};
typedef enum {Normal,TextOnly} HIGHLIGHTSTYLE;
///
//
// CCustomMenu window
class CCustomMenu : public CMenu
{
// Construction
public:
CCustomMenu();
// Attributes
public:
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CCustomMenu)
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CCustomMenu();
virtual void DrawItem( LPDRAWITEMSTRUCT);
virtual void MeasureItem( LPMEASUREITEMSTRUCT );
void SetTextColor (COLORREF );
void SetBackColor (COLORREF);
void SetHighlightColor (COLORREF);
void SetIconSize (int, int);
void SetHighlightStyle (HIGHLIGHTSTYLE );
void SetHighlightTextColor (COLORREF);
// Generated message map functions
protected:
COLORREF m_crText;
COLORREF m_clrBack;
COLORREF m_clrText;
COLORREF m_clrHilight;
COLORREF m_clrHilightText;
LOGFONT m_lf;
CFont m_fontMenu;
UINT m_iMenuHeight;
BOOL m_bLBtnDown;
CBrush m_brBackground,m_brSelect;
CPen m_penBack;
int m_iconX,m_iconY;
HIGHLIGHTSTYLE m_hilightStyle;
//{{AFX_MSG(CCustomMenu)
// NOTE - the ClassWizard will add and remove member functions here.
//}}AFX_MSG
};
///
//
//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
#endif //!defined(AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_)
//*************************************************************************
// CustomMenu.cpp : implementation file
//
#include "stdafx.h"
#include "CustomMenu.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
///
//
// CCustomMenu
CCustomMenu::CCustomMenu()
{
m_clrText = GetSysColor (COLOR_MENUTEXT);
m_clrBack = GetSysColor (COLOR_MENU);
m_brBackground.CreateSolidBrush (m_clrBack);
m_penBack.CreatePen (PS_SOLID,0,m_clrBack);
m_crText = m_clrText;
m_bLBtnDown = FALSE;
m_iconX = GetSystemMetrics ( SM_CXMENUCHECK);
m_iconY = GetSystemMetrics (SM_CYMENUCHECK );
m_clrHilight = GetSysColor (COLOR_HIGHLIGHT);
m_brSelect.CreateSolidBrush (m_clrHilight);
m_clrHilightText = GetSysColor (COLOR_HIGHLIGHTTEXT);
ZeroMemory ((PVOID) &m_lf,sizeof (LOGFONT));
NONCLIENTMETRICS nm;
nm.cbSize = sizeof (NONCLIENTMETRICS);
//Get the system metrics for the Captionfromhere
VERIFY (SystemParametersInfo (SPI_GETNONCLIENTMETRICS,0,&nm,0));
m_lf = nm.lfMenuFont;
m_iMenuHeight = nm.iMenuHeight;
m_fontMenu.CreateFontIndirect (&m_lf);
}
CCustomMenu::~CCustomMenu()
{
if ((HBRUSH) m_brBackground != NULL)
m_brBackground.DeleteObject ();
if ((HFONT)m_fontMenu !=NULL)
m_fontMenu.DeleteObject ();
if ((HBRUSH)m_brSelect != NULL)
m_brSelect.DeleteObject ();
}
///
//
// CCustomMenu message handlers
void CCustomMenu::DrawItem (LPDRAWITEMSTRUCT lpDIS)
{
ASSERT(lpDIS != NULL);
CDC* pDC = CDC::FromHandle(lpDIS->hDC);
CRect rect;
HICON hIcon;
COLORREF crText = m_crText;
// draw the colored rectangle portion
rect.CopyRect(&lpDIS->rcItem);
// draw the up/down/focused/disabled state
UINT action = lpDIS->itemAction;
UINT state = lpDIS->itemState;
CString strText;
LOGFONT lf;
lf = m_lf;
CFont dispFont;
CFont *pFont;
//GetWindowText(strText);
if (lpDIS->itemData != NULL)
{
strText = (((MENUDATA*) (lpDIS->itemData))->menuText);
if ((((MENUDATA *)(lpDIS->itemData))->menuIconNormal) == -1)
hIcon = NULL;
else if (state & ODS_SELECTED)
{
if ((((MENUDATA *)(lpDIS->itemData))->menuIconSelected) != -1)
hIcon = AfxGetApp ()->LoadIcon ((
(MENUDATA *)(lpDIS->itemData))->menuIconSelected);
else
hIcon = AfxGetApp()->LoadIcon ((
(MENUDATA*)(lpDIS->itemData))->menuIconNormal);
}
else
hIcon = AfxGetApp()->LoadIcon (
((MENUDATA*)(lpDIS->itemData))->menuIconNormal);
TRACE1 ("Draw for %s\n", strText);
}
else
{
strText.Empty();
hIcon = NULL;
}
if ( (state & ODS_SELECTED) )
{
// draw the down edges
CPen *pOldPen = pDC->SelectObject (&m_penBack);
//You need only Text highlight and thats what you get
if (m_hilightStyle != Normal)
{
pDC->FillRect (rect,&m_brBackground);
}
else
{
pDC->FillRect (rect,&m_brSelect);
}
pDC->SelectObject (pOldPen);
pDC->Draw3dRect (rect,GetSysColor (COLOR_3DHILIGHT),GetSysColor(COLOR_3DSHADOW));
lf.lfWeight = FW_BOLD;
if ((HFONT)dispFont != NULL)
dispFont.DeleteObject ();
dispFont.CreateFontIndirect (&lf);
crText = m_clrHilightText;
//While selected move the text a bit
TRACE0 ("SELECT,SELECTED\n");
}
else
{
CPen *pOldPen = pDC->SelectObject (&m_penBack);
pDC->FillRect (rect,&m_brBackground);
pDC->SelectObject (pOldPen);
// draw the up edges
pDC->Draw3dRect (rect,m_clrBack,m_clrBack);
if ((HFONT)dispFont != NULL)
dispFont.DeleteObject ();
dispFont.CreateFontIndirect (&lf); //Normal
TRACE0 ("SELECT, NORMAL\n");
}
// draw the text if there is any
//We have to paint the text only if the image is nonexistant
if (hIcon != NULL)
{
if(DrawIconEx (pDC->GetSafeHdc(),rect.left,rect.top,hIcon,
(m_iconX)?m_iconX:32,(m_iconY)?m_iconY:32,0,NULL,DI_NORMAL))
TRACE0("Wrote the icon successfully\n");
else
TRACE0 ("SORRY.NOGO\n");
}
//This is needed always so that we can have the space for check marks
rect.left = rect.left +((m_iconX)?m_iconX:32);
if ( !strText.IsEmpty())
{
// pFont->GetLogFont (&lf);
int iOldMode = pDC->GetBkMode();
pDC->SetBkMode( TRANSPARENT);
pDC->SetTextColor( crText);
pFont = pDC->SelectObject (&dispFont);
TRACE1( "About To DrawText %s\n",strText);
pDC->DrawText (strText,rect,DT_LEFT|DT_SINGLELINE|DT_VCENTER);
TRACE0("Done\n");
pDC->SetBkMode( iOldMode );
pDC->SelectObject (pFont); //set it to the old font
}
dispFont.DeleteObject ();
}
void CCustomMenu::MeasureItem( LPMEASUREITEMSTRUCT lpMIS )
{
CDC *pDC = AfxGetApp()->m_pMainWnd->GetDC();
CFont* pFont = pDC->SelectObject (&m_fontMenu);
int iconX = 0,iconY= 0;
TEXTMETRIC tm;
pDC->GetTextMetrics (&tm);
pDC->SelectObject (pFont);
AfxGetApp()->m_pMainWnd->ReleaseDC (pDC);
if (m_iconX)
iconX = m_iconX;
if (m_iconY)
iconY = m_iconY;
lpMIS->itemWidth = iconX + tm.tmAveCharWidth
* lstrlen(((MENUDATA*)(lpMIS->itemData))->menuText) +10;
lpMIS->itemHeight = (iconY > (m_iMenuHeight+1))?iconY:m_iMenuHeight + 1;
}
void CCustomMenu::SetIconSize (int width, int height)
{
m_iconX = width;
m_iconY = height;
}
void CCustomMenu::SetTextColor (COLORREF clrText)
{
m_crText = clrText;
}
void CCustomMenu::SetBackColor (COLORREF clrBack)
{
m_clrBack = clrBack;
if ((HBRUSH)m_brBackground != NULL)
m_brBackground.DeleteObject ();
m_brBackground.CreateSolidBrush (clrBack);
}
void CCustomMenu::SetHighlightColor (COLORREF clrHilight)
{
m_clrHilight = clrHilight;
if ((HBRUSH)m_brSelect != NULL)
m_brSelect.DeleteObject ();
m_brSelect.CreateSolidBrush (clrHilight);
}
void CCustomMenu::SetHighlightTextColor (COLORREF clrHilightText)
{
m_clrHilightText = clrHilightText;
}
void CCustomMenu::SetHighlightStyle (HIGHLIGHTSTYLE hilightStyle)
{
m_hilightStyle = hilightStyle;
}
//*************************************************************************
在系統菜單中加子菜單
系統菜單與其它菜單類似,你可以添加或删除項目,這需要使用CMenu 類的成員函數。下面的代碼在你的系統菜單後面添加一個新菜單項:
CMenu *sysmenu;
sysmenu = m_pMainWnd->GetSystemMenu(FALSE);
sysmenu->AppendMenu(MF_STRING, 1000, "xxx");
參見MFC 幫助檔案中的CMenu 類
在系統菜單中加子菜單
系統菜單與其它菜單類似,你可以添加或删除項目,這需要使用CMenu 類的成員函數。下面的代碼在你的系統菜單後面添加一個新菜單項:
CMenu *sysmenu;
sysmenu = m_pMainWnd->GetSystemMenu(FALSE);
sysmenu->AppendMenu(MF_STRING, 1000, "xxx");
參見MFC 幫助檔案中的CMenu 類
MFC的CMenu類有一個成員函數SetMenuItemBitmaps,可以用于往菜單中增加圖示。具體方法如下:
在應用程式的資源檔案中添加想要增加的位圖,并将其ID命名為IDB_OPEN1和IDB_OPEN2;---在應用程式的視圖類中添加CBitmap類的對象,不妨取名為bm_open1和bm_open2。在視圖類的構造函數中添加以下代碼:
bm_open1.LoadBitmap(IDB_OPEN1);bm_open2.LoadBitmap(IDB_OPEN2);(函數LoadBitmap用于加載位圖到CBitmap類的對象)在視圖類的成員函數OnDraw()中添加以下代碼:
CWnd*parent=GetParent();CMenu*pmenubar=parent-$#@62;GetMenu();
CMenu*pmenu=pmenubar-$#@62;GetSubMenu(2);
pmenu-$#@62;SetMenuItemBitmaps(1,MF_BYPOSITION,&bm_open1,&bm_open2);
前三行得到指向菜單的指針,第四行調用函數SetMenuItemBitmaps往菜單中增加圖示,具體參數的含義可參見有關介紹MFC及其成員函數的書即可。
自繪菜單
在這裡提供一個C++類(CCustomMenu),該類是CMenu的子類,并且擁有自繪能力。它可以向你提供以下的功能:
- 設定字型顔色。
- 設定高亮度顔色。
- 設定高亮度時的風格。
- 設定選中時和在普通狀态下的菜單顯示的圖示。
- 設定顯示圖示大小。
在CCustomMenu中定義了結構MENUDATA,你必須根據你的需要填充該結構,并且在增加菜單時提供該結構的指針(調用AppendMenu,InsertMenu)。下面是一個例子:
1、定義CCustomMenu的執行個體,和MENUDATA結構變量。
CCustomMenu m_cCustomMenu;
MENUDATA menuData [8];
// as many menu items are present ,
//You should be able to use
//new and do the same
2、調用CreateMenu()設定有關參數。
m_customMenu.CreateMenu ();
m_customMenu.SetIconSize (25,25);
//This is to set the size of the Icon.
// This should be used only once for any menu
// in order to resize it, destroy and create the menu again with different size.
m_customMenu.SetHighlightStyle (Normal); //Or TextOnly, if you want the
// background color to remain the same
// and the Text color to change to the Highlight color.
// The following setXXXColor sets the menu colors.
//If you dont want to change any, Dont call these member functions.
m_customMenu.SetTextColor (RGB (255,0,0));
m_customMenu.SetBackColor (RGB (255,255,255));
m_customMenu.SetHighlightColor (RGB (0,0,255));
3、設定MENUDATA變量,并增加菜單項。
lstrcpy (menuData[0].menuText , "text1");
menuData[0].menuIconNormal= IDI_ICON1;
m_customMenu.AppendMenu (MF_OWNERDRAW,3,(LPCTSTR)menuData);
3、在你的視窗中重載OnMeasureItem(...)函數。
void CMyView::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
if ( lpMeasureItemStruct->CtlType == ODT_MENU &&
IsMenu((HMENU)lpMeasureItemStruct->i ID) &&
(lpMeasureItemStruct->itemID == (UINT)m_hMenuSub) )
{
m_customMenu.MeasureItem (lpMeasureItemStruct);
}
else
// let MFCs self-drawing handle it
CView::OnMeasureItem(nIDCtl, lpMeasureItemStruct);
}
下面的函數将幫助你設定菜單屬性。
void SetTextColor (COLORREF );
void SetBackColor (COLORREF);
void SetHighlightColor (COLORREF);
void SetIconSize (int, int);
void SetHighlightStyle (HIGHLIGHTSTYLE );
// HIGHLIGHTSTYLE : enum {Normal, TextOnly}
void SetHighlightTextColor (COLORREF);
下面是檔案代碼:
//*************************************************************************
// CustomMenu.h : header file
//
#if
!defined(AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_)
#define AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
class MENUDATA
{
public:
MENUDATA () { menuIconNormal = -1; menuIconSelected = -1;};
char menuText[32];
UINT menuIconNormal;
UINT menuIconSelected;
};
typedef enum {Normal,TextOnly} HIGHLIGHTSTYLE;
///
//
// CCustomMenu window
class CCustomMenu : public CMenu
{
// Construction
public:
CCustomMenu();
// Attributes
public:
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CCustomMenu)
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CCustomMenu();
virtual void DrawItem( LPDRAWITEMSTRUCT);
virtual void MeasureItem( LPMEASUREITEMSTRUCT );
void SetTextColor (COLORREF );
void SetBackColor (COLORREF);
void SetHighlightColor (COLORREF);
void SetIconSize (int, int);
void SetHighlightStyle (HIGHLIGHTSTYLE );
void SetHighlightTextColor (COLORREF);
// Generated message map functions
protected:
COLORREF m_crText;
COLORREF m_clrBack;
COLORREF m_clrText;
COLORREF m_clrHilight;
COLORREF m_clrHilightText;
LOGFONT m_lf;
CFont m_fontMenu;
UINT m_iMenuHeight;
BOOL m_bLBtnDown;
CBrush m_brBackground,m_brSelect;
CPen m_penBack;
int m_iconX,m_iconY;
HIGHLIGHTSTYLE m_hilightStyle;
//{{AFX_MSG(CCustomMenu)
// NOTE - the ClassWizard will add and remove member functions here.
//}}AFX_MSG
};
///
//
//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
#endif //!defined(AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_)
//*************************************************************************
// CustomMenu.cpp : implementation file
//
#include "stdafx.h"
#include "CustomMenu.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
///
//
// CCustomMenu
CCustomMenu::CCustomMenu()
{
m_clrText = GetSysColor (COLOR_MENUTEXT);
m_clrBack = GetSysColor (COLOR_MENU);
m_brBackground.CreateSolidBrush (m_clrBack);
m_penBack.CreatePen (PS_SOLID,0,m_clrBack);
m_crText = m_clrText;
m_bLBtnDown = FALSE;
m_iconX = GetSystemMetrics ( SM_CXMENUCHECK);
m_iconY = GetSystemMetrics (SM_CYMENUCHECK );
m_clrHilight = GetSysColor (COLOR_HIGHLIGHT);
m_brSelect.CreateSolidBrush (m_clrHilight);
m_clrHilightText = GetSysColor (COLOR_HIGHLIGHTTEXT);
ZeroMemory ((PVOID) &m_lf,sizeof (LOGFONT));
NONCLIENTMETRICS nm;
nm.cbSize = sizeof (NONCLIENTMETRICS);
//Get the system metrics for the Captionfromhere
VERIFY (SystemParametersInfo (SPI_GETNONCLIENTMETRICS,0,&nm,0));
m_lf = nm.lfMenuFont;
m_iMenuHeight = nm.iMenuHeight;
m_fontMenu.CreateFontIndirect (&m_lf);
}
CCustomMenu::~CCustomMenu()
{
if ((HBRUSH) m_brBackground != NULL)
m_brBackground.DeleteObject ();
if ((HFONT)m_fontMenu !=NULL)
m_fontMenu.DeleteObject ();
if ((HBRUSH)m_brSelect != NULL)
m_brSelect.DeleteObject ();
}
///
//
// CCustomMenu message handlers
void CCustomMenu::DrawItem (LPDRAWITEMSTRUCT lpDIS)
{
ASSERT(lpDIS != NULL);
CDC* pDC = CDC::FromHandle(lpDIS->hDC);
CRect rect;
HICON hIcon;
COLORREF crText = m_crText;
// draw the colored rectangle portion
rect.CopyRect(&lpDIS->rcItem);
// draw the up/down/focused/disabled state
UINT action = lpDIS->itemAction;
UINT state = lpDIS->itemState;
CString strText;
LOGFONT lf;
lf = m_lf;
CFont dispFont;
CFont *pFont;
//GetWindowText(strText);
if (lpDIS->itemData != NULL)
{
strText = (((MENUDATA*) (lpDIS->itemData))->menuText);
if ((((MENUDATA *)(lpDIS->itemData))->menuIconNormal) == -1)
hIcon = NULL;
else if (state & ODS_SELECTED)
{
if ((((MENUDATA *)(lpDIS->itemData))->menuIconSelected) != -1)
hIcon = AfxGetApp ()->LoadIcon ((
(MENUDATA *)(lpDIS->itemData))->menuIconSelected);
else
hIcon = AfxGetApp()->LoadIcon ((
(MENUDATA*)(lpDIS->itemData))->menuIconNormal);
}
else
hIcon = AfxGetApp()->LoadIcon (
((MENUDATA*)(lpDIS->itemData))->menuIconNormal);
TRACE1 ("Draw for %s\n", strText);
}
else
{
strText.Empty();
hIcon = NULL;
}
if ( (state & ODS_SELECTED) )
{
// draw the down edges
CPen *pOldPen = pDC->SelectObject (&m_penBack);
//You need only Text highlight and thats what you get
if (m_hilightStyle != Normal)
{
pDC->FillRect (rect,&m_brBackground);
}
else
{
pDC->FillRect (rect,&m_brSelect);
}
pDC->SelectObject (pOldPen);
pDC->Draw3dRect (rect,GetSysColor (COLOR_3DHILIGHT),GetSysColor(COLOR_3DSHADOW));
lf.lfWeight = FW_BOLD;
if ((HFONT)dispFont != NULL)
dispFont.DeleteObject ();
dispFont.CreateFontIndirect (&lf);
crText = m_clrHilightText;
//While selected move the text a bit
TRACE0 ("SELECT,SELECTED\n");
}
else
{
CPen *pOldPen = pDC->SelectObject (&m_penBack);
pDC->FillRect (rect,&m_brBackground);
pDC->SelectObject (pOldPen);
// draw the up edges
pDC->Draw3dRect (rect,m_clrBack,m_clrBack);
if ((HFONT)dispFont != NULL)
dispFont.DeleteObject ();
dispFont.CreateFontIndirect (&lf); //Normal
TRACE0 ("SELECT, NORMAL\n");
}
// draw the text if there is any
//We have to paint the text only if the image is nonexistant
if (hIcon != NULL)
{
if(DrawIconEx (pDC->GetSafeHdc(),rect.left,rect.top,hIcon,
(m_iconX)?m_iconX:32,(m_iconY)?m_iconY:32,0,NULL,DI_NORMAL))
TRACE0("Wrote the icon successfully\n");
else
TRACE0 ("SORRY.NOGO\n");
}
//This is needed always so that we can have the space for check marks
rect.left = rect.left +((m_iconX)?m_iconX:32);
if ( !strText.IsEmpty())
{
// pFont->GetLogFont (&lf);
int iOldMode = pDC->GetBkMode();
pDC->SetBkMode( TRANSPARENT);
pDC->SetTextColor( crText);
pFont = pDC->SelectObject (&dispFont);
TRACE1( "About To DrawText %s\n",strText);
pDC->DrawText (strText,rect,DT_LEFT|DT_SINGLELINE|DT_VCENTER);
TRACE0("Done\n");
pDC->SetBkMode( iOldMode );
pDC->SelectObject (pFont); //set it to the old font
}
dispFont.DeleteObject ();
}
void CCustomMenu::MeasureItem( LPMEASUREITEMSTRUCT lpMIS )
{
CDC *pDC = AfxGetApp()->m_pMainWnd->GetDC();
CFont* pFont = pDC->SelectObject (&m_fontMenu);
int iconX = 0,iconY= 0;
TEXTMETRIC tm;
pDC->GetTextMetrics (&tm);
pDC->SelectObject (pFont);
AfxGetApp()->m_pMainWnd->ReleaseDC (pDC);
if (m_iconX)
iconX = m_iconX;
if (m_iconY)
iconY = m_iconY;
lpMIS->itemWidth = iconX + tm.tmAveCharWidth
* lstrlen(((MENUDATA*)(lpMIS->itemData))->menuText) +10;
lpMIS->itemHeight = (iconY > (m_iMenuHeight+1))?iconY:m_iMenuHeight + 1;
}
void CCustomMenu::SetIconSize (int width, int height)
{
m_iconX = width;
m_iconY = height;
}
void CCustomMenu::SetTextColor (COLORREF clrText)
{
m_crText = clrText;
}
void CCustomMenu::SetBackColor (COLORREF clrBack)
{
m_clrBack = clrBack;
if ((HBRUSH)m_brBackground != NULL)
m_brBackground.DeleteObject ();
m_brBackground.CreateSolidBrush (clrBack);
}
void CCustomMenu::SetHighlightColor (COLORREF clrHilight)
{
m_clrHilight = clrHilight;
if ((HBRUSH)m_brSelect != NULL)
m_brSelect.DeleteObject ();
m_brSelect.CreateSolidBrush (clrHilight);
}
void CCustomMenu::SetHighlightTextColor (COLORREF clrHilightText)
{
m_clrHilightText = clrHilightText;
}
void CCustomMenu::SetHighlightStyle (HIGHLIGHTSTYLE hilightStyle)
{
m_hilightStyle = hilightStyle;
}
//*************************************************************************
在系統菜單中加子菜單
系統菜單與其它菜單類似,你可以添加或删除項目,這需要使用CMenu 類的成員函數。下面的代碼在你的系統菜單後面添加一個新菜單項:
CMenu *sysmenu;
sysmenu = m_pMainWnd->GetSystemMenu(FALSE);
sysmenu->AppendMenu(MF_STRING, 1000, "xxx");
參見MFC 幫助檔案中的CMenu 類
在系統菜單中加子菜單
系統菜單與其它菜單類似,你可以添加或删除項目,這需要使用CMenu 類的成員函數。下面的代碼在你的系統菜單後面添加一個新菜單項:
CMenu *sysmenu;
sysmenu = m_pMainWnd->GetSystemMenu(FALSE);
sysmenu->AppendMenu(MF_STRING, 1000, "xxx");
參見MFC 幫助檔案中的CMenu 類
千奇百怪的窗體
VC++對話框的任意擴充
我們在資訊輸入的時候,可能有很大的資訊量,而這些資訊又不是必須的,這時我們就需要給資訊輸入人員一個選擇的接口。例如一個人事部門的職工資訊錄入系統就有這樣的問題,其中的姓名、性别、年齡、政治面目、職務、學曆、部門和聯系電話是必須輸入的資訊,而婚姻狀況、畢業學校、籍貫和健康狀況是可輸可不輸的資訊且大多數情況下不需要錄入,如何為資訊錄入人員提供一個友善的輸入接口,下面我們就針對這個問題提供一個我認為比較好的方法。
第一步:在VC程式設計環境下建立一個基于對話框的工程,工程名為ExpandDlg,所有的選項都取預設值。
第二步:建立我們都對話框,其中必須要有這樣兩個控件,一個是PICTURE控件,一個為按鈕,其ID值分别為IDC_DIVIDER和IDC_MORE。其它的控件可以任意布局,最終結果就是對話框被IDC_DIVIDER控件分成了兩部分,其中下半部分可以根據你的愛好動态顯示或不顯示,對話框如下圖:
第三步:生成按鈕IDC_MORE的消息映射函數OnMore,在ExpandDlgDlg.h中定義兩個函數如下:
public: void EnableVisibleChildren(); void ExpandDialog (int nResourceID, BOOL bExpand); |
第四步:在ExpandDlgDlg.cpp中定義函數的實作代碼如下:
void CExpandDlgDlg::ExpandDialog (int nResourceID, BOOL bExpand) { // 對話框被nResourceID分成上下兩部分,如果bExpand的值為TRUE // 對話框被完整顯示,否則對話框顯示上半部分。 static CRect rcLarge; static CRect rcSmall; CString sExpand; //開始時,對話框隻顯示上半部分 if (rcLarge.IsRectNull()) { CRect rcLandmark; CWnd* pWndLandmark = GetDlgItem (nResourceID); ASSERT(pWndLandmark); GetWindowRect (rcLarge); pWndLandmark->GetWindowRect (rcLandmark); rcSmall = rcLarge; rcSmall.bottom = rcLandmark.top; } if (bExpand) { //擴充對話框到最大尺寸 SetWindowPos(NULL, 0, 0, rcLarge.Width(), rcLarge.Height(), SWP_NOMOVE | SWP_NOZORDER); sExpand = "<< &Less"; EnableVisibleChildren(); } else { //隻顯示對話框的上半部分 SetWindowPos(NULL, 0, 0, rcSmall.Width(), rcSmall.Height(), SWP_NOMOVE | SWP_NOZORDER); sExpand = " &More >>"; EnableVisibleChildren(); } SetDlgItemText (IDC_MORE, sExpand); } void CExpandDlgDlg::EnableVisibleChildren() { //去掉沒有顯示的對話框的控件的功能和快捷鍵。 //得到第一個視窗 CWnd *pWndCtl = GetWindow (GW_CHILD); CRect rcTest; CRect rcControl; CRect rcShow; //得到對話框的完整矩形框 GetWindowRect(rcShow); while (pWndCtl != NULL) { //得到目前顯示的對話框的矩形尺寸 pWndCtl->GetWindowRect (rcControl); if (rcTest.IntersectRect (rcShow, rcControl)) pWndCtl->EnableWindow(TRUE); else pWndCtl->EnableWindow(FALSE); //得到第二個矩形框 pWndCtl = pWndCtl->GetWindow (GW_HWNDNEXT); } } void CExpandDlgDlg::OnMore() { static BOOL bExpand = TRUE; ExpandDialog (IDC_DIVIDER, bExpand); bExpand = !bExpand; } |
按照上面的步驟生成我們的可執行檔案後運作,點選對話框上的〔More〕我們可以發現對話框擴充,點選〔Less〕後,我們發現對話框收縮,希望可以給你帶來友善。
使用VC建立不規則形狀視窗
仔細檢視了一下WIN32的API,發現其實建立任意形狀的視窗其實也是很簡單的,在VC中簡單步驟如下:
當我們注冊并建立了一個視窗類以後,我們在WM_CREATE消息中做如下處理:
(1)建立一個區域,使用CreatePolyonRgn,該函數建立一個多邊形區域,(也可以使用其他方法如CreateRectRgn建立矩形區域),該函數傳回一個HRGN的句柄;
(2)調用函數SetWindowRgn,即可設定視窗的形狀。
補充說明的是,我們可以制作多個區域,然後用CombineRgn方法将多個區域合并為一個區域。這樣我們就可以制作出更為豐富多采的視窗了。
VC程式設計實作IE風格的界面
使用過IE浏覽器的朋友都知道IE界面上的扁平工具條、位址欄,扁平工具欄上的按鈕正常狀态下為扁平态,按鈕上的圖像為灰色,當滑鼠放在按鈕上時,按鈕突起(這種狀态稱為搖桿),并且其上的圖像變得鮮豔醒目,一些按鈕上還有漢字說明或标有小黑三角的下拉按鈕,單擊時顯示下拉菜單,這些技術是怎麼實作的呢,本文針對這些問題介紹了如何利用VC程式設計來實作它們。
IE風格的實作主要在主架構類的CMainFrame::OnCreate()實作,它的主要思想如下:首先定義一個CReBar對象,用以作工具條、位址欄的容器,然後分别聲明圖像清單對象img用于存儲工具欄上按鈕的熱點圖像和正常狀态下顯示的圖像。為了顯示扁平工具欄,需要用CreateEx()函數建立CToolBar對象m_wndToolBar,用ModifyStyle()函數将工具欄的風格設為扁平類型,你不能用CToolBar::Create() 或 CToolBar:: SetBarStyle()設定這種新風格。CToolBar 類不支援TBSTYLE_FLAT。要解決這個問題,必須繞過CToolBar類,使用CWnd::ModifyStyle()。工具欄對象調用SetButtonInfo()設定按鈕的風格為TBSTYLE_DROPDOWN,就可以将工具欄按鈕設定為附帶有下拉按鈕。至于按鈕帶有中文提示,用工具欄的SetButtonText()就可以輕松實作了。下面是實作IE風格界面的部分代碼和注釋:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { CReBar m_wndReBar;//聲明CReBar對象 CImageList img;//聲明圖像清單對象 CString str; if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; if (!m_wndReBar.Create(this))//建立CReBar對象 { TRACE0("Failed to create rebar\n"); return -1; // fail to create } if (!m_wndToolBar.CreateEx(this))//建立工具條對象 { TRACE0("Failed to create toolbar\n"); return -1; // fail to create } // set up toolbar properties m_wndToolBar.GetToolBarCtrl().SetButtonWidth(50, 150); file://設定工具條上按鈕的最大、最小尺寸 m_wndToolBar.GetToolBarCtrl().SetExtendedStyle(TBSTYLE_EX_DRAWDDARROWS); file://工具條可以帶有下拉按鈕 img.Create(IDB_HOTTOOLBAR, 22, 0, RGB(255, 0, 255)); file://向圖像清單裝載熱點圖像資源,IDB_HOTTOOLBAR為熱點圖像資源ID m_wndToolBar.GetToolBarCtrl().SetHotImageList(&img);//工具條裝載熱點圖像 img.Detach(); img.Create(IDB_COLDTOOLBAR, 22, 0, RGB(255, 0, 255)); file://圖象清單裝載正常狀态的圖像資源,IDB_COLDTOOLBAR為圖像資源ID m_wndToolBar.GetToolBarCtrl().SetImageList(&img);//将圖像裝入工具條 img.Detach(); m_wndToolBar.ModifyStyle(0, TBSTYLE_FLAT | TBSTYLE_TRANSPARENT); file://工具條為扁平風格 m_wndToolBar.SetButtons(NULL, 9);//工具條上有9個按鈕 // set up each toolbar button file://以下分别對九個按鈕分别設定風格和按鈕漢語提示 m_wndToolBar.SetButtonInfo(0, ID_BUTTON0, TBSTYLE_BUTTON, 0); str.LoadString(IDS_ BUTTON0); m_wndToolBar.SetButtonText(0, str); m_wndToolBar.SetButtonInfo(1, ID_BUTTON1, TBSTYLE_BUTTON, 1); str.LoadString(IDS_ BUTTON1); m_wndToolBar.SetButtonText(1, str); m_wndToolBar.SetButtonInfo(2, ID_BUTTON2, TBSTYLE_BUTTON, 2); str.LoadString(IDS_ BUTTON2); m_wndToolBar.SetButtonText(2, str); m_wndToolBar.SetButtonInfo(3, ID_BUTTON3, TBSTYLE_BUTTON, 3); str.LoadString(IDS_ BUTTON3); m_wndToolBar.SetButtonText(3, str); m_wndToolBar.SetButtonInfo(4, ID_BUTTON4, TBSTYLE_BUTTON, 4); str.LoadString(IDS_ BUTTON4); m_wndToolBar.SetButtonText(4, str); m_wndToolBar.SetButtonInfo(5, ID_BUTTON5, TBSTYLE_BUTTON, 5); str.LoadString(IDS_ BUTTON5); m_wndToolBar.SetButtonText(5, str); m_wndToolBar.SetButtonInfo(6, ID_BUTTON6, TBSTYLE_BUTTON | TBSTYLE_DROPDOWN, 6); str.LoadString(IDS_ BUTTON6); m_wndToolBar.SetButtonText(6, str); m_wndToolBar.SetButtonInfo(7, ID_BUTTON7, TBSTYLE_BUTTON, 7); str.LoadString(IDS_ BUTTON7); m_wndToolBar.SetButtonText(7, str); m_wndToolBar.SetButtonInfo(8,ID_BUTTON8, TBSTYLE_BUTTON | TBSTYLE_DROPDOWN, 8); str.LoadString(IDS_ BUTTON8); m_wndToolBar.SetButtonText(8, str); file://重新調整按鈕的尺寸 CRect rectToolBar; m_wndToolBar.GetItemRect(0, &rectToolBar);//得到工具條第一個按鈕的尺寸 m_wndToolBar.SetSizes(rectToolBar.Size(), CSize(30,20)); file://第一個參數為按鈕尺寸,第二個參數為圖像尺寸 file://建立一個組合框作為位址欄 if (!m_wndAddress.Create(CBS_DROPDOWN | WS_CHILD, CRect(0, 0, 200, 120), this, AFX_IDW_TOOLBAR + 1)) { TRACE0("Failed to create combobox\n"); return -1; // fail to create } file://加入工具欄、位址欄 m_wndReBar.AddBar(&m_wndToolBar); str.LoadString(IDS_ADDRESS); m_wndReBar.AddBar(&m_wndAddress, str, NULL, RBBS_FIXEDBMP | RBBS_BREAK); file://定義REBARBANDINFO對象,對工具條和位址欄設定理想尺寸 REBARBANDINFO rbbi; rbbi.cbSize = sizeof(rbbi); rbbi.fMask = RBBIM_CHILDSIZE | RBBIM_IDEALSIZE | RBBIM_SIZE; rbbi.cxMinChild = rectToolBar.Width(); rbbi.cyMinChild = rectToolBar.Height(); rbbi.cx = rbbi.cxIdeal = rectToolBar.Width() * 9; m_wndReBar.GetReBarCtrl().SetBandInfo(0, &rbbi);//設定工具欄尺寸 rbbi.cxMinChild = 0; CRect rectAddress; rbbi.fMask = RBBIM_CHILDSIZE | RBBIM_IDEALSIZE; m_wndAddress.GetEditCtrl()->GetWindowRect(&rectAddress); rbbi.cyMinChild = rectAddress.Height() + 10; rbbi.cxIdeal = 200; m_wndReBar.GetReBarCtrl().SetBandInfo(2, &rbbi);//設定位址欄尺寸 m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_FIXED); 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 } return 0; } |
以上代碼在Windows2000和Visual C++環境下編譯通過,程式運作正常,有興趣的朋友可以動手親自實驗一下。
VC限制視窗大小又一法
一般說見到的方法,,都是截獲WM_GETMAXMININFO消息。
俺有另一經驗可實作之。
由于一般視窗大小的改變,都是使用者拖動視窗邊框而造成的。是以,我們可以截獲主視窗消息WM_NCHITTEST在其響應函數中判斷CWnd::OnNcHitTest()的傳回值是否為HTRIGHT,HTLEFT,HTTOP,HTBOTTOM四個值之一,如果是,說明使用者此時已點選了四個邊框之一,此時我們應該傳回HTCLIENT.那麼,滑鼠的形狀就不會變成水準或垂直的雙向箭頭,使用者就不可能依靠拖動邊框來改變視窗大小了。
另外,還應補上一個小漏洞,就是還要把系統菜單中的SC_SIZE去掉。
主程式之前的版權視窗
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
DWORD lTime;
try
{
Application->Initialize();
AboutBox=new TAboutBox(AboutBox);
AboutBox->BorderStyle=bsNone;
AboutBox->OKButton->Visible=false;
AboutBox->Height=185;
AboutBox->Show();
AboutBox->Update();
lTime=GetTickCount();
Application->CreateForm(__classid(TMainForm), &MainForm);
while((GetTickCount()-lTime) / 1000 < 3);
AboutBox->Hide();
AboutBox->Free();
Application->Run();
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
return 0;
}
VISUAL C++6.0在MDI主架構視窗中添加位圖
筆者在開發項目時想在MDI程式中添加彩色位圖以美化界面,也實驗了幾種方法,但都有一些小問題,經多方查找資料,終于圓滿的實作了這種功能,現把我的實作方法介紹給大家。
首先要清楚對于一個MDI應用程式的主架構視窗來說包含一個特殊的子視窗稱為MDICLIENT視窗,應用程式的主架構類中有一個成員變量m_hWndMDIClient 指的就是MDICLIENT視窗。MDICLIENT視窗負責管理主架構視窗的客戶區,對MDI客戶視窗程式設計有一定的難度。原因是MDIFrameWnd的客戶區完全被MDICLIENT視窗覆寫掉了。這樣,MDI主視窗類MDIFrameWnd的背景色和光标都不起作用。同時,微軟并不支援将MDICLIENT視窗作為子類,MDICLIENT視窗隻能使用标準的背景色和光标。是以,對MDI客戶視窗程式設計不能象對普通視窗那樣簡單地重載WM_PAINT的消息處理函數。我們可以在主架構視窗截獲關于MDICLIENT視窗的重畫消息,然後加入自己設計的代碼。我用PreTranslateMessage(MSG* pMsg) 截獲MDI客戶視窗WM_PAINT消息,在這個函數中向主架構視窗發送WM_PAINT消息,在該消息的處理函數中實作彩色位圖的顯示。我的具體實作如下:1、向程式添加256色彩色位圖資源,命名為IDB_BITMAP1;2、用ClassWizard向主架構類添加函數CMainFrame::PreTranslateMessage(MSG* pMsg);3、用ClassWizard向主架構類添加函數CMainFrame::OnPaint();現給出兩個函數的實作:
BOOL CMainFrame::PreTranslateMessage(MSG* pMsg) { // TODO: Add your specialized code here and/or call the base class if(pMsg->hwnd==m_hWndMDIClient && pMsg->message==WM_PAINT) PostMessage(WM_PAINT); return CMDIFrameWnd::PreTranslateMessage(pMsg); } void CMainFrame::OnPaint() { CDC dc, memdc; dc.m_hDC=::GetDC(this->m_hWndMDIClient); CRect rect; CBitmap bitmap; BITMAP szbitmap; bitmap.LoadBitmap(IDB_BITMAP1); bitmap.GetObject(sizeof(BITMAP),&szbitmap); CSize size(szbitmap.bmWidth,szbitmap.bmHeight); memdc.CreateCompatibleDC(&dc); CBitmap *oldbitmap=memdc.SelectObject(&bitmap); GetClientRect(&rect); StretchBlt(dc.m_hDC,0,0,rect.Width(),rect.Height(), memdc.m_hDC,0,0,size.cx,size.cy,SRCCOPY); memdc.SelectObject(oldbitmap); memdc.DeleteDC(); dc.DeleteDC(); CMDIFrameWnd::OnPaint(); } |
按上述步驟就可以實作在MDI程式中顯示彩色位圖了,我舉的例子用的是256色位圖,你也可以實作真彩色位圖的顯示,具體方法我就不多說了,有興趣的朋友可以試一試。
華麗的界面
VC6.0實作逆向操作并防止界面閃爍
在系統程式設計中,使用VC是很好的開發工具,而對于一個成熟的系統,幾乎都需要有回退與重做功能(即文檔操作逆向化)以防止使用者誤操作或不合适的操作,進而提高系統的友好性和可操作性。在很多VC技術文章中均提到過這個問題,不過總存在着界面閃爍或不完全可逆.
本文提出一種對系統程式設計可實作完全可逆并防止閃屏的方法.
一、基本原理
要對文檔進行回退重做功能,要做兩方面的工作,一方面要保留删除的文檔(在操作過程中,删除的文檔資料一定能夠保留),另一方面,系統必須能夠記錄進行文檔操作的全過程及每個操作過程的參數。為了保留曆史操作,所有資料非常占用記憶體空間,這就是一些系統隻能進行有限次退步逆向操作的原因。本文提出的方法建立如下存儲機制:建一個臨時檔案儲存資料模拟堆棧,進行一次操作時将相關操作資料入棧.回退一次将相關資料彈出棧,重做一次又依據相關資料重新恢複原有資料.它的好處是在回退和重做時隻入一次棧即申請一次記憶體。
堆棧的資料排放如圖:
// Undo、Redo 資料排放示意圖(m_UndoDataList)
// // ==== // |###| } // |###| } // |###| } ----->> Redo 資料 // |###| } // |###| } // |\\\| } // |\\\| } // |\\\| } // |\\\| } --->> Undo 資料(Undo資料彈出後将轉換為Redo資料) // |\\\| } // |\\\| } // ===== // Undo資料棧 |
二、實作文檔回退重做的引擎
建一文檔逆向化堆棧引擎.主要代碼為:
1.建立臨時檔案.(m_TempPath可以按照某種規則形成路徑)
if(m_File.Open((LPCTSTR)m_TempPath, CFile::modeCreate|CFile::modeReadWrite|CFile::shareExclusive)) { m_File.SeekToBegin(); m_UndoCount = 0; file://目前可重做的步數 m_RedoCount = 0; file://目前可回退的步數 } |
2.儲存回退資料子產品.
// 儲存一個Undo資料塊(由使用者提供) int CRedoUndoEngine::PushData( LPVOID pData, // 由使用者提供的記憶體塊首位址,其中含有使用者定義的待儲存的資料。 // (注:如果函數成功,此記憶體塊将會被本函數釋放,是以,該記憶體塊必須是用::GlobalAlloc()函數配置設定的) DWORD size, // pData指向的記憶體塊尺寸 DWORD param1, // 使用者提供的對該記憶體塊的說明參數,含義由使用者定義 DWORD param2, // 使用者提供的對該記憶體塊的說明參數,含義由使用者定義 int *pIndex // 如果成功,本函數将傳回壓入的Undo塊在棧中的索引值。 如果不需要此傳回值,可用NULL作為參數 ) { // 删除Redo資料 if (m_RedoCount) { while(m_RedoCount--) delete (LPISEEUNDOINFO)m_UndoDataList.RemoveTail(); m_RedoCount = 0; } // 填寫Undo資料的索引資訊(lpISeeUndoInfo為一個儲存資料的結構體) lpISeeUndoInfo->m_index = m_UndoCount; // 索引 lpISeeUndoInfo->m_UserData1 = param1; // 使用者定義的辨別性資料1 lpISeeUndoInfo->m_UserData2 = param2; // 使用者定義的辨別性資料2 lpISeeUndoInfo->m_DataSize = size; // 使用者的Undo資料塊尺寸 lpISeeUndoInfo->m_FilePosition = _get_current_overwrite_pos(); // 加新的Undo資料到Undo棧的尾部 m_UndoDataList.AddTail((void*)lpISeeUndoInfo); // 将使用者的Undo資料寫入臨時檔案 m_File.Seek(lpISeeUndoInfo->m_FilePosition, CFile::begin); m_File.Write((const void *)pData, size); 并使Undo塊計數加1 m_UndoCount++; // 此時使用者傳過來的資料塊已經無用,删除! ::GlobalFree(pData); return 1; } |
3.彈出重做資料子產品.
// 彈出一個Redo資料塊 int CIUndoEngine::RedoData( LPVOID *ppData, // 用于接收本函數傳回的含有最近一個Redo資料的記憶體塊首位址的指針 // (注:此記憶體塊交由調用者釋放,使用::GlobalFree()函數) DWORD *pSize, // ppData記憶體塊的尺寸(in byte) ,如果不需要此資料可用NULL作為參數 DWORD *pParam1, // 傳回使用者對該Redo塊的附加資訊,如果不需要此資料可用NULL作為參數 DWORD *pParam2, // 傳回使用者對該Redo塊的附加資訊,如果不需要此資料可用NULL作為參數 int *pIndex // 傳回本Redo塊的索引,如果不需要此資料可用NULL作為參數 ) { if (!m_RedoCount) return 0; // 鎖定待彈出的Redo索引資訊塊的位址 POSITION pos = m_UndoDataList.FindIndex(m_UndoCount); ASSERT(pos); LPISEEUNDOINFO lpISeeUndoInfo= (LPISEEUNDOINFO)m_UndoDataList.GetAt(pos); ASSERT(lpISeeUndoInfo); ASSERT(lpISeeUndoInfo->m_index == m_UndoCount); if (!(*ppData)) return -1; // 讀出使用者儲存在臨時檔案中的Undo資料(也即Redo資料) m_File.Seek((LONG)lpISeeUndoInfo->m_FilePosition, CFile::begin); m_File.Read(*ppData, lpISeeUndoInfo->m_DataSize); m_UndoCount++; // 可用Undo資料塊個數加1 m_RedoCount--; // 可用Redo資料塊個數減1 if (pSize) *pSize = lpISeeUndoInfo->m_DataSize; if (pParam1) *pParam1= lpISeeUndoInfo->m_UserData1; if (pParam2) *pParam2= lpISeeUndoInfo->m_UserData2; if (pIndex) *pIndex = m_RedoCount;// 注:此處的索引是Redo的索引,而不是Undo的 return 1; } |
由這個文檔逆向化操作引擎,可以獲得目前改動的文檔的資料,并根據改動的資料更新視圖,而不重新整理沒有更改資料的視圖.進而防止了閃爍的産生.
三、簡單開發執行個體
下面以我們開發服裝CAD過程中加入的回退重做功能(文檔逆向化)說明之。
1.定義回退類型
#define REUNDO_MOV 0x0001 file://衣片移動回退重做 #define REUNDO_SEL 0x0002 file://衣片選擇回退重做 ………. |
2.儲存某個操作之前和之後的資料(以衣片移動回退重做為例)
//----------申請記憶體----------------------// int nByte = 4*sizeof(DWORD); HGLOBAL hMem = GlobalAlloc(GMEM_FIXED,nByte); LPVOID pData = (LPVOID) GlobalLock(hMem); file://-----儲存衣片移動前後的位置讀入記憶體------//用移動前後衣片的某個坐标點表示 memcpy((DWORD*)pData, &m_oldPoint, 2*sizeof(DWORD)); memcpy((DWORD*)pData+2,&point, 2*sizeof(DWORD)); file://--------資料入棧---------------------------------------// m_pReUndoEngine->PushData(pData,//衣片m_pReUndoEngine文檔逆向化引擎對象指針 nByte,//儲存資料衣片位元組數 REUNDO_MOV,//回退類型 NULL,NULL); |
3.當回退操作事件觸發時.
//彈出回退值 int nByte = m_pReUndoEngine->GetPopDataSize(); HGLOBAL hMem = GlobalAlloc(GMEM_FIXED,nByte);//申請記憶體 LPVOID pData = (LPVOID) GlobalLock(hMem); DWORD undo_type;DWORD index; m_pReUndoEngine->PopData(&pData,NULL,&undo_type,&index); switch(undo_type){//回退類型 case REUNDO_SEL: SelUndo(pData,index,&dc);break; case REUNDO_MOV: MovUndo(pData);break; ………… } void CMarkView::MovUndo(LPVOID pData) 函數功能 { CPoint pt1,pt2; memcpy(&pt1,(DWORD*)pData,8); memcpy(&pt2,(DWORD*)pData+2,8); …….由pt1 和pt2可以求出位移量,進而恢複原衣片的位置. } |
4.當重做操作事件觸發時
//彈出回退值 int nByte = m_pReUndoEngine->GetRedoDataSize(); HGLOBAL hMem = GlobalAlloc(GMEM_FIXED,nByte);//申請記憶體 LPVOID pData = (LPVOID) GlobalLock(hMem); DWORD undo_type;DWORD index; m_pReUndoEngine->RedoData(&pData,NULL,&undo_type,&index); switch(undo_type){//回退類型 case REUNDO_SEL: SelRedo(pData,index,&dc,nByte);break;
case REUNDO_MOV: MovRedo(pData); break; ………… } |
函數MovRedo(pData)與MovUndo(pData)類似就不多說了.
由3,4可以看出,在回退與重做過程中,隻是儲存和取出操作對象已變化的過程,使程式設計者很容易實作高效率重新整理與充分節約存儲空間.
小結
在系統程式設計中,文檔的回退與重做幾乎是必不可少的,本文提出了一種思路,即對文檔的各種操作分解,并把每種操作下變化的對象的資料值儲存于臨時檔案(棧)中,在回退與重做時根據變化量很容易恢複操作之前狀态或重做, 避免了有些系統(儲存全部文檔資料)占用大量記憶體空間而隻能有限次文檔逆向化,并且全部重新整理而閃爍,破壞了界面的友好性。
怎樣在一個Pane中顯示多種View?
在MS Windows 中,一個視窗可以分割成若幹個子視窗,每一個子視窗稱作一個窗片(pane),每個窗片可以獨立控制,這給界面設計提供了很大的友善。
---- 利用VC 可以很友善地實作分割視窗。分割的方法有兩種:動态和靜态。動态分割時可以根據使用者的需要分割成數目不同的窗片,但所有窗片的屬性和父視窗都是一樣的;而靜态分割的窗片的數目在程式中指定,運作時是固定的,但每個窗片可以有各自不同類型的視(View),是以其使用範圍更為廣泛。本文所讨論的問題僅限于靜态分割。
---- 窗片中視的類型大多是在主視窗的建立過程中指定的。這也就意味着,一個窗片雖然可以顯示任意類型的視,但是這種類型一旦确定,在程式運作過程中就難以改變。
---- 一、我要的是這樣的!
---- 但是我們有時确實需要改變一個窗片所顯示的視的類型,也就是說,需要讓一個窗片顯示多種類型的視。例如一個視窗被分割成兩部分,一邊是指令視窗,另一邊是工作視窗,根據指令視窗中發出的不同指令,需要變換不同的工作類型,這就需要工作視窗中能夠顯示多種類型的視窗,那麼,如何做到這一點呢?
---- 二、你可以這樣做!
---- 從圖1 中可以看到,本程式共有三個視類,分别是:
---- ? 指令視類CCmdView:用來控制右邊窗片中不同視的顯示;
---- ? 選項按鈕視類CRdiView:顯示在右窗片中的選項視類;
---- ? 檢查按鈕視類CChkView:顯示在右窗片中的檢查視類。
---- 這三個視類都是CFormView 的子類。
---- 下面我們來看如何在右窗片内進行兩類視間的切換。實際上,由視A 切換到視B 的原理很簡單,那就是:
---- 1. 從窗片中删除視A;
---- 2. 往窗片中添加視B。
---- 步驟1 的實作非常簡單,僅用一條語句即可:
---- m_wndSplitter.DeleteView(0, 1);
---- 但它是必不可少的,因為你不能讓一個窗片同時包含兩個視。我本來希望往一個窗片中添加新的視時,VC 會自動将原來的視删簦墒撬桓傘?br>
---- 我們來看如何實作步驟2,當一個窗片是空的時候,怎樣往裡面添加一個視呢?其實這樣的功能在程式裡我們已經用過了,看下面的語句:
BOOL CMainFrame::OnCreateClient
(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
……
if (!m_wndSplitter.CreateView(0, 0,
pContext->m_pNe ewClass,
size,
pContext))
……
}
---- 是的,用的就是CSplitterWnd::CreateView(),要注意的是它共有五個參數,其中前兩個用來指定分割視窗的窗片,第三個用來指定視的類型,第四個指定視的大小。最後的一個我們暫時用不上,用空值NULL 就可以了。
---- 這樣我們就可以編寫視切換的代碼了。因為視切換要操縱m_wndSplitter,而它是主視窗的成員,是以切換過程最好設計為主視窗的成員函數。但是切換指令是CCmdView 接受的,因而可以讓CCmdView 接受到視更改消息後,将消息傳給主視窗,由主視窗完成視更改。具體的代碼是這樣的:
---- 指令視類中的消息映射:
BEGIN_MESSAGE_MAP(CCmdView, CFormView)
……
ON_BN_CLICKED(IDC_CHECK, OnSwitchToCheckView)
ON_BN_CLICKED(IDC_RADIO, OnSwitchToRadioView)
……
END_MESSAGE_MAP()
指令視類中的消息響應:
void CCmdView::OnSwitchToCheckView()
{
AfxGetApp()->m_pMainWnd->
SendMessage(WM_COMMAND, ID_CHECK);
}
void CCmdView::OnSwitchToRadioView()
{
AfxGetApp()->m_pMainWnd->
SendMessage(WM_COMMAND, ID_RADIO);
}
主視窗中的消息映射:
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
……
ON_COMMAND(ID_CHECK, OnSwitchToCheckView)
ON_COMMAND(ID_RADIO, OnSwitchToRadioView)
……
END_MESSAGE_MAP()
主視窗中的消息響應:
void CMainFrame::OnSwitchToCheckView()
{
m_wndSplitter.DeleteView(0, 1);
m_wndSplitter.CreateView(0, 1,
RUNTIME_CLASS(CChkView),
CSize(0, 0),
NULL);
m_wndSplitter.RecalcLayout();
}
void CMainFrame::OnSwitchToRadioView()
{
m_wndSplitter.DeleteView(0, 1);
m_wndSplitter.CreateView(0, 1,
RUNTIME_CLASS(CRdiView),
CSize(0, 0),
NULL);
m_wndSplitter.RecalcLayout();
}
---- 好啦,運作一下這個程式,感覺是否不錯?看來大功告成了,可是……
---- 三、還有一個問題
---- 在運作我們辛辛苦苦編出來的程式時,回頭看看VC 的調試視窗,你會發現有很多行這樣的話:
---- Create view without document.
---- 這是說我們建立了視,可是沒有相應的文檔。好在這隻是警告資訊,不是什麼錯誤,如果你不需要相應的文檔,就完全不用去管它。可是,VC 中一種很重要的結構就是文檔- 視結構,利用這種結構,對資料操縱起來非常友善。如果需要建立與視相對應的文檔,應該怎麼辦呢?
---- 這就涉及到VC 中文檔- 視結構的知識,不過不用怕麻煩,與本文有關的就隻有這麼兩點而已:
---- 1. 利用VC 建立的應用程式一般都會管理一些文檔模闆(Document Template),文檔類和視類的對應關系就是在文檔模闆裡描述的。
---- 2. 一個文檔可以有多個視,建立視的時候,需要根據文檔和視的對應關系,給出它所依附的文檔。
---- 怎樣實作上述第一點呢?
---- 首先建立相應的文檔類:CRdiDoc 和CChkDoc。
---- 其次是定義相應的文檔模闆,這是應用類的成員變量。因為在别的類中要使用它們,我們将之定義為公共類型:
class CViewSwitcherApp : public CWinApp
{
……
public:
CSingleDocTemplate* m_pRdiDocTemplate;
CSingleDocTemplate* m_pChkDocTemplate;
……
}
然後建立這兩個文檔模闆,并加入到模闆清單中:
BOOL CViewSwitcherApp::InitInstance()
{
……
m_pRdiDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CRdiDoc),
RUNTIME_CLASS(CMainFrame),
RUNTIME_CLASS(CRdiView));
AddDocTemplate(m_pRdiDocTemplate);
m_pChkDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CChkDoc),
RUNTIME_CLASS(CMainFrame),
RUNTIME_CLASS(CChkView));
AddDocTemplate(m_pChkDocTemplate);
……
}
---- 至于第二點,是在建立視時完成的。還記得建立視的情況麼?當時有一個叫做pCreateContext 的參數,我們将之置為空,這裡就要用到它了。
---- pCreateContext 是一個指向被稱作" 建立上下文"(CreateContext) 結構的指針,這個結構中儲存一些與建立視相關的内容。在建立主視窗時,系統會構造這樣一個結構,并将它作為參數傳遞到與建立視有關的函數中。但現在我們不建立主視窗,是以不得不自己構造這樣一個結構。實際上,該結構中我們所要使用的字段隻有三個:
---- 1. 新視所屬的文檔模闆m_pNewDocTemplate;
---- 2. 新視的類型m_pNewViewClass;
---- 3. 新視所屬的文檔m_pCurrentDoc;
---- 其中僅有第三項需要建立,前兩項都是已知的,隻要指定即可。以切換到選項視為例,修改後的代碼是:
void CMainFrame::OnSwitchToRadioView()
{
m_wndSplitter.DeleteView(0, 1);
CCreateContext createContext;
// 定義并初始化CreateContext
// 擷取新視所屬的文檔模闆
CSingleDocTemplate* pDocTemplate =
((CViewSwitcherApp*)AfxGetApp())-> m_pRdiDocTemplate;
// 建立新文檔并初始化
CDocument* pDoc = pDocTemplate->CreateNewDocument();
pDoc->OnNewDocument();
// 設定CreateContext 相關字段
createContext.m_pNewViewClass = RUNTIME_CLASS(CChkView);
createContext.m_pCurrentDoc = pDoc;
createContext.m_pNewDocTemplate = pDocTemplate;
m_wndSplitter.CreateView(0, 1,
RUNTIME_CLASS(CRdiView),
CSize(0, 0),
&createContext);
m_wndSplitter.RecalcLayout();
}
---- 四、最後的修改
---- 為了使這個程式更符合要求,我們還要做一些與更換視無關的修改。在這個程式中我們一共定義了三種類型的文檔,程式啟動時一般要建立一個文檔開始工作,可是它不知道要選擇哪一種,就彈出一個對話框來詢問。而這是我們不希望看到的。修改的方法是不讓VC 選擇新文檔類型,而我們指定建立哪一種類型的文檔,即把CViewSwitcherApp::CViewSwitcherApp() 中的語句
---- if (!ProcessShellCommand(cmdInfo)) return FALSE;
---- 更改為
---- m_pDocTemplate->OpenDocumentFile(NULL)。
讓基于對話框應用程式也有啟動畫面
用MFC的應用向導建立一個有主架構結構的應用程式,要使它具有啟動畫面是很簡單的(下面會體驗到),而要使一個基于對話框的應用程式也有啟動畫面則要費些事了,不過按以下筆者的方法則也是很容易的,我主要介紹方法,對畫面僅采用預設情況,讀者有興趣可自己加工。
一、給一文檔/視圖應用程式做啟動畫面
(一) 建立一單文檔/視圖應用程式Hs
從File菜單選擇New對話,在Projects頁籤中選擇AppWizard(exe)圖示。鍵入Hs作為項目名并單擊OK。在第一步中選中Single Document單選按鈕,其它接受所有預設選項。
(二) 添加啟動畫面
當AppWizard完成,而且Visual C++打開項目的時候,從Project菜單中選擇Add To Project,并單擊位于次級菜單上的Comonents and Controls…,選擇Splash screen元件,如圖1(略)所示,單擊Insert。接受所有的預設設定。
以上幾步就建立起了一個有主架構結構的應用程式,并使它具有了啟動畫面。這是我們要做的準備工作已經完成。
二、給基于對話框應用程式做啟動畫面
(一)建立基于對話框的應用程式Spla
從File菜單選擇New對話,在Projects頁籤中選擇AppWizard(exe)圖示。鍵入Spla 作為項目名并單擊OK。在第一步中選中Dialog Based單選按鈕,其它接受所有預設選項。
(二)做啟動畫面
這裡做啟動畫面如果仍采用前述用Gallery來插入是不行的,因為基于對話框的應用程式沒有主架構。不過我們可以把上面建立起的啟動畫面檔案移植過來,然後,對程式進行少許程式設計修改就行。請按照下面的步驟來做:
1、将Splash.cpp和Splash.h兩個檔案從Hs工程中拷貝到你的工程中。添加如下代碼到CSplaApp的InitInstance()函數中。
#include "Splash.h"//頭檔案請放在開始位置
BOOL CSplaApp::InitInstance()
{
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
CSplashWnd::EnableSplashScreen
(cmdInfo.m_bShowSplash);
...
}
2、接下來,使用ClassWizard來添加OnCreate函數到你的對話框類中,并且添加如下代碼: #include "Splash.h"//頭檔案請放在開始位置
int CSplaDlg::OnCreate
(LPCREATESTRUCT lpCreateStruct)
{
…
CSplashWnd::ShowSplashScreen(this);
…
}
3、将Splash16.bmp檔案從Hs工程中拷貝到你的工程中 蚩猈orkspace的Resouce項,将Splash16.bmp插入。打開Properties将IDB_BITMAP1改為IDB_SPLASH,這個ID值隻要和程式中一緻起來就行,現在這樣改最簡便。
現在可以編譯運作程式了,程式運作時出現如圖2(略)的啟動畫面。這是預設的畫面,你可以打開圖形編輯器自己加工。如果你要改變啟動畫面的停留時間,就修改SetTime中的第二個參數,這裡是750毫秒。
int CSplashWnd::OnCreate
(LPCREATESTRUCT lpCreateStruct)
{
…
// Set a timer to destroy the splash screen.
SetTimer(1, 750, NULL);
return 0;
}
一、給一文檔/視圖應用程式做啟動畫面
(一) 建立一單文檔/視圖應用程式Hs
從File菜單選擇New對話,在Projects頁籤中選擇AppWizard(exe)圖示。鍵入Hs作為項目名并單擊OK。在第一步中選中Single Document單選按鈕,其它接受所有預設選項。
(二) 添加啟動畫面
當AppWizard完成,而且Visual C++打開項目的時候,從Project菜單中選擇Add To Project,并單擊位于次級菜單上的Comonents and Controls…,選擇Splash screen元件,如圖1(略)所示,單擊Insert。接受所有的預設設定。
以上幾步就建立起了一個有主架構結構的應用程式,并使它具有了啟動畫面。這是我們要做的準備工作已經完成。
二、給基于對話框應用程式做啟動畫面
(一)建立基于對話框的應用程式Spla
從File菜單選擇New對話,在Projects頁籤中選擇AppWizard(exe)圖示。鍵入Spla 作為項目名并單擊OK。在第一步中選中Dialog Based單選按鈕,其它接受所有預設選項。
(二)做啟動畫面
這裡做啟動畫面如果仍采用前述用Gallery來插入是不行的,因為基于對話框的應用程式沒有主架構。不過我們可以把上面建立起的啟動畫面檔案移植過來,然後,對程式進行少許程式設計修改就行。請按照下面的步驟來做:
1、将Splash.cpp和Splash.h兩個檔案從Hs工程中拷貝到你的工程中。添加如下代碼到CSplaApp的InitInstance()函數中。
#include "Splash.h"//頭檔案請放在開始位置
BOOL CSplaApp::InitInstance()
{
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
CSplashWnd::EnableSplashScreen
(cmdInfo.m_bShowSplash);
...
}
2、接下來,使用ClassWizard來添加OnCreate函數到你的對話框類中,并且添加如下代碼: #include "Splash.h"//頭檔案請放在開始位置
int CSplaDlg::OnCreate
(LPCREATESTRUCT lpCreateStruct)
{
…
CSplashWnd::ShowSplashScreen(this);
…
}
3、将Splash16.bmp檔案從Hs工程中拷貝到你的工程中 蚩猈orkspace的Resouce項,将Splash16.bmp插入。打開Properties将IDB_BITMAP1改為IDB_SPLASH,這個ID值隻要和程式中一緻起來就行,現在這樣改最簡便。
現在可以編譯運作程式了,程式運作時出現如圖2(略)的啟動畫面。這是預設的畫面,你可以打開圖形編輯器自己加工。如果你要改變啟動畫面的停留時間,就修改SetTime中的第二個參數,這裡是750毫秒。
int CSplashWnd::OnCreate
(LPCREATESTRUCT lpCreateStruct)
{
…
// Set a timer to destroy the splash screen.
SetTimer(1, 750, NULL);
return 0;
}
VC程式設計實作IE風格的界面
使用過IE浏覽器的朋友都知道IE界面上的扁平工具條、位址欄,扁平工具欄上的按鈕正常狀态下為扁平态,按鈕上的圖像為灰色,當滑鼠放在按鈕上時,按鈕突起(這種狀态稱為搖桿),并且其上的圖像變得鮮豔醒目,一些按鈕上還有漢字說明或标有小黑三角的下拉按鈕,單擊時顯示下拉菜單,這些技術是怎麼實作的呢,本文針對這些問題介紹了如何利用VC程式設計來實作它們。
IE風格的實作主要在主架構類的CMainFrame::OnCreate()實作,它的主要思想如下:首先定義一個CReBar對象,用以作工具條、位址欄的容器,然後分别聲明圖像清單對象img用于存儲工具欄上按鈕的熱點圖像和正常狀态下顯示的圖像。為了顯示扁平工具欄,需要用CreateEx()函數建立CToolBar對象m_wndToolBar,用ModifyStyle()函數将工具欄的風格設為扁平類型,你不能用CToolBar::Create() 或 CToolBar:: SetBarStyle()設定這種新風格。CToolBar 類不支援TBSTYLE_FLAT。要解決這個問題,必須繞過CToolBar類,使用CWnd::ModifyStyle()。工具欄對象調用SetButtonInfo()設定按鈕的風格為TBSTYLE_DROPDOWN,就可以将工具欄按鈕設定為附帶有下拉按鈕。至于按鈕帶有中文提示,用工具欄的SetButtonText()就可以輕松實作了。下面是實作IE風格界面的部分代碼和注釋:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { CReBar m_wndReBar;//聲明CReBar對象 CImageList img;//聲明圖像清單對象 CString str; if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; if (!m_wndReBar.Create(this))//建立CReBar對象 { TRACE0("Failed to create rebar\n"); return -1; // fail to create } if (!m_wndToolBar.CreateEx(this))//建立工具條對象 { TRACE0("Failed to create toolbar\n"); return -1; // fail to create } // set up toolbar properties m_wndToolBar.GetToolBarCtrl().SetButtonWidth(50, 150); file://設定工具條上按鈕的最大、最小尺寸 m_wndToolBar.GetToolBarCtrl().SetExtendedStyle(TBSTYLE_EX_DRAWDDARROWS); file://工具條可以帶有下拉按鈕 img.Create(IDB_HOTTOOLBAR, 22, 0, RGB(255, 0, 255)); file://向圖像清單裝載熱點圖像資源,IDB_HOTTOOLBAR為熱點圖像資源ID m_wndToolBar.GetToolBarCtrl().SetHotImageList(&img);//工具條裝載熱點圖像 img.Detach(); img.Create(IDB_COLDTOOLBAR, 22, 0, RGB(255, 0, 255)); file://圖象清單裝載正常狀态的圖像資源,IDB_COLDTOOLBAR為圖像資源ID m_wndToolBar.GetToolBarCtrl().SetImageList(&img);//将圖像裝入工具條 img.Detach(); m_wndToolBar.ModifyStyle(0, TBSTYLE_FLAT | TBSTYLE_TRANSPARENT); file://工具條為扁平風格 m_wndToolBar.SetButtons(NULL, 9);//工具條上有9個按鈕 // set up each toolbar button file://以下分别對九個按鈕分别設定風格和按鈕漢語提示 m_wndToolBar.SetButtonInfo(0, ID_BUTTON0, TBSTYLE_BUTTON, 0); str.LoadString(IDS_ BUTTON0); m_wndToolBar.SetButtonText(0, str); m_wndToolBar.SetButtonInfo(1, ID_BUTTON1, TBSTYLE_BUTTON, 1); str.LoadString(IDS_ BUTTON1); m_wndToolBar.SetButtonText(1, str); m_wndToolBar.SetButtonInfo(2, ID_BUTTON2, TBSTYLE_BUTTON, 2); str.LoadString(IDS_ BUTTON2); m_wndToolBar.SetButtonText(2, str); m_wndToolBar.SetButtonInfo(3, ID_BUTTON3, TBSTYLE_BUTTON, 3); str.LoadString(IDS_ BUTTON3); m_wndToolBar.SetButtonText(3, str); m_wndToolBar.SetButtonInfo(4, ID_BUTTON4, TBSTYLE_BUTTON, 4); str.LoadString(IDS_ BUTTON4); m_wndToolBar.SetButtonText(4, str); m_wndToolBar.SetButtonInfo(5, ID_BUTTON5, TBSTYLE_BUTTON, 5); str.LoadString(IDS_ BUTTON5); m_wndToolBar.SetButtonText(5, str); m_wndToolBar.SetButtonInfo(6, ID_BUTTON6, TBSTYLE_BUTTON | TBSTYLE_DROPDOWN, 6); str.LoadString(IDS_ BUTTON6); m_wndToolBar.SetButtonText(6, str); m_wndToolBar.SetButtonInfo(7, ID_BUTTON7, TBSTYLE_BUTTON, 7); str.LoadString(IDS_ BUTTON7); m_wndToolBar.SetButtonText(7, str); m_wndToolBar.SetButtonInfo(8,ID_BUTTON8, TBSTYLE_BUTTON | TBSTYLE_DROPDOWN, 8); str.LoadString(IDS_ BUTTON8); m_wndToolBar.SetButtonText(8, str); file://重新調整按鈕的尺寸 CRect rectToolBar; m_wndToolBar.GetItemRect(0, &rectToolBar);//得到工具條第一個按鈕的尺寸 m_wndToolBar.SetSizes(rectToolBar.Size(), CSize(30,20)); file://第一個參數為按鈕尺寸,第二個參數為圖像尺寸 file://建立一個組合框作為位址欄 if (!m_wndAddress.Create(CBS_DROPDOWN | WS_CHILD, CRect(0, 0, 200, 120), this, AFX_IDW_TOOLBAR + 1)) { TRACE0("Failed to create combobox\n"); return -1; // fail to create } file://加入工具欄、位址欄 m_wndReBar.AddBar(&m_wndToolBar); str.LoadString(IDS_ADDRESS); m_wndReBar.AddBar(&m_wndAddress, str, NULL, RBBS_FIXEDBMP | RBBS_BREAK); file://定義REBARBANDINFO對象,對工具條和位址欄設定理想尺寸 REBARBANDINFO rbbi; rbbi.cbSize = sizeof(rbbi); rbbi.fMask = RBBIM_CHILDSIZE | RBBIM_IDEALSIZE | RBBIM_SIZE; rbbi.cxMinChild = rectToolBar.Width(); rbbi.cyMinChild = rectToolBar.Height(); rbbi.cx = rbbi.cxIdeal = rectToolBar.Width() * 9; m_wndReBar.GetReBarCtrl().SetBandInfo(0, &rbbi);//設定工具欄尺寸 rbbi.cxMinChild = 0; CRect rectAddress; rbbi.fMask = RBBIM_CHILDSIZE | RBBIM_IDEALSIZE; m_wndAddress.GetEditCtrl()->GetWindowRect(&rectAddress); rbbi.cyMinChild = rectAddress.Height() + 10; rbbi.cxIdeal = 200; m_wndReBar.GetReBarCtrl().SetBandInfo(2, &rbbi);//設定位址欄尺寸 m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_FIXED); 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 } return 0; } |
以上代碼在Windows2000和Visual C++環境下編譯通過,程式運作正常,有興趣的朋友可以動手親自實驗一下。
VC限制視窗大小又一法
一般說見到的方法,,都是截獲WM_GETMAXMININFO消息。
俺有另一經驗可實作之。
由于一般視窗大小的改變,都是使用者拖動視窗邊框而造成的。是以,我們可以截獲主視窗消息WM_NCHITTEST在其響應函數中判斷CWnd::OnNcHitTest()的傳回值是否為HTRIGHT,HTLEFT,HTTOP,HTBOTTOM四個值之一,如果是,說明使用者此時已點選了四個邊框之一,此時我們應該傳回HTCLIENT.那麼,滑鼠的形狀就不會變成水準或垂直的雙向箭頭,使用者就不可能依靠拖動邊框來改變視窗大小了。
另外,還應補上一個小漏洞,就是還要把系統菜單中的SC_SIZE去掉。
主程式之前的版權視窗
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
DWORD lTime;
try
{
Application->Initialize();
AboutBox=new TAboutBox(AboutBox);
AboutBox->BorderStyle=bsNone;
AboutBox->OKButton->Visible=false;
AboutBox->Height=185;
AboutBox->Show();
AboutBox->Update();
lTime=GetTickCount();
Application->CreateForm(__classid(TMainForm), &MainForm);
while((GetTickCount()-lTime) / 1000 < 3);
AboutBox->Hide();
AboutBox->Free();
Application->Run();
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
return 0;
}
VISUAL C++6.0在MDI主架構視窗中添加位圖
筆者在開發項目時想在MDI程式中添加彩色位圖以美化界面,也實驗了幾種方法,但都有一些小問題,經多方查找資料,終于圓滿的實作了這種功能,現把我的實作方法介紹給大家。
首先要清楚對于一個MDI應用程式的主架構視窗來說包含一個特殊的子視窗稱為MDICLIENT視窗,應用程式的主架構類中有一個成員變量m_hWndMDIClient 指的就是MDICLIENT視窗。MDICLIENT視窗負責管理主架構視窗的客戶區,對MDI客戶視窗程式設計有一定的難度。原因是MDIFrameWnd的客戶區完全被MDICLIENT視窗覆寫掉了。這樣,MDI主視窗類MDIFrameWnd的背景色和光标都不起作用。同時,微軟并不支援将MDICLIENT視窗作為子類,MDICLIENT視窗隻能使用标準的背景色和光标。是以,對MDI客戶視窗程式設計不能象對普通視窗那樣簡單地重載WM_PAINT的消息處理函數。我們可以在主架構視窗截獲關于MDICLIENT視窗的重畫消息,然後加入自己設計的代碼。我用PreTranslateMessage(MSG* pMsg) 截獲MDI客戶視窗WM_PAINT消息,在這個函數中向主架構視窗發送WM_PAINT消息,在該消息的處理函數中實作彩色位圖的顯示。我的具體實作如下:1、向程式添加256色彩色位圖資源,命名為IDB_BITMAP1;2、用ClassWizard向主架構類添加函數CMainFrame::PreTranslateMessage(MSG* pMsg);3、用ClassWizard向主架構類添加函數CMainFrame::OnPaint();現給出兩個函數的實作:
BOOL CMainFrame::PreTranslateMessage(MSG* pMsg) { // TODO: Add your specialized code here and/or call the base class if(pMsg->hwnd==m_hWndMDIClient && pMsg->message==WM_PAINT) PostMessage(WM_PAINT); return CMDIFrameWnd::PreTranslateMessage(pMsg); } void CMainFrame::OnPaint() { CDC dc, memdc; dc.m_hDC=::GetDC(this->m_hWndMDIClient); CRect rect; CBitmap bitmap; BITMAP szbitmap; bitmap.LoadBitmap(IDB_BITMAP1); bitmap.GetObject(sizeof(BITMAP),&szbitmap); CSize size(szbitmap.bmWidth,szbitmap.bmHeight); memdc.CreateCompatibleDC(&dc); CBitmap *oldbitmap=memdc.SelectObject(&bitmap); GetClientRect(&rect); StretchBlt(dc.m_hDC,0,0,rect.Width(),rect.Height(), memdc.m_hDC,0,0,size.cx,size.cy,SRCCOPY); memdc.SelectObject(oldbitmap); memdc.DeleteDC(); dc.DeleteDC(); CMDIFrameWnd::OnPaint(); } |
按上述步驟就可以實作在MDI程式中顯示彩色位圖了,我舉的例子用的是256色位圖,你也可以實作真彩色位圖的顯示,具體方法我就不多說了,有興趣的朋友可以試一試。
華麗的界面
VC6.0實作逆向操作并防止界面閃爍
在系統程式設計中,使用VC是很好的開發工具,而對于一個成熟的系統,幾乎都需要有回退與重做功能(即文檔操作逆向化)以防止使用者誤操作或不合适的操作,進而提高系統的友好性和可操作性。在很多VC技術文章中均提到過這個問題,不過總存在着界面閃爍或不完全可逆.
本文提出一種對系統程式設計可實作完全可逆并防止閃屏的方法.
一、基本原理
要對文檔進行回退重做功能,要做兩方面的工作,一方面要保留删除的文檔(在操作過程中,删除的文檔資料一定能夠保留),另一方面,系統必須能夠記錄進行文檔操作的全過程及每個操作過程的參數。為了保留曆史操作,所有資料非常占用記憶體空間,這就是一些系統隻能進行有限次退步逆向操作的原因。本文提出的方法建立如下存儲機制:建一個臨時檔案儲存資料模拟堆棧,進行一次操作時将相關操作資料入棧.回退一次将相關資料彈出棧,重做一次又依據相關資料重新恢複原有資料.它的好處是在回退和重做時隻入一次棧即申請一次記憶體。
堆棧的資料排放如圖:
// Undo、Redo 資料排放示意圖(m_UndoDataList)
// // ==== // |###| } // |###| } // |###| } ----->> Redo 資料 // |###| } // |###| } // |\\\| } // |\\\| } // |\\\| } // |\\\| } --->> Undo 資料(Undo資料彈出後将轉換為Redo資料) // |\\\| } // |\\\| } // ===== // Undo資料棧 |
二、實作文檔回退重做的引擎
建一文檔逆向化堆棧引擎.主要代碼為:
1.建立臨時檔案.(m_TempPath可以按照某種規則形成路徑)
if(m_File.Open((LPCTSTR)m_TempPath, CFile::modeCreate|CFile::modeReadWrite|CFile::shareExclusive)) { m_File.SeekToBegin(); m_UndoCount = 0; file://目前可重做的步數 m_RedoCount = 0; file://目前可回退的步數 } |
2.儲存回退資料子產品.
// 儲存一個Undo資料塊(由使用者提供) int CRedoUndoEngine::PushData( LPVOID pData, // 由使用者提供的記憶體塊首位址,其中含有使用者定義的待儲存的資料。 // (注:如果函數成功,此記憶體塊将會被本函數釋放,是以,該記憶體塊必須是用::GlobalAlloc()函數配置設定的) DWORD size, // pData指向的記憶體塊尺寸 DWORD param1, // 使用者提供的對該記憶體塊的說明參數,含義由使用者定義 DWORD param2, // 使用者提供的對該記憶體塊的說明參數,含義由使用者定義 int *pIndex // 如果成功,本函數将傳回壓入的Undo塊在棧中的索引值。 如果不需要此傳回值,可用NULL作為參數 ) { // 删除Redo資料 if (m_RedoCount) { while(m_RedoCount--) delete (LPISEEUNDOINFO)m_UndoDataList.RemoveTail(); m_RedoCount = 0; } // 填寫Undo資料的索引資訊(lpISeeUndoInfo為一個儲存資料的結構體) lpISeeUndoInfo->m_index = m_UndoCount; // 索引 lpISeeUndoInfo->m_UserData1 = param1; // 使用者定義的辨別性資料1 lpISeeUndoInfo->m_UserData2 = param2; // 使用者定義的辨別性資料2 lpISeeUndoInfo->m_DataSize = size; // 使用者的Undo資料塊尺寸 lpISeeUndoInfo->m_FilePosition = _get_current_overwrite_pos(); // 加新的Undo資料到Undo棧的尾部 m_UndoDataList.AddTail((void*)lpISeeUndoInfo); // 将使用者的Undo資料寫入臨時檔案 m_File.Seek(lpISeeUndoInfo->m_FilePosition, CFile::begin); m_File.Write((const void *)pData, size); 并使Undo塊計數加1 m_UndoCount++; // 此時使用者傳過來的資料塊已經無用,删除! ::GlobalFree(pData); return 1; } |
3.彈出重做資料子產品.
// 彈出一個Redo資料塊 int CIUndoEngine::RedoData( LPVOID *ppData, // 用于接收本函數傳回的含有最近一個Redo資料的記憶體塊首位址的指針 // (注:此記憶體塊交由調用者釋放,使用::GlobalFree()函數) DWORD *pSize, // ppData記憶體塊的尺寸(in byte) ,如果不需要此資料可用NULL作為參數 DWORD *pParam1, // 傳回使用者對該Redo塊的附加資訊,如果不需要此資料可用NULL作為參數 DWORD *pParam2, // 傳回使用者對該Redo塊的附加資訊,如果不需要此資料可用NULL作為參數 int *pIndex // 傳回本Redo塊的索引,如果不需要此資料可用NULL作為參數 ) { if (!m_RedoCount) return 0; // 鎖定待彈出的Redo索引資訊塊的位址 POSITION pos = m_UndoDataList.FindIndex(m_UndoCount); ASSERT(pos); LPISEEUNDOINFO lpISeeUndoInfo= (LPISEEUNDOINFO)m_UndoDataList.GetAt(pos); ASSERT(lpISeeUndoInfo); ASSERT(lpISeeUndoInfo->m_index == m_UndoCount); if (!(*ppData)) return -1; // 讀出使用者儲存在臨時檔案中的Undo資料(也即Redo資料) m_File.Seek((LONG)lpISeeUndoInfo->m_FilePosition, CFile::begin); m_File.Read(*ppData, lpISeeUndoInfo->m_DataSize); m_UndoCount++; // 可用Undo資料塊個數加1 m_RedoCount--; // 可用Redo資料塊個數減1 if (pSize) *pSize = lpISeeUndoInfo->m_DataSize; if (pParam1) *pParam1= lpISeeUndoInfo->m_UserData1; if (pParam2) *pParam2= lpISeeUndoInfo->m_UserData2; if (pIndex) *pIndex = m_RedoCount;// 注:此處的索引是Redo的索引,而不是Undo的 return 1; } |
由這個文檔逆向化操作引擎,可以獲得目前改動的文檔的資料,并根據改動的資料更新視圖,而不重新整理沒有更改資料的視圖.進而防止了閃爍的産生.
三、簡單開發執行個體
下面以我們開發服裝CAD過程中加入的回退重做功能(文檔逆向化)說明之。
1.定義回退類型
#define REUNDO_MOV 0x0001 file://衣片移動回退重做 #define REUNDO_SEL 0x0002 file://衣片選擇回退重做 ………. |
2.儲存某個操作之前和之後的資料(以衣片移動回退重做為例)
//----------申請記憶體----------------------// int nByte = 4*sizeof(DWORD); HGLOBAL hMem = GlobalAlloc(GMEM_FIXED,nByte); LPVOID pData = (LPVOID) GlobalLock(hMem); file://-----儲存衣片移動前後的位置讀入記憶體------//用移動前後衣片的某個坐标點表示 memcpy((DWORD*)pData, &m_oldPoint, 2*sizeof(DWORD)); memcpy((DWORD*)pData+2,&point, 2*sizeof(DWORD)); file://--------資料入棧---------------------------------------// m_pReUndoEngine->PushData(pData,//衣片m_pReUndoEngine文檔逆向化引擎對象指針 nByte,//儲存資料衣片位元組數 REUNDO_MOV,//回退類型 NULL,NULL); |
3.當回退操作事件觸發時.
//彈出回退值 int nByte = m_pReUndoEngine->GetPopDataSize(); HGLOBAL hMem = GlobalAlloc(GMEM_FIXED,nByte);//申請記憶體 LPVOID pData = (LPVOID) GlobalLock(hMem); DWORD undo_type;DWORD index; m_pReUndoEngine->PopData(&pData,NULL,&undo_type,&index); switch(undo_type){//回退類型 case REUNDO_SEL: SelUndo(pData,index,&dc);break; case REUNDO_MOV: MovUndo(pData);break; ………… } void CMarkView::MovUndo(LPVOID pData) 函數功能 { CPoint pt1,pt2; memcpy(&pt1,(DWORD*)pData,8); memcpy(&pt2,(DWORD*)pData+2,8); …….由pt1 和pt2可以求出位移量,進而恢複原衣片的位置. } |
4.當重做操作事件觸發時
//彈出回退值 int nByte = m_pReUndoEngine->GetRedoDataSize(); HGLOBAL hMem = GlobalAlloc(GMEM_FIXED,nByte);//申請記憶體 LPVOID pData = (LPVOID) GlobalLock(hMem); DWORD undo_type;DWORD index; m_pReUndoEngine->RedoData(&pData,NULL,&undo_type,&index); switch(undo_type){//回退類型 case REUNDO_SEL: SelRedo(pData,index,&dc,nByte);break;
case REUNDO_MOV: MovRedo(pData); break; ………… } |
函數MovRedo(pData)與MovUndo(pData)類似就不多說了.
由3,4可以看出,在回退與重做過程中,隻是儲存和取出操作對象已變化的過程,使程式設計者很容易實作高效率重新整理與充分節約存儲空間.
小結
在系統程式設計中,文檔的回退與重做幾乎是必不可少的,本文提出了一種思路,即對文檔的各種操作分解,并把每種操作下變化的對象的資料值儲存于臨時檔案(棧)中,在回退與重做時根據變化量很容易恢複操作之前狀态或重做, 避免了有些系統(儲存全部文檔資料)占用大量記憶體空間而隻能有限次文檔逆向化,并且全部重新整理而閃爍,破壞了界面的友好性。
怎樣在一個Pane中顯示多種View?
在MS Windows 中,一個視窗可以分割成若幹個子視窗,每一個子視窗稱作一個窗片(pane),每個窗片可以獨立控制,這給界面設計提供了很大的友善。
---- 利用VC 可以很友善地實作分割視窗。分割的方法有兩種:動态和靜态。動态分割時可以根據使用者的需要分割成數目不同的窗片,但所有窗片的屬性和父視窗都是一樣的;而靜态分割的窗片的數目在程式中指定,運作時是固定的,但每個窗片可以有各自不同類型的視(View),是以其使用範圍更為廣泛。本文所讨論的問題僅限于靜态分割。
---- 窗片中視的類型大多是在主視窗的建立過程中指定的。這也就意味着,一個窗片雖然可以顯示任意類型的視,但是這種類型一旦确定,在程式運作過程中就難以改變。
---- 一、我要的是這樣的!
---- 但是我們有時确實需要改變一個窗片所顯示的視的類型,也就是說,需要讓一個窗片顯示多種類型的視。例如一個視窗被分割成兩部分,一邊是指令視窗,另一邊是工作視窗,根據指令視窗中發出的不同指令,需要變換不同的工作類型,這就需要工作視窗中能夠顯示多種類型的視窗,那麼,如何做到這一點呢?
---- 二、你可以這樣做!
---- 從圖1 中可以看到,本程式共有三個視類,分别是:
---- ? 指令視類CCmdView:用來控制右邊窗片中不同視的顯示;
---- ? 選項按鈕視類CRdiView:顯示在右窗片中的選項視類;
---- ? 檢查按鈕視類CChkView:顯示在右窗片中的檢查視類。
---- 這三個視類都是CFormView 的子類。
---- 下面我們來看如何在右窗片内進行兩類視間的切換。實際上,由視A 切換到視B 的原理很簡單,那就是:
---- 1. 從窗片中删除視A;
---- 2. 往窗片中添加視B。
---- 步驟1 的實作非常簡單,僅用一條語句即可:
---- m_wndSplitter.DeleteView(0, 1);
---- 但它是必不可少的,因為你不能讓一個窗片同時包含兩個視。我本來希望往一個窗片中添加新的視時,VC 會自動将原來的視删簦墒撬桓傘?br>
---- 我們來看如何實作步驟2,當一個窗片是空的時候,怎樣往裡面添加一個視呢?其實這樣的功能在程式裡我們已經用過了,看下面的語句:
BOOL CMainFrame::OnCreateClient
(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
……
if (!m_wndSplitter.CreateView(0, 0,
pContext->m_pNe ewClass,
size,
pContext))
……
}
---- 是的,用的就是CSplitterWnd::CreateView(),要注意的是它共有五個參數,其中前兩個用來指定分割視窗的窗片,第三個用來指定視的類型,第四個指定視的大小。最後的一個我們暫時用不上,用空值NULL 就可以了。
---- 這樣我們就可以編寫視切換的代碼了。因為視切換要操縱m_wndSplitter,而它是主視窗的成員,是以切換過程最好設計為主視窗的成員函數。但是切換指令是CCmdView 接受的,因而可以讓CCmdView 接受到視更改消息後,将消息傳給主視窗,由主視窗完成視更改。具體的代碼是這樣的:
---- 指令視類中的消息映射:
BEGIN_MESSAGE_MAP(CCmdView, CFormView)
……
ON_BN_CLICKED(IDC_CHECK, OnSwitchToCheckView)
ON_BN_CLICKED(IDC_RADIO, OnSwitchToRadioView)
……
END_MESSAGE_MAP()
指令視類中的消息響應:
void CCmdView::OnSwitchToCheckView()
{
AfxGetApp()->m_pMainWnd->
SendMessage(WM_COMMAND, ID_CHECK);
}
void CCmdView::OnSwitchToRadioView()
{
AfxGetApp()->m_pMainWnd->
SendMessage(WM_COMMAND, ID_RADIO);
}
主視窗中的消息映射:
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
……
ON_COMMAND(ID_CHECK, OnSwitchToCheckView)
ON_COMMAND(ID_RADIO, OnSwitchToRadioView)
……
END_MESSAGE_MAP()
主視窗中的消息響應:
void CMainFrame::OnSwitchToCheckView()
{
m_wndSplitter.DeleteView(0, 1);
m_wndSplitter.CreateView(0, 1,
RUNTIME_CLASS(CChkView),
CSize(0, 0),
NULL);
m_wndSplitter.RecalcLayout();
}
void CMainFrame::OnSwitchToRadioView()
{
m_wndSplitter.DeleteView(0, 1);
m_wndSplitter.CreateView(0, 1,
RUNTIME_CLASS(CRdiView),
CSize(0, 0),
NULL);
m_wndSplitter.RecalcLayout();
}
---- 好啦,運作一下這個程式,感覺是否不錯?看來大功告成了,可是……
---- 三、還有一個問題
---- 在運作我們辛辛苦苦編出來的程式時,回頭看看VC 的調試視窗,你會發現有很多行這樣的話:
---- Create view without document.
---- 這是說我們建立了視,可是沒有相應的文檔。好在這隻是警告資訊,不是什麼錯誤,如果你不需要相應的文檔,就完全不用去管它。可是,VC 中一種很重要的結構就是文檔- 視結構,利用這種結構,對資料操縱起來非常友善。如果需要建立與視相對應的文檔,應該怎麼辦呢?
---- 這就涉及到VC 中文檔- 視結構的知識,不過不用怕麻煩,與本文有關的就隻有這麼兩點而已:
---- 1. 利用VC 建立的應用程式一般都會管理一些文檔模闆(Document Template),文檔類和視類的對應關系就是在文檔模闆裡描述的。
---- 2. 一個文檔可以有多個視,建立視的時候,需要根據文檔和視的對應關系,給出它所依附的文檔。
---- 怎樣實作上述第一點呢?
---- 首先建立相應的文檔類:CRdiDoc 和CChkDoc。
---- 其次是定義相應的文檔模闆,這是應用類的成員變量。因為在别的類中要使用它們,我們将之定義為公共類型:
class CViewSwitcherApp : public CWinApp
{
……
public:
CSingleDocTemplate* m_pRdiDocTemplate;
CSingleDocTemplate* m_pChkDocTemplate;
……
}
然後建立這兩個文檔模闆,并加入到模闆清單中:
BOOL CViewSwitcherApp::InitInstance()
{
……
m_pRdiDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CRdiDoc),
RUNTIME_CLASS(CMainFrame),
RUNTIME_CLASS(CRdiView));
AddDocTemplate(m_pRdiDocTemplate);
m_pChkDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CChkDoc),
RUNTIME_CLASS(CMainFrame),
RUNTIME_CLASS(CChkView));
AddDocTemplate(m_pChkDocTemplate);
……
}
---- 至于第二點,是在建立視時完成的。還記得建立視的情況麼?當時有一個叫做pCreateContext 的參數,我們将之置為空,這裡就要用到它了。
---- pCreateContext 是一個指向被稱作" 建立上下文"(CreateContext) 結構的指針,這個結構中儲存一些與建立視相關的内容。在建立主視窗時,系統會構造這樣一個結構,并将它作為參數傳遞到與建立視有關的函數中。但現在我們不建立主視窗,是以不得不自己構造這樣一個結構。實際上,該結構中我們所要使用的字段隻有三個:
---- 1. 新視所屬的文檔模闆m_pNewDocTemplate;
---- 2. 新視的類型m_pNewViewClass;
---- 3. 新視所屬的文檔m_pCurrentDoc;
---- 其中僅有第三項需要建立,前兩項都是已知的,隻要指定即可。以切換到選項視為例,修改後的代碼是:
void CMainFrame::OnSwitchToRadioView()
{
m_wndSplitter.DeleteView(0, 1);
CCreateContext createContext;
// 定義并初始化CreateContext
// 擷取新視所屬的文檔模闆
CSingleDocTemplate* pDocTemplate =
((CViewSwitcherApp*)AfxGetApp())-> m_pRdiDocTemplate;
// 建立新文檔并初始化
CDocument* pDoc = pDocTemplate->CreateNewDocument();
pDoc->OnNewDocument();
// 設定CreateContext 相關字段
createContext.m_pNewViewClass = RUNTIME_CLASS(CChkView);
createContext.m_pCurrentDoc = pDoc;
createContext.m_pNewDocTemplate = pDocTemplate;
m_wndSplitter.CreateView(0, 1,
RUNTIME_CLASS(CRdiView),
CSize(0, 0),
&createContext);
m_wndSplitter.RecalcLayout();
}
---- 四、最後的修改
---- 為了使這個程式更符合要求,我們還要做一些與更換視無關的修改。在這個程式中我們一共定義了三種類型的文檔,程式啟動時一般要建立一個文檔開始工作,可是它不知道要選擇哪一種,就彈出一個對話框來詢問。而這是我們不希望看到的。修改的方法是不讓VC 選擇新文檔類型,而我們指定建立哪一種類型的文檔,即把CViewSwitcherApp::CViewSwitcherApp() 中的語句
---- if (!ProcessShellCommand(cmdInfo)) return FALSE;
---- 更改為
---- m_pDocTemplate->OpenDocumentFile(NULL)。
讓基于對話框應用程式也有啟動畫面
用MFC的應用向導建立一個有主架構結構的應用程式,要使它具有啟動畫面是很簡單的(下面會體驗到),而要使一個基于對話框的應用程式也有啟動畫面則要費些事了,不過按以下筆者的方法則也是很容易的,我主要介紹方法,對畫面僅采用預設情況,讀者有興趣可自己加工。
一、給一文檔/視圖應用程式做啟動畫面
(一) 建立一單文檔/視圖應用程式Hs
從File菜單選擇New對話,在Projects頁籤中選擇AppWizard(exe)圖示。鍵入Hs作為項目名并單擊OK。在第一步中選中Single Document單選按鈕,其它接受所有預設選項。
(二) 添加啟動畫面
當AppWizard完成,而且Visual C++打開項目的時候,從Project菜單中選擇Add To Project,并單擊位于次級菜單上的Comonents and Controls…,選擇Splash screen元件,如圖1(略)所示,單擊Insert。接受所有的預設設定。
以上幾步就建立起了一個有主架構結構的應用程式,并使它具有了啟動畫面。這是我們要做的準備工作已經完成。
二、給基于對話框應用程式做啟動畫面
(一)建立基于對話框的應用程式Spla
從File菜單選擇New對話,在Projects頁籤中選擇AppWizard(exe)圖示。鍵入Spla 作為項目名并單擊OK。在第一步中選中Dialog Based單選按鈕,其它接受所有預設選項。
(二)做啟動畫面
這裡做啟動畫面如果仍采用前述用Gallery來插入是不行的,因為基于對話框的應用程式沒有主架構。不過我們可以把上面建立起的啟動畫面檔案移植過來,然後,對程式進行少許程式設計修改就行。請按照下面的步驟來做:
1、将Splash.cpp和Splash.h兩個檔案從Hs工程中拷貝到你的工程中。添加如下代碼到CSplaApp的InitInstance()函數中。
#include "Splash.h"//頭檔案請放在開始位置
BOOL CSplaApp::InitInstance()
{
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
CSplashWnd::EnableSplashScreen
(cmdInfo.m_bShowSplash);
...
}
2、接下來,使用ClassWizard來添加OnCreate函數到你的對話框類中,并且添加如下代碼: #include "Splash.h"//頭檔案請放在開始位置
int CSplaDlg::OnCreate
(LPCREATESTRUCT lpCreateStruct)
{
…
CSplashWnd::ShowSplashScreen(this);
…
}
3、将Splash16.bmp檔案從Hs工程中拷貝到你的工程中 蚩猈orkspace的Resouce項,将Splash16.bmp插入。打開Properties将IDB_BITMAP1改為IDB_SPLASH,這個ID值隻要和程式中一緻起來就行,現在這樣改最簡便。
現在可以編譯運作程式了,程式運作時出現如圖2(略)的啟動畫面。這是預設的畫面,你可以打開圖形編輯器自己加工。如果你要改變啟動畫面的停留時間,就修改SetTime中的第二個參數,這裡是750毫秒。
int CSplashWnd::OnCreate
(LPCREATESTRUCT lpCreateStruct)
{
…
// Set a timer to destroy the splash screen.
SetTimer(1, 750, NULL);
return 0;
}
一、給一文檔/視圖應用程式做啟動畫面
(一) 建立一單文檔/視圖應用程式Hs
從File菜單選擇New對話,在Projects頁籤中選擇AppWizard(exe)圖示。鍵入Hs作為項目名并單擊OK。在第一步中選中Single Document單選按鈕,其它接受所有預設選項。
(二) 添加啟動畫面
當AppWizard完成,而且Visual C++打開項目的時候,從Project菜單中選擇Add To Project,并單擊位于次級菜單上的Comonents and Controls…,選擇Splash screen元件,如圖1(略)所示,單擊Insert。接受所有的預設設定。
以上幾步就建立起了一個有主架構結構的應用程式,并使它具有了啟動畫面。這是我們要做的準備工作已經完成。
二、給基于對話框應用程式做啟動畫面
(一)建立基于對話框的應用程式Spla
從File菜單選擇New對話,在Projects頁籤中選擇AppWizard(exe)圖示。鍵入Spla 作為項目名并單擊OK。在第一步中選中Dialog Based單選按鈕,其它接受所有預設選項。
(二)做啟動畫面
這裡做啟動畫面如果仍采用前述用Gallery來插入是不行的,因為基于對話框的應用程式沒有主架構。不過我們可以把上面建立起的啟動畫面檔案移植過來,然後,對程式進行少許程式設計修改就行。請按照下面的步驟來做:
1、将Splash.cpp和Splash.h兩個檔案從Hs工程中拷貝到你的工程中。添加如下代碼到CSplaApp的InitInstance()函數中。
#include "Splash.h"//頭檔案請放在開始位置
BOOL CSplaApp::InitInstance()
{
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
CSplashWnd::EnableSplashScreen
(cmdInfo.m_bShowSplash);
...
}
2、接下來,使用ClassWizard來添加OnCreate函數到你的對話框類中,并且添加如下代碼: #include "Splash.h"//頭檔案請放在開始位置
int CSplaDlg::OnCreate
(LPCREATESTRUCT lpCreateStruct)
{
…
CSplashWnd::ShowSplashScreen(this);
…
}
3、将Splash16.bmp檔案從Hs工程中拷貝到你的工程中 蚩猈orkspace的Resouce項,将Splash16.bmp插入。打開Properties将IDB_BITMAP1改為IDB_SPLASH,這個ID值隻要和程式中一緻起來就行,現在這樣改最簡便。
現在可以編譯運作程式了,程式運作時出現如圖2(略)的啟動畫面。這是預設的畫面,你可以打開圖形編輯器自己加工。如果你要改變啟動畫面的停留時間,就修改SetTime中的第二個參數,這裡是750毫秒。
int CSplashWnd::OnCreate
(LPCREATESTRUCT lpCreateStruct)
{
…
// Set a timer to destroy the splash screen.
SetTimer(1, 750, NULL);
return 0;
}
仔細檢視了一下WIN32的API,發現其實建立任意形狀的視窗其實也是很簡單的,在VC中簡單步驟如下:
當我們注冊并建立了一個視窗類以後,我們在WM_CREATE消息中做如下處理:
(1)建立一個區域,使用CreatePolyonRgn,該函數建立一個多邊形區域,(也可以使用其他方法如CreateRectRgn建立矩形區域),該函數傳回一個HRGN的句柄;
(2)調用函數SetWindowRgn,即可設定視窗的形狀。
補充說明的是,我們可以制作多個區域,然後用CombineRgn方法将多個區域合并為一個區域。這樣我們就可以制作出更為豐富多采的視窗了。
VC程式設計實作IE風格的界面
使用過IE浏覽器的朋友都知道IE界面上的扁平工具條、位址欄,扁平工具欄上的按鈕正常狀态下為扁平态,按鈕上的圖像為灰色,當滑鼠放在按鈕上時,按鈕突起(這種狀态稱為搖桿),并且其上的圖像變得鮮豔醒目,一些按鈕上還有漢字說明或标有小黑三角的下拉按鈕,單擊時顯示下拉菜單,這些技術是怎麼實作的呢,本文針對這些問題介紹了如何利用VC程式設計來實作它們。
IE風格的實作主要在主架構類的CMainFrame::OnCreate()實作,它的主要思想如下:首先定義一個CReBar對象,用以作工具條、位址欄的容器,然後分别聲明圖像清單對象img用于存儲工具欄上按鈕的熱點圖像和正常狀态下顯示的圖像。為了顯示扁平工具欄,需要用CreateEx()函數建立CToolBar對象m_wndToolBar,用ModifyStyle()函數将工具欄的風格設為扁平類型,你不能用CToolBar::Create() 或 CToolBar:: SetBarStyle()設定這種新風格。CToolBar 類不支援TBSTYLE_FLAT。要解決這個問題,必須繞過CToolBar類,使用CWnd::ModifyStyle()。工具欄對象調用SetButtonInfo()設定按鈕的風格為TBSTYLE_DROPDOWN,就可以将工具欄按鈕設定為附帶有下拉按鈕。至于按鈕帶有中文提示,用工具欄的SetButtonText()就可以輕松實作了。下面是實作IE風格界面的部分代碼和注釋:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { CReBar m_wndReBar;//聲明CReBar對象 CImageList img;//聲明圖像清單對象 CString str; if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; if (!m_wndReBar.Create(this))//建立CReBar對象 { TRACE0("Failed to create rebar\n"); return -1; // fail to create } if (!m_wndToolBar.CreateEx(this))//建立工具條對象 { TRACE0("Failed to create toolbar\n"); return -1; // fail to create } // set up toolbar properties m_wndToolBar.GetToolBarCtrl().SetButtonWidth(50, 150); file://設定工具條上按鈕的最大、最小尺寸 m_wndToolBar.GetToolBarCtrl().SetExtendedStyle(TBSTYLE_EX_DRAWDDARROWS); file://工具條可以帶有下拉按鈕 img.Create(IDB_HOTTOOLBAR, 22, 0, RGB(255, 0, 255)); file://向圖像清單裝載熱點圖像資源,IDB_HOTTOOLBAR為熱點圖像資源ID m_wndToolBar.GetToolBarCtrl().SetHotImageList(&img);//工具條裝載熱點圖像 img.Detach(); img.Create(IDB_COLDTOOLBAR, 22, 0, RGB(255, 0, 255)); file://圖象清單裝載正常狀态的圖像資源,IDB_COLDTOOLBAR為圖像資源ID m_wndToolBar.GetToolBarCtrl().SetImageList(&img);//将圖像裝入工具條 img.Detach(); m_wndToolBar.ModifyStyle(0, TBSTYLE_FLAT | TBSTYLE_TRANSPARENT); file://工具條為扁平風格 m_wndToolBar.SetButtons(NULL, 9);//工具條上有9個按鈕 // set up each toolbar button file://以下分别對九個按鈕分别設定風格和按鈕漢語提示 m_wndToolBar.SetButtonInfo(0, ID_BUTTON0, TBSTYLE_BUTTON, 0); str.LoadString(IDS_ BUTTON0); m_wndToolBar.SetButtonText(0, str); m_wndToolBar.SetButtonInfo(1, ID_BUTTON1, TBSTYLE_BUTTON, 1); str.LoadString(IDS_ BUTTON1); m_wndToolBar.SetButtonText(1, str); m_wndToolBar.SetButtonInfo(2, ID_BUTTON2, TBSTYLE_BUTTON, 2); str.LoadString(IDS_ BUTTON2); m_wndToolBar.SetButtonText(2, str); m_wndToolBar.SetButtonInfo(3, ID_BUTTON3, TBSTYLE_BUTTON, 3); str.LoadString(IDS_ BUTTON3); m_wndToolBar.SetButtonText(3, str); m_wndToolBar.SetButtonInfo(4, ID_BUTTON4, TBSTYLE_BUTTON, 4); str.LoadString(IDS_ BUTTON4); m_wndToolBar.SetButtonText(4, str); m_wndToolBar.SetButtonInfo(5, ID_BUTTON5, TBSTYLE_BUTTON, 5); str.LoadString(IDS_ BUTTON5); m_wndToolBar.SetButtonText(5, str); m_wndToolBar.SetButtonInfo(6, ID_BUTTON6, TBSTYLE_BUTTON | TBSTYLE_DROPDOWN, 6); str.LoadString(IDS_ BUTTON6); m_wndToolBar.SetButtonText(6, str); m_wndToolBar.SetButtonInfo(7, ID_BUTTON7, TBSTYLE_BUTTON, 7); str.LoadString(IDS_ BUTTON7); m_wndToolBar.SetButtonText(7, str); m_wndToolBar.SetButtonInfo(8,ID_BUTTON8, TBSTYLE_BUTTON | TBSTYLE_DROPDOWN, 8); str.LoadString(IDS_ BUTTON8); m_wndToolBar.SetButtonText(8, str); file://重新調整按鈕的尺寸 CRect rectToolBar; m_wndToolBar.GetItemRect(0, &rectToolBar);//得到工具條第一個按鈕的尺寸 m_wndToolBar.SetSizes(rectToolBar.Size(), CSize(30,20)); file://第一個參數為按鈕尺寸,第二個參數為圖像尺寸 file://建立一個組合框作為位址欄 if (!m_wndAddress.Create(CBS_DROPDOWN | WS_CHILD, CRect(0, 0, 200, 120), this, AFX_IDW_TOOLBAR + 1)) { TRACE0("Failed to create combobox\n"); return -1; // fail to create } file://加入工具欄、位址欄 m_wndReBar.AddBar(&m_wndToolBar); str.LoadString(IDS_ADDRESS); m_wndReBar.AddBar(&m_wndAddress, str, NULL, RBBS_FIXEDBMP | RBBS_BREAK); file://定義REBARBANDINFO對象,對工具條和位址欄設定理想尺寸 REBARBANDINFO rbbi; rbbi.cbSize = sizeof(rbbi); rbbi.fMask = RBBIM_CHILDSIZE | RBBIM_IDEALSIZE | RBBIM_SIZE; rbbi.cxMinChild = rectToolBar.Width(); rbbi.cyMinChild = rectToolBar.Height(); rbbi.cx = rbbi.cxIdeal = rectToolBar.Width() * 9; m_wndReBar.GetReBarCtrl().SetBandInfo(0, &rbbi);//設定工具欄尺寸 rbbi.cxMinChild = 0; CRect rectAddress; rbbi.fMask = RBBIM_CHILDSIZE | RBBIM_IDEALSIZE; m_wndAddress.GetEditCtrl()->GetWindowRect(&rectAddress); rbbi.cyMinChild = rectAddress.Height() + 10; rbbi.cxIdeal = 200; m_wndReBar.GetReBarCtrl().SetBandInfo(2, &rbbi);//設定位址欄尺寸 m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_FIXED); 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 } return 0; } |
以上代碼在Windows2000和Visual C++環境下編譯通過,程式運作正常,有興趣的朋友可以動手親自實驗一下。
VC限制視窗大小又一法
一般說見到的方法,,都是截獲WM_GETMAXMININFO消息。
俺有另一經驗可實作之。
由于一般視窗大小的改變,都是使用者拖動視窗邊框而造成的。是以,我們可以截獲主視窗消息WM_NCHITTEST在其響應函數中判斷CWnd::OnNcHitTest()的傳回值是否為HTRIGHT,HTLEFT,HTTOP,HTBOTTOM四個值之一,如果是,說明使用者此時已點選了四個邊框之一,此時我們應該傳回HTCLIENT.那麼,滑鼠的形狀就不會變成水準或垂直的雙向箭頭,使用者就不可能依靠拖動邊框來改變視窗大小了。
另外,還應補上一個小漏洞,就是還要把系統菜單中的SC_SIZE去掉。
主程式之前的版權視窗
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
DWORD lTime;
try
{
Application->Initialize();
AboutBox=new TAboutBox(AboutBox);
AboutBox->BorderStyle=bsNone;
AboutBox->OKButton->Visible=false;
AboutBox->Height=185;
AboutBox->Show();
AboutBox->Update();
lTime=GetTickCount();
Application->CreateForm(__classid(TMainForm), &MainForm);
while((GetTickCount()-lTime) / 1000 < 3);
AboutBox->Hide();
AboutBox->Free();
Application->Run();
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
return 0;
}
VISUAL C++6.0在MDI主架構視窗中添加位圖
筆者在開發項目時想在MDI程式中添加彩色位圖以美化界面,也實驗了幾種方法,但都有一些小問題,經多方查找資料,終于圓滿的實作了這種功能,現把我的實作方法介紹給大家。
首先要清楚對于一個MDI應用程式的主架構視窗來說包含一個特殊的子視窗稱為MDICLIENT視窗,應用程式的主架構類中有一個成員變量m_hWndMDIClient 指的就是MDICLIENT視窗。MDICLIENT視窗負責管理主架構視窗的客戶區,對MDI客戶視窗程式設計有一定的難度。原因是MDIFrameWnd的客戶區完全被MDICLIENT視窗覆寫掉了。這樣,MDI主視窗類MDIFrameWnd的背景色和光标都不起作用。同時,微軟并不支援将MDICLIENT視窗作為子類,MDICLIENT視窗隻能使用标準的背景色和光标。是以,對MDI客戶視窗程式設計不能象對普通視窗那樣簡單地重載WM_PAINT的消息處理函數。我們可以在主架構視窗截獲關于MDICLIENT視窗的重畫消息,然後加入自己設計的代碼。我用PreTranslateMessage(MSG* pMsg) 截獲MDI客戶視窗WM_PAINT消息,在這個函數中向主架構視窗發送WM_PAINT消息,在該消息的處理函數中實作彩色位圖的顯示。我的具體實作如下:1、向程式添加256色彩色位圖資源,命名為IDB_BITMAP1;2、用ClassWizard向主架構類添加函數CMainFrame::PreTranslateMessage(MSG* pMsg);3、用ClassWizard向主架構類添加函數CMainFrame::OnPaint();現給出兩個函數的實作:
BOOL CMainFrame::PreTranslateMessage(MSG* pMsg) { // TODO: Add your specialized code here and/or call the base class if(pMsg->hwnd==m_hWndMDIClient && pMsg->message==WM_PAINT) PostMessage(WM_PAINT); return CMDIFrameWnd::PreTranslateMessage(pMsg); } void CMainFrame::OnPaint() { CDC dc, memdc; dc.m_hDC=::GetDC(this->m_hWndMDIClient); CRect rect; CBitmap bitmap; BITMAP szbitmap; bitmap.LoadBitmap(IDB_BITMAP1); bitmap.GetObject(sizeof(BITMAP),&szbitmap); CSize size(szbitmap.bmWidth,szbitmap.bmHeight); memdc.CreateCompatibleDC(&dc); CBitmap *oldbitmap=memdc.SelectObject(&bitmap); GetClientRect(&rect); StretchBlt(dc.m_hDC,0,0,rect.Width(),rect.Height(), memdc.m_hDC,0,0,size.cx,size.cy,SRCCOPY); memdc.SelectObject(oldbitmap); memdc.DeleteDC(); dc.DeleteDC(); CMDIFrameWnd::OnPaint(); } |
按上述步驟就可以實作在MDI程式中顯示彩色位圖了,我舉的例子用的是256色位圖,你也可以實作真彩色位圖的顯示,具體方法我就不多說了,有興趣的朋友可以試一試。
華麗的界面
VC6.0實作逆向操作并防止界面閃爍
在系統程式設計中,使用VC是很好的開發工具,而對于一個成熟的系統,幾乎都需要有回退與重做功能(即文檔操作逆向化)以防止使用者誤操作或不合适的操作,進而提高系統的友好性和可操作性。在很多VC技術文章中均提到過這個問題,不過總存在着界面閃爍或不完全可逆.
本文提出一種對系統程式設計可實作完全可逆并防止閃屏的方法.
一、基本原理
要對文檔進行回退重做功能,要做兩方面的工作,一方面要保留删除的文檔(在操作過程中,删除的文檔資料一定能夠保留),另一方面,系統必須能夠記錄進行文檔操作的全過程及每個操作過程的參數。為了保留曆史操作,所有資料非常占用記憶體空間,這就是一些系統隻能進行有限次退步逆向操作的原因。本文提出的方法建立如下存儲機制:建一個臨時檔案儲存資料模拟堆棧,進行一次操作時将相關操作資料入棧.回退一次将相關資料彈出棧,重做一次又依據相關資料重新恢複原有資料.它的好處是在回退和重做時隻入一次棧即申請一次記憶體。
堆棧的資料排放如圖:
// Undo、Redo 資料排放示意圖(m_UndoDataList)
// // ==== // |###| } // |###| } // |###| } ----->> Redo 資料 // |###| } // |###| } // |\\\| } // |\\\| } // |\\\| } // |\\\| } --->> Undo 資料(Undo資料彈出後将轉換為Redo資料) // |\\\| } // |\\\| } // ===== // Undo資料棧 |
二、實作文檔回退重做的引擎
建一文檔逆向化堆棧引擎.主要代碼為:
1.建立臨時檔案.(m_TempPath可以按照某種規則形成路徑)
if(m_File.Open((LPCTSTR)m_TempPath, CFile::modeCreate|CFile::modeReadWrite|CFile::shareExclusive)) { m_File.SeekToBegin(); m_UndoCount = 0; file://目前可重做的步數 m_RedoCount = 0; file://目前可回退的步數 } |
2.儲存回退資料子產品.
// 儲存一個Undo資料塊(由使用者提供) int CRedoUndoEngine::PushData( LPVOID pData, // 由使用者提供的記憶體塊首位址,其中含有使用者定義的待儲存的資料。 // (注:如果函數成功,此記憶體塊将會被本函數釋放,是以,該記憶體塊必須是用::GlobalAlloc()函數配置設定的) DWORD size, // pData指向的記憶體塊尺寸 DWORD param1, // 使用者提供的對該記憶體塊的說明參數,含義由使用者定義 DWORD param2, // 使用者提供的對該記憶體塊的說明參數,含義由使用者定義 int *pIndex // 如果成功,本函數将傳回壓入的Undo塊在棧中的索引值。 如果不需要此傳回值,可用NULL作為參數 ) { // 删除Redo資料 if (m_RedoCount) { while(m_RedoCount--) delete (LPISEEUNDOINFO)m_UndoDataList.RemoveTail(); m_RedoCount = 0; } // 填寫Undo資料的索引資訊(lpISeeUndoInfo為一個儲存資料的結構體) lpISeeUndoInfo->m_index = m_UndoCount; // 索引 lpISeeUndoInfo->m_UserData1 = param1; // 使用者定義的辨別性資料1 lpISeeUndoInfo->m_UserData2 = param2; // 使用者定義的辨別性資料2 lpISeeUndoInfo->m_DataSize = size; // 使用者的Undo資料塊尺寸 lpISeeUndoInfo->m_FilePosition = _get_current_overwrite_pos(); // 加新的Undo資料到Undo棧的尾部 m_UndoDataList.AddTail((void*)lpISeeUndoInfo); // 将使用者的Undo資料寫入臨時檔案 m_File.Seek(lpISeeUndoInfo->m_FilePosition, CFile::begin); m_File.Write((const void *)pData, size); 并使Undo塊計數加1 m_UndoCount++; // 此時使用者傳過來的資料塊已經無用,删除! ::GlobalFree(pData); return 1; } |
3.彈出重做資料子產品.
// 彈出一個Redo資料塊 int CIUndoEngine::RedoData( LPVOID *ppData, // 用于接收本函數傳回的含有最近一個Redo資料的記憶體塊首位址的指針 // (注:此記憶體塊交由調用者釋放,使用::GlobalFree()函數) DWORD *pSize, // ppData記憶體塊的尺寸(in byte) ,如果不需要此資料可用NULL作為參數 DWORD *pParam1, // 傳回使用者對該Redo塊的附加資訊,如果不需要此資料可用NULL作為參數 DWORD *pParam2, // 傳回使用者對該Redo塊的附加資訊,如果不需要此資料可用NULL作為參數 int *pIndex // 傳回本Redo塊的索引,如果不需要此資料可用NULL作為參數 ) { if (!m_RedoCount) return 0; // 鎖定待彈出的Redo索引資訊塊的位址 POSITION pos = m_UndoDataList.FindIndex(m_UndoCount); ASSERT(pos); LPISEEUNDOINFO lpISeeUndoInfo= (LPISEEUNDOINFO)m_UndoDataList.GetAt(pos); ASSERT(lpISeeUndoInfo); ASSERT(lpISeeUndoInfo->m_index == m_UndoCount); if (!(*ppData)) return -1; // 讀出使用者儲存在臨時檔案中的Undo資料(也即Redo資料) m_File.Seek((LONG)lpISeeUndoInfo->m_FilePosition, CFile::begin); m_File.Read(*ppData, lpISeeUndoInfo->m_DataSize); m_UndoCount++; // 可用Undo資料塊個數加1 m_RedoCount--; // 可用Redo資料塊個數減1 if (pSize) *pSize = lpISeeUndoInfo->m_DataSize; if (pParam1) *pParam1= lpISeeUndoInfo->m_UserData1; if (pParam2) *pParam2= lpISeeUndoInfo->m_UserData2; if (pIndex) *pIndex = m_RedoCount;// 注:此處的索引是Redo的索引,而不是Undo的 return 1; } |
由這個文檔逆向化操作引擎,可以獲得目前改動的文檔的資料,并根據改動的資料更新視圖,而不重新整理沒有更改資料的視圖.進而防止了閃爍的産生.
三、簡單開發執行個體
下面以我們開發服裝CAD過程中加入的回退重做功能(文檔逆向化)說明之。
1.定義回退類型
#define REUNDO_MOV 0x0001 file://衣片移動回退重做 #define REUNDO_SEL 0x0002 file://衣片選擇回退重做 ………. |
2.儲存某個操作之前和之後的資料(以衣片移動回退重做為例)
//----------申請記憶體----------------------// int nByte = 4*sizeof(DWORD); HGLOBAL hMem = GlobalAlloc(GMEM_FIXED,nByte); LPVOID pData = (LPVOID) GlobalLock(hMem); file://-----儲存衣片移動前後的位置讀入記憶體------//用移動前後衣片的某個坐标點表示 memcpy((DWORD*)pData, &m_oldPoint, 2*sizeof(DWORD)); memcpy((DWORD*)pData+2,&point, 2*sizeof(DWORD)); file://--------資料入棧---------------------------------------// m_pReUndoEngine->PushData(pData,//衣片m_pReUndoEngine文檔逆向化引擎對象指針 nByte,//儲存資料衣片位元組數 REUNDO_MOV,//回退類型 NULL,NULL); |
3.當回退操作事件觸發時.
//彈出回退值 int nByte = m_pReUndoEngine->GetPopDataSize(); HGLOBAL hMem = GlobalAlloc(GMEM_FIXED,nByte);//申請記憶體 LPVOID pData = (LPVOID) GlobalLock(hMem); DWORD undo_type;DWORD index; m_pReUndoEngine->PopData(&pData,NULL,&undo_type,&index); switch(undo_type){//回退類型 case REUNDO_SEL: SelUndo(pData,index,&dc);break; case REUNDO_MOV: MovUndo(pData);break; ………… } void CMarkView::MovUndo(LPVOID pData) 函數功能 { CPoint pt1,pt2; memcpy(&pt1,(DWORD*)pData,8); memcpy(&pt2,(DWORD*)pData+2,8); …….由pt1 和pt2可以求出位移量,進而恢複原衣片的位置. } |
4.當重做操作事件觸發時
//彈出回退值 int nByte = m_pReUndoEngine->GetRedoDataSize(); HGLOBAL hMem = GlobalAlloc(GMEM_FIXED,nByte);//申請記憶體 LPVOID pData = (LPVOID) GlobalLock(hMem); DWORD undo_type;DWORD index; m_pReUndoEngine->RedoData(&pData,NULL,&undo_type,&index); switch(undo_type){//回退類型 case REUNDO_SEL: SelRedo(pData,index,&dc,nByte);break;
case REUNDO_MOV: MovRedo(pData); break; ………… } |
函數MovRedo(pData)與MovUndo(pData)類似就不多說了.
由3,4可以看出,在回退與重做過程中,隻是儲存和取出操作對象已變化的過程,使程式設計者很容易實作高效率重新整理與充分節約存儲空間.
小結
在系統程式設計中,文檔的回退與重做幾乎是必不可少的,本文提出了一種思路,即對文檔的各種操作分解,并把每種操作下變化的對象的資料值儲存于臨時檔案(棧)中,在回退與重做時根據變化量很容易恢複操作之前狀态或重做, 避免了有些系統(儲存全部文檔資料)占用大量記憶體空間而隻能有限次文檔逆向化,并且全部重新整理而閃爍,破壞了界面的友好性。
怎樣在一個Pane中顯示多種View?
在MS Windows 中,一個視窗可以分割成若幹個子視窗,每一個子視窗稱作一個窗片(pane),每個窗片可以獨立控制,這給界面設計提供了很大的友善。
---- 利用VC 可以很友善地實作分割視窗。分割的方法有兩種:動态和靜态。動态分割時可以根據使用者的需要分割成數目不同的窗片,但所有窗片的屬性和父視窗都是一樣的;而靜态分割的窗片的數目在程式中指定,運作時是固定的,但每個窗片可以有各自不同類型的視(View),是以其使用範圍更為廣泛。本文所讨論的問題僅限于靜态分割。
---- 窗片中視的類型大多是在主視窗的建立過程中指定的。這也就意味着,一個窗片雖然可以顯示任意類型的視,但是這種類型一旦确定,在程式運作過程中就難以改變。
---- 一、我要的是這樣的!
---- 但是我們有時确實需要改變一個窗片所顯示的視的類型,也就是說,需要讓一個窗片顯示多種類型的視。例如一個視窗被分割成兩部分,一邊是指令視窗,另一邊是工作視窗,根據指令視窗中發出的不同指令,需要變換不同的工作類型,這就需要工作視窗中能夠顯示多種類型的視窗,那麼,如何做到這一點呢?
---- 二、你可以這樣做!
---- 從圖1 中可以看到,本程式共有三個視類,分别是:
---- ? 指令視類CCmdView:用來控制右邊窗片中不同視的顯示;
---- ? 選項按鈕視類CRdiView:顯示在右窗片中的選項視類;
---- ? 檢查按鈕視類CChkView:顯示在右窗片中的檢查視類。
---- 這三個視類都是CFormView 的子類。
---- 下面我們來看如何在右窗片内進行兩類視間的切換。實際上,由視A 切換到視B 的原理很簡單,那就是:
---- 1. 從窗片中删除視A;
---- 2. 往窗片中添加視B。
---- 步驟1 的實作非常簡單,僅用一條語句即可:
---- m_wndSplitter.DeleteView(0, 1);
---- 但它是必不可少的,因為你不能讓一個窗片同時包含兩個視。我本來希望往一個窗片中添加新的視時,VC 會自動将原來的視删簦墒撬桓傘?br>
---- 我們來看如何實作步驟2,當一個窗片是空的時候,怎樣往裡面添加一個視呢?其實這樣的功能在程式裡我們已經用過了,看下面的語句:
BOOL CMainFrame::OnCreateClient
(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
……
if (!m_wndSplitter.CreateView(0, 0,
pContext->m_pNe ewClass,
size,
pContext))
……
}
---- 是的,用的就是CSplitterWnd::CreateView(),要注意的是它共有五個參數,其中前兩個用來指定分割視窗的窗片,第三個用來指定視的類型,第四個指定視的大小。最後的一個我們暫時用不上,用空值NULL 就可以了。
---- 這樣我們就可以編寫視切換的代碼了。因為視切換要操縱m_wndSplitter,而它是主視窗的成員,是以切換過程最好設計為主視窗的成員函數。但是切換指令是CCmdView 接受的,因而可以讓CCmdView 接受到視更改消息後,将消息傳給主視窗,由主視窗完成視更改。具體的代碼是這樣的:
---- 指令視類中的消息映射:
BEGIN_MESSAGE_MAP(CCmdView, CFormView)
……
ON_BN_CLICKED(IDC_CHECK, OnSwitchToCheckView)
ON_BN_CLICKED(IDC_RADIO, OnSwitchToRadioView)
……
END_MESSAGE_MAP()
指令視類中的消息響應:
void CCmdView::OnSwitchToCheckView()
{
AfxGetApp()->m_pMainWnd->
SendMessage(WM_COMMAND, ID_CHECK);
}
void CCmdView::OnSwitchToRadioView()
{
AfxGetApp()->m_pMainWnd->
SendMessage(WM_COMMAND, ID_RADIO);
}
主視窗中的消息映射:
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
……
ON_COMMAND(ID_CHECK, OnSwitchToCheckView)
ON_COMMAND(ID_RADIO, OnSwitchToRadioView)
……
END_MESSAGE_MAP()
主視窗中的消息響應:
void CMainFrame::OnSwitchToCheckView()
{
m_wndSplitter.DeleteView(0, 1);
m_wndSplitter.CreateView(0, 1,
RUNTIME_CLASS(CChkView),
CSize(0, 0),
NULL);
m_wndSplitter.RecalcLayout();
}
void CMainFrame::OnSwitchToRadioView()
{
m_wndSplitter.DeleteView(0, 1);
m_wndSplitter.CreateView(0, 1,
RUNTIME_CLASS(CRdiView),
CSize(0, 0),
NULL);
m_wndSplitter.RecalcLayout();
}
---- 好啦,運作一下這個程式,感覺是否不錯?看來大功告成了,可是……
---- 三、還有一個問題
---- 在運作我們辛辛苦苦編出來的程式時,回頭看看VC 的調試視窗,你會發現有很多行這樣的話:
---- Create view without document.
---- 這是說我們建立了視,可是沒有相應的文檔。好在這隻是警告資訊,不是什麼錯誤,如果你不需要相應的文檔,就完全不用去管它。可是,VC 中一種很重要的結構就是文檔- 視結構,利用這種結構,對資料操縱起來非常友善。如果需要建立與視相對應的文檔,應該怎麼辦呢?
---- 這就涉及到VC 中文檔- 視結構的知識,不過不用怕麻煩,與本文有關的就隻有這麼兩點而已:
---- 1. 利用VC 建立的應用程式一般都會管理一些文檔模闆(Document Template),文檔類和視類的對應關系就是在文檔模闆裡描述的。
---- 2. 一個文檔可以有多個視,建立視的時候,需要根據文檔和視的對應關系,給出它所依附的文檔。
---- 怎樣實作上述第一點呢?
---- 首先建立相應的文檔類:CRdiDoc 和CChkDoc。
---- 其次是定義相應的文檔模闆,這是應用類的成員變量。因為在别的類中要使用它們,我們将之定義為公共類型:
class CViewSwitcherApp : public CWinApp
{
……
public:
CSingleDocTemplate* m_pRdiDocTemplate;
CSingleDocTemplate* m_pChkDocTemplate;
……
}
然後建立這兩個文檔模闆,并加入到模闆清單中:
BOOL CViewSwitcherApp::InitInstance()
{
……
m_pRdiDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CRdiDoc),
RUNTIME_CLASS(CMainFrame),
RUNTIME_CLASS(CRdiView));
AddDocTemplate(m_pRdiDocTemplate);
m_pChkDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CChkDoc),
RUNTIME_CLASS(CMainFrame),
RUNTIME_CLASS(CChkView));
AddDocTemplate(m_pChkDocTemplate);
……
}
---- 至于第二點,是在建立視時完成的。還記得建立視的情況麼?當時有一個叫做pCreateContext 的參數,我們将之置為空,這裡就要用到它了。
---- pCreateContext 是一個指向被稱作" 建立上下文"(CreateContext) 結構的指針,這個結構中儲存一些與建立視相關的内容。在建立主視窗時,系統會構造這樣一個結構,并将它作為參數傳遞到與建立視有關的函數中。但現在我們不建立主視窗,是以不得不自己構造這樣一個結構。實際上,該結構中我們所要使用的字段隻有三個:
---- 1. 新視所屬的文檔模闆m_pNewDocTemplate;
---- 2. 新視的類型m_pNewViewClass;
---- 3. 新視所屬的文檔m_pCurrentDoc;
---- 其中僅有第三項需要建立,前兩項都是已知的,隻要指定即可。以切換到選項視為例,修改後的代碼是:
void CMainFrame::OnSwitchToRadioView()
{
m_wndSplitter.DeleteView(0, 1);
CCreateContext createContext;
// 定義并初始化CreateContext
// 擷取新視所屬的文檔模闆
CSingleDocTemplate* pDocTemplate =
((CViewSwitcherApp*)AfxGetApp())-> m_pRdiDocTemplate;
// 建立新文檔并初始化
CDocument* pDoc = pDocTemplate->CreateNewDocument();
pDoc->OnNewDocument();
// 設定CreateContext 相關字段
createContext.m_pNewViewClass = RUNTIME_CLASS(CChkView);
createContext.m_pCurrentDoc = pDoc;
createContext.m_pNewDocTemplate = pDocTemplate;
m_wndSplitter.CreateView(0, 1,
RUNTIME_CLASS(CRdiView),
CSize(0, 0),
&createContext);
m_wndSplitter.RecalcLayout();
}
---- 四、最後的修改
---- 為了使這個程式更符合要求,我們還要做一些與更換視無關的修改。在這個程式中我們一共定義了三種類型的文檔,程式啟動時一般要建立一個文檔開始工作,可是它不知道要選擇哪一種,就彈出一個對話框來詢問。而這是我們不希望看到的。修改的方法是不讓VC 選擇新文檔類型,而我們指定建立哪一種類型的文檔,即把CViewSwitcherApp::CViewSwitcherApp() 中的語句
---- if (!ProcessShellCommand(cmdInfo)) return FALSE;
---- 更改為
---- m_pDocTemplate->OpenDocumentFile(NULL)。
讓基于對話框應用程式也有啟動畫面
用MFC的應用向導建立一個有主架構結構的應用程式,要使它具有啟動畫面是很簡單的(下面會體驗到),而要使一個基于對話框的應用程式也有啟動畫面則要費些事了,不過按以下筆者的方法則也是很容易的,我主要介紹方法,對畫面僅采用預設情況,讀者有興趣可自己加工。
一、給一文檔/視圖應用程式做啟動畫面
(一) 建立一單文檔/視圖應用程式Hs
從File菜單選擇New對話,在Projects頁籤中選擇AppWizard(exe)圖示。鍵入Hs作為項目名并單擊OK。在第一步中選中Single Document單選按鈕,其它接受所有預設選項。
(二) 添加啟動畫面
當AppWizard完成,而且Visual C++打開項目的時候,從Project菜單中選擇Add To Project,并單擊位于次級菜單上的Comonents and Controls…,選擇Splash screen元件,如圖1(略)所示,單擊Insert。接受所有的預設設定。
以上幾步就建立起了一個有主架構結構的應用程式,并使它具有了啟動畫面。這是我們要做的準備工作已經完成。
二、給基于對話框應用程式做啟動畫面
(一)建立基于對話框的應用程式Spla
從File菜單選擇New對話,在Projects頁籤中選擇AppWizard(exe)圖示。鍵入Spla 作為項目名并單擊OK。在第一步中選中Dialog Based單選按鈕,其它接受所有預設選項。
(二)做啟動畫面
這裡做啟動畫面如果仍采用前述用Gallery來插入是不行的,因為基于對話框的應用程式沒有主架構。不過我們可以把上面建立起的啟動畫面檔案移植過來,然後,對程式進行少許程式設計修改就行。請按照下面的步驟來做:
1、将Splash.cpp和Splash.h兩個檔案從Hs工程中拷貝到你的工程中。添加如下代碼到CSplaApp的InitInstance()函數中。
#include "Splash.h"//頭檔案請放在開始位置
BOOL CSplaApp::InitInstance()
{
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
CSplashWnd::EnableSplashScreen
(cmdInfo.m_bShowSplash);
...
}
2、接下來,使用ClassWizard來添加OnCreate函數到你的對話框類中,并且添加如下代碼: #include "Splash.h"//頭檔案請放在開始位置
int CSplaDlg::OnCreate
(LPCREATESTRUCT lpCreateStruct)
{
…
CSplashWnd::ShowSplashScreen(this);
…
}
3、将Splash16.bmp檔案從Hs工程中拷貝到你的工程中 蚩猈orkspace的Resouce項,将Splash16.bmp插入。打開Properties将IDB_BITMAP1改為IDB_SPLASH,這個ID值隻要和程式中一緻起來就行,現在這樣改最簡便。
現在可以編譯運作程式了,程式運作時出現如圖2(略)的啟動畫面。這是預設的畫面,你可以打開圖形編輯器自己加工。如果你要改變啟動畫面的停留時間,就修改SetTime中的第二個參數,這裡是750毫秒。
int CSplashWnd::OnCreate
(LPCREATESTRUCT lpCreateStruct)
{
…
// Set a timer to destroy the splash screen.
SetTimer(1, 750, NULL);
return 0;
}
一、給一文檔/視圖應用程式做啟動畫面
(一) 建立一單文檔/視圖應用程式Hs
從File菜單選擇New對話,在Projects頁籤中選擇AppWizard(exe)圖示。鍵入Hs作為項目名并單擊OK。在第一步中選中Single Document單選按鈕,其它接受所有預設選項。
(二) 添加啟動畫面
當AppWizard完成,而且Visual C++打開項目的時候,從Project菜單中選擇Add To Project,并單擊位于次級菜單上的Comonents and Controls…,選擇Splash screen元件,如圖1(略)所示,單擊Insert。接受所有的預設設定。
以上幾步就建立起了一個有主架構結構的應用程式,并使它具有了啟動畫面。這是我們要做的準備工作已經完成。
二、給基于對話框應用程式做啟動畫面
(一)建立基于對話框的應用程式Spla
從File菜單選擇New對話,在Projects頁籤中選擇AppWizard(exe)圖示。鍵入Spla 作為項目名并單擊OK。在第一步中選中Dialog Based單選按鈕,其它接受所有預設選項。
(二)做啟動畫面
這裡做啟動畫面如果仍采用前述用Gallery來插入是不行的,因為基于對話框的應用程式沒有主架構。不過我們可以把上面建立起的啟動畫面檔案移植過來,然後,對程式進行少許程式設計修改就行。請按照下面的步驟來做:
1、将Splash.cpp和Splash.h兩個檔案從Hs工程中拷貝到你的工程中。添加如下代碼到CSplaApp的InitInstance()函數中。
#include "Splash.h"//頭檔案請放在開始位置
BOOL CSplaApp::InitInstance()
{
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
CSplashWnd::EnableSplashScreen
(cmdInfo.m_bShowSplash);
...
}
2、接下來,使用ClassWizard來添加OnCreate函數到你的對話框類中,并且添加如下代碼: #include "Splash.h"//頭檔案請放在開始位置
int CSplaDlg::OnCreate
(LPCREATESTRUCT lpCreateStruct)
{
…
CSplashWnd::ShowSplashScreen(this);
…
}
3、将Splash16.bmp檔案從Hs工程中拷貝到你的工程中 蚩猈orkspace的Resouce項,将Splash16.bmp插入。打開Properties将IDB_BITMAP1改為IDB_SPLASH,這個ID值隻要和程式中一緻起來就行,現在這樣改最簡便。
現在可以編譯運作程式了,程式運作時出現如圖2(略)的啟動畫面。這是預設的畫面,你可以打開圖形編輯器自己加工。如果你要改變啟動畫面的停留時間,就修改SetTime中的第二個參數,這裡是750毫秒。
int CSplashWnd::OnCreate
(LPCREATESTRUCT lpCreateStruct)
{
…
// Set a timer to destroy the splash screen.
SetTimer(1, 750, NULL);
return 0;
}
俺有另一經驗可實作之。
由于一般視窗大小的改變,都是使用者拖動視窗邊框而造成的。是以,我們可以截獲主視窗消息WM_NCHITTEST在其響應函數中判斷CWnd::OnNcHitTest()的傳回值是否為HTRIGHT,HTLEFT,HTTOP,HTBOTTOM四個值之一,如果是,說明使用者此時已點選了四個邊框之一,此時我們應該傳回HTCLIENT.那麼,滑鼠的形狀就不會變成水準或垂直的雙向箭頭,使用者就不可能依靠拖動邊框來改變視窗大小了。
另外,還應補上一個小漏洞,就是還要把系統菜單中的SC_SIZE去掉。
主程式之前的版權視窗
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
DWORD lTime;
try
{
Application->Initialize();
AboutBox=new TAboutBox(AboutBox);
AboutBox->BorderStyle=bsNone;
AboutBox->OKButton->Visible=false;
AboutBox->Height=185;
AboutBox->Show();
AboutBox->Update();
lTime=GetTickCount();
Application->CreateForm(__classid(TMainForm), &MainForm);
while((GetTickCount()-lTime) / 1000 < 3);
AboutBox->Hide();
AboutBox->Free();
Application->Run();
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
return 0;
}
VISUAL C++6.0在MDI主架構視窗中添加位圖
筆者在開發項目時想在MDI程式中添加彩色位圖以美化界面,也實驗了幾種方法,但都有一些小問題,經多方查找資料,終于圓滿的實作了這種功能,現把我的實作方法介紹給大家。
首先要清楚對于一個MDI應用程式的主架構視窗來說包含一個特殊的子視窗稱為MDICLIENT視窗,應用程式的主架構類中有一個成員變量m_hWndMDIClient 指的就是MDICLIENT視窗。MDICLIENT視窗負責管理主架構視窗的客戶區,對MDI客戶視窗程式設計有一定的難度。原因是MDIFrameWnd的客戶區完全被MDICLIENT視窗覆寫掉了。這樣,MDI主視窗類MDIFrameWnd的背景色和光标都不起作用。同時,微軟并不支援将MDICLIENT視窗作為子類,MDICLIENT視窗隻能使用标準的背景色和光标。是以,對MDI客戶視窗程式設計不能象對普通視窗那樣簡單地重載WM_PAINT的消息處理函數。我們可以在主架構視窗截獲關于MDICLIENT視窗的重畫消息,然後加入自己設計的代碼。我用PreTranslateMessage(MSG* pMsg) 截獲MDI客戶視窗WM_PAINT消息,在這個函數中向主架構視窗發送WM_PAINT消息,在該消息的處理函數中實作彩色位圖的顯示。我的具體實作如下:1、向程式添加256色彩色位圖資源,命名為IDB_BITMAP1;2、用ClassWizard向主架構類添加函數CMainFrame::PreTranslateMessage(MSG* pMsg);3、用ClassWizard向主架構類添加函數CMainFrame::OnPaint();現給出兩個函數的實作:
BOOL CMainFrame::PreTranslateMessage(MSG* pMsg) { // TODO: Add your specialized code here and/or call the base class if(pMsg->hwnd==m_hWndMDIClient && pMsg->message==WM_PAINT) PostMessage(WM_PAINT); return CMDIFrameWnd::PreTranslateMessage(pMsg); } void CMainFrame::OnPaint() { CDC dc, memdc; dc.m_hDC=::GetDC(this->m_hWndMDIClient); CRect rect; CBitmap bitmap; BITMAP szbitmap; bitmap.LoadBitmap(IDB_BITMAP1); bitmap.GetObject(sizeof(BITMAP),&szbitmap); CSize size(szbitmap.bmWidth,szbitmap.bmHeight); memdc.CreateCompatibleDC(&dc); CBitmap *oldbitmap=memdc.SelectObject(&bitmap); GetClientRect(&rect); StretchBlt(dc.m_hDC,0,0,rect.Width(),rect.Height(), memdc.m_hDC,0,0,size.cx,size.cy,SRCCOPY); memdc.SelectObject(oldbitmap); memdc.DeleteDC(); dc.DeleteDC(); CMDIFrameWnd::OnPaint(); } |
按上述步驟就可以實作在MDI程式中顯示彩色位圖了,我舉的例子用的是256色位圖,你也可以實作真彩色位圖的顯示,具體方法我就不多說了,有興趣的朋友可以試一試。
華麗的界面
VC6.0實作逆向操作并防止界面閃爍
在系統程式設計中,使用VC是很好的開發工具,而對于一個成熟的系統,幾乎都需要有回退與重做功能(即文檔操作逆向化)以防止使用者誤操作或不合适的操作,進而提高系統的友好性和可操作性。在很多VC技術文章中均提到過這個問題,不過總存在着界面閃爍或不完全可逆.
本文提出一種對系統程式設計可實作完全可逆并防止閃屏的方法.
一、基本原理
要對文檔進行回退重做功能,要做兩方面的工作,一方面要保留删除的文檔(在操作過程中,删除的文檔資料一定能夠保留),另一方面,系統必須能夠記錄進行文檔操作的全過程及每個操作過程的參數。為了保留曆史操作,所有資料非常占用記憶體空間,這就是一些系統隻能進行有限次退步逆向操作的原因。本文提出的方法建立如下存儲機制:建一個臨時檔案儲存資料模拟堆棧,進行一次操作時将相關操作資料入棧.回退一次将相關資料彈出棧,重做一次又依據相關資料重新恢複原有資料.它的好處是在回退和重做時隻入一次棧即申請一次記憶體。
堆棧的資料排放如圖:
// Undo、Redo 資料排放示意圖(m_UndoDataList)
// // ==== // |###| } // |###| } // |###| } ----->> Redo 資料 // |###| } // |###| } // |\\\| } // |\\\| } // |\\\| } // |\\\| } --->> Undo 資料(Undo資料彈出後将轉換為Redo資料) // |\\\| } // |\\\| } // ===== // Undo資料棧 |
二、實作文檔回退重做的引擎
建一文檔逆向化堆棧引擎.主要代碼為:
1.建立臨時檔案.(m_TempPath可以按照某種規則形成路徑)
if(m_File.Open((LPCTSTR)m_TempPath, CFile::modeCreate|CFile::modeReadWrite|CFile::shareExclusive)) { m_File.SeekToBegin(); m_UndoCount = 0; file://目前可重做的步數 m_RedoCount = 0; file://目前可回退的步數 } |
2.儲存回退資料子產品.
// 儲存一個Undo資料塊(由使用者提供) int CRedoUndoEngine::PushData( LPVOID pData, // 由使用者提供的記憶體塊首位址,其中含有使用者定義的待儲存的資料。 // (注:如果函數成功,此記憶體塊将會被本函數釋放,是以,該記憶體塊必須是用::GlobalAlloc()函數配置設定的) DWORD size, // pData指向的記憶體塊尺寸 DWORD param1, // 使用者提供的對該記憶體塊的說明參數,含義由使用者定義 DWORD param2, // 使用者提供的對該記憶體塊的說明參數,含義由使用者定義 int *pIndex // 如果成功,本函數将傳回壓入的Undo塊在棧中的索引值。 如果不需要此傳回值,可用NULL作為參數 ) { // 删除Redo資料 if (m_RedoCount) { while(m_RedoCount--) delete (LPISEEUNDOINFO)m_UndoDataList.RemoveTail(); m_RedoCount = 0; } // 填寫Undo資料的索引資訊(lpISeeUndoInfo為一個儲存資料的結構體) lpISeeUndoInfo->m_index = m_UndoCount; // 索引 lpISeeUndoInfo->m_UserData1 = param1; // 使用者定義的辨別性資料1 lpISeeUndoInfo->m_UserData2 = param2; // 使用者定義的辨別性資料2 lpISeeUndoInfo->m_DataSize = size; // 使用者的Undo資料塊尺寸 lpISeeUndoInfo->m_FilePosition = _get_current_overwrite_pos(); // 加新的Undo資料到Undo棧的尾部 m_UndoDataList.AddTail((void*)lpISeeUndoInfo); // 将使用者的Undo資料寫入臨時檔案 m_File.Seek(lpISeeUndoInfo->m_FilePosition, CFile::begin); m_File.Write((const void *)pData, size); 并使Undo塊計數加1 m_UndoCount++; // 此時使用者傳過來的資料塊已經無用,删除! ::GlobalFree(pData); return 1; } |
3.彈出重做資料子產品.
// 彈出一個Redo資料塊 int CIUndoEngine::RedoData( LPVOID *ppData, // 用于接收本函數傳回的含有最近一個Redo資料的記憶體塊首位址的指針 // (注:此記憶體塊交由調用者釋放,使用::GlobalFree()函數) DWORD *pSize, // ppData記憶體塊的尺寸(in byte) ,如果不需要此資料可用NULL作為參數 DWORD *pParam1, // 傳回使用者對該Redo塊的附加資訊,如果不需要此資料可用NULL作為參數 DWORD *pParam2, // 傳回使用者對該Redo塊的附加資訊,如果不需要此資料可用NULL作為參數 int *pIndex // 傳回本Redo塊的索引,如果不需要此資料可用NULL作為參數 ) { if (!m_RedoCount) return 0; // 鎖定待彈出的Redo索引資訊塊的位址 POSITION pos = m_UndoDataList.FindIndex(m_UndoCount); ASSERT(pos); LPISEEUNDOINFO lpISeeUndoInfo= (LPISEEUNDOINFO)m_UndoDataList.GetAt(pos); ASSERT(lpISeeUndoInfo); ASSERT(lpISeeUndoInfo->m_index == m_UndoCount); if (!(*ppData)) return -1; // 讀出使用者儲存在臨時檔案中的Undo資料(也即Redo資料) m_File.Seek((LONG)lpISeeUndoInfo->m_FilePosition, CFile::begin); m_File.Read(*ppData, lpISeeUndoInfo->m_DataSize); m_UndoCount++; // 可用Undo資料塊個數加1 m_RedoCount--; // 可用Redo資料塊個數減1 if (pSize) *pSize = lpISeeUndoInfo->m_DataSize; if (pParam1) *pParam1= lpISeeUndoInfo->m_UserData1; if (pParam2) *pParam2= lpISeeUndoInfo->m_UserData2; if (pIndex) *pIndex = m_RedoCount;// 注:此處的索引是Redo的索引,而不是Undo的 return 1; } |
由這個文檔逆向化操作引擎,可以獲得目前改動的文檔的資料,并根據改動的資料更新視圖,而不重新整理沒有更改資料的視圖.進而防止了閃爍的産生.
三、簡單開發執行個體
下面以我們開發服裝CAD過程中加入的回退重做功能(文檔逆向化)說明之。
1.定義回退類型
#define REUNDO_MOV 0x0001 file://衣片移動回退重做 #define REUNDO_SEL 0x0002 file://衣片選擇回退重做 ………. |
2.儲存某個操作之前和之後的資料(以衣片移動回退重做為例)
//----------申請記憶體----------------------// int nByte = 4*sizeof(DWORD); HGLOBAL hMem = GlobalAlloc(GMEM_FIXED,nByte); LPVOID pData = (LPVOID) GlobalLock(hMem); file://-----儲存衣片移動前後的位置讀入記憶體------//用移動前後衣片的某個坐标點表示 memcpy((DWORD*)pData, &m_oldPoint, 2*sizeof(DWORD)); memcpy((DWORD*)pData+2,&point, 2*sizeof(DWORD)); file://--------資料入棧---------------------------------------// m_pReUndoEngine->PushData(pData,//衣片m_pReUndoEngine文檔逆向化引擎對象指針 nByte,//儲存資料衣片位元組數 REUNDO_MOV,//回退類型 NULL,NULL); |
3.當回退操作事件觸發時.
//彈出回退值 int nByte = m_pReUndoEngine->GetPopDataSize(); HGLOBAL hMem = GlobalAlloc(GMEM_FIXED,nByte);//申請記憶體 LPVOID pData = (LPVOID) GlobalLock(hMem); DWORD undo_type;DWORD index; m_pReUndoEngine->PopData(&pData,NULL,&undo_type,&index); switch(undo_type){//回退類型 case REUNDO_SEL: SelUndo(pData,index,&dc);break; case REUNDO_MOV: MovUndo(pData);break; ………… } void CMarkView::MovUndo(LPVOID pData) 函數功能 { CPoint pt1,pt2; memcpy(&pt1,(DWORD*)pData,8); memcpy(&pt2,(DWORD*)pData+2,8); …….由pt1 和pt2可以求出位移量,進而恢複原衣片的位置. } |
4.當重做操作事件觸發時
//彈出回退值 int nByte = m_pReUndoEngine->GetRedoDataSize(); HGLOBAL hMem = GlobalAlloc(GMEM_FIXED,nByte);//申請記憶體 LPVOID pData = (LPVOID) GlobalLock(hMem); DWORD undo_type;DWORD index; m_pReUndoEngine->RedoData(&pData,NULL,&undo_type,&index); switch(undo_type){//回退類型 case REUNDO_SEL: SelRedo(pData,index,&dc,nByte);break;
case REUNDO_MOV: MovRedo(pData); break; ………… } |
函數MovRedo(pData)與MovUndo(pData)類似就不多說了.
由3,4可以看出,在回退與重做過程中,隻是儲存和取出操作對象已變化的過程,使程式設計者很容易實作高效率重新整理與充分節約存儲空間.
小結
在系統程式設計中,文檔的回退與重做幾乎是必不可少的,本文提出了一種思路,即對文檔的各種操作分解,并把每種操作下變化的對象的資料值儲存于臨時檔案(棧)中,在回退與重做時根據變化量很容易恢複操作之前狀态或重做, 避免了有些系統(儲存全部文檔資料)占用大量記憶體空間而隻能有限次文檔逆向化,并且全部重新整理而閃爍,破壞了界面的友好性。
怎樣在一個Pane中顯示多種View?
在MS Windows 中,一個視窗可以分割成若幹個子視窗,每一個子視窗稱作一個窗片(pane),每個窗片可以獨立控制,這給界面設計提供了很大的友善。
---- 利用VC 可以很友善地實作分割視窗。分割的方法有兩種:動态和靜态。動态分割時可以根據使用者的需要分割成數目不同的窗片,但所有窗片的屬性和父視窗都是一樣的;而靜态分割的窗片的數目在程式中指定,運作時是固定的,但每個窗片可以有各自不同類型的視(View),是以其使用範圍更為廣泛。本文所讨論的問題僅限于靜态分割。
---- 窗片中視的類型大多是在主視窗的建立過程中指定的。這也就意味着,一個窗片雖然可以顯示任意類型的視,但是這種類型一旦确定,在程式運作過程中就難以改變。
---- 一、我要的是這樣的!
---- 但是我們有時确實需要改變一個窗片所顯示的視的類型,也就是說,需要讓一個窗片顯示多種類型的視。例如一個視窗被分割成兩部分,一邊是指令視窗,另一邊是工作視窗,根據指令視窗中發出的不同指令,需要變換不同的工作類型,這就需要工作視窗中能夠顯示多種類型的視窗,那麼,如何做到這一點呢?
---- 二、你可以這樣做!
---- 從圖1 中可以看到,本程式共有三個視類,分别是:
---- ? 指令視類CCmdView:用來控制右邊窗片中不同視的顯示;
---- ? 選項按鈕視類CRdiView:顯示在右窗片中的選項視類;
---- ? 檢查按鈕視類CChkView:顯示在右窗片中的檢查視類。
---- 這三個視類都是CFormView 的子類。
---- 下面我們來看如何在右窗片内進行兩類視間的切換。實際上,由視A 切換到視B 的原理很簡單,那就是:
---- 1. 從窗片中删除視A;
---- 2. 往窗片中添加視B。
---- 步驟1 的實作非常簡單,僅用一條語句即可:
---- m_wndSplitter.DeleteView(0, 1);
---- 但它是必不可少的,因為你不能讓一個窗片同時包含兩個視。我本來希望往一個窗片中添加新的視時,VC 會自動将原來的視删簦墒撬桓傘?br>
---- 我們來看如何實作步驟2,當一個窗片是空的時候,怎樣往裡面添加一個視呢?其實這樣的功能在程式裡我們已經用過了,看下面的語句:
BOOL CMainFrame::OnCreateClient
(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
……
if (!m_wndSplitter.CreateView(0, 0,
pContext->m_pNe ewClass,
size,
pContext))
……
}
---- 是的,用的就是CSplitterWnd::CreateView(),要注意的是它共有五個參數,其中前兩個用來指定分割視窗的窗片,第三個用來指定視的類型,第四個指定視的大小。最後的一個我們暫時用不上,用空值NULL 就可以了。
---- 這樣我們就可以編寫視切換的代碼了。因為視切換要操縱m_wndSplitter,而它是主視窗的成員,是以切換過程最好設計為主視窗的成員函數。但是切換指令是CCmdView 接受的,因而可以讓CCmdView 接受到視更改消息後,将消息傳給主視窗,由主視窗完成視更改。具體的代碼是這樣的:
---- 指令視類中的消息映射:
BEGIN_MESSAGE_MAP(CCmdView, CFormView)
……
ON_BN_CLICKED(IDC_CHECK, OnSwitchToCheckView)
ON_BN_CLICKED(IDC_RADIO, OnSwitchToRadioView)
……
END_MESSAGE_MAP()
指令視類中的消息響應:
void CCmdView::OnSwitchToCheckView()
{
AfxGetApp()->m_pMainWnd->
SendMessage(WM_COMMAND, ID_CHECK);
}
void CCmdView::OnSwitchToRadioView()
{
AfxGetApp()->m_pMainWnd->
SendMessage(WM_COMMAND, ID_RADIO);
}
主視窗中的消息映射:
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
……
ON_COMMAND(ID_CHECK, OnSwitchToCheckView)
ON_COMMAND(ID_RADIO, OnSwitchToRadioView)
……
END_MESSAGE_MAP()
主視窗中的消息響應:
void CMainFrame::OnSwitchToCheckView()
{
m_wndSplitter.DeleteView(0, 1);
m_wndSplitter.CreateView(0, 1,
RUNTIME_CLASS(CChkView),
CSize(0, 0),
NULL);
m_wndSplitter.RecalcLayout();
}
void CMainFrame::OnSwitchToRadioView()
{
m_wndSplitter.DeleteView(0, 1);
m_wndSplitter.CreateView(0, 1,
RUNTIME_CLASS(CRdiView),
CSize(0, 0),
NULL);
m_wndSplitter.RecalcLayout();
}
---- 好啦,運作一下這個程式,感覺是否不錯?看來大功告成了,可是……
---- 三、還有一個問題
---- 在運作我們辛辛苦苦編出來的程式時,回頭看看VC 的調試視窗,你會發現有很多行這樣的話:
---- Create view without document.
---- 這是說我們建立了視,可是沒有相應的文檔。好在這隻是警告資訊,不是什麼錯誤,如果你不需要相應的文檔,就完全不用去管它。可是,VC 中一種很重要的結構就是文檔- 視結構,利用這種結構,對資料操縱起來非常友善。如果需要建立與視相對應的文檔,應該怎麼辦呢?
---- 這就涉及到VC 中文檔- 視結構的知識,不過不用怕麻煩,與本文有關的就隻有這麼兩點而已:
---- 1. 利用VC 建立的應用程式一般都會管理一些文檔模闆(Document Template),文檔類和視類的對應關系就是在文檔模闆裡描述的。
---- 2. 一個文檔可以有多個視,建立視的時候,需要根據文檔和視的對應關系,給出它所依附的文檔。
---- 怎樣實作上述第一點呢?
---- 首先建立相應的文檔類:CRdiDoc 和CChkDoc。
---- 其次是定義相應的文檔模闆,這是應用類的成員變量。因為在别的類中要使用它們,我們将之定義為公共類型:
class CViewSwitcherApp : public CWinApp
{
……
public:
CSingleDocTemplate* m_pRdiDocTemplate;
CSingleDocTemplate* m_pChkDocTemplate;
……
}
然後建立這兩個文檔模闆,并加入到模闆清單中:
BOOL CViewSwitcherApp::InitInstance()
{
……
m_pRdiDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CRdiDoc),
RUNTIME_CLASS(CMainFrame),
RUNTIME_CLASS(CRdiView));
AddDocTemplate(m_pRdiDocTemplate);
m_pChkDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CChkDoc),
RUNTIME_CLASS(CMainFrame),
RUNTIME_CLASS(CChkView));
AddDocTemplate(m_pChkDocTemplate);
……
}
---- 至于第二點,是在建立視時完成的。還記得建立視的情況麼?當時有一個叫做pCreateContext 的參數,我們将之置為空,這裡就要用到它了。
---- pCreateContext 是一個指向被稱作" 建立上下文"(CreateContext) 結構的指針,這個結構中儲存一些與建立視相關的内容。在建立主視窗時,系統會構造這樣一個結構,并将它作為參數傳遞到與建立視有關的函數中。但現在我們不建立主視窗,是以不得不自己構造這樣一個結構。實際上,該結構中我們所要使用的字段隻有三個:
---- 1. 新視所屬的文檔模闆m_pNewDocTemplate;
---- 2. 新視的類型m_pNewViewClass;
---- 3. 新視所屬的文檔m_pCurrentDoc;
---- 其中僅有第三項需要建立,前兩項都是已知的,隻要指定即可。以切換到選項視為例,修改後的代碼是:
void CMainFrame::OnSwitchToRadioView()
{
m_wndSplitter.DeleteView(0, 1);
CCreateContext createContext;
// 定義并初始化CreateContext
// 擷取新視所屬的文檔模闆
CSingleDocTemplate* pDocTemplate =
((CViewSwitcherApp*)AfxGetApp())-> m_pRdiDocTemplate;
// 建立新文檔并初始化
CDocument* pDoc = pDocTemplate->CreateNewDocument();
pDoc->OnNewDocument();
// 設定CreateContext 相關字段
createContext.m_pNewViewClass = RUNTIME_CLASS(CChkView);
createContext.m_pCurrentDoc = pDoc;
createContext.m_pNewDocTemplate = pDocTemplate;
m_wndSplitter.CreateView(0, 1,
RUNTIME_CLASS(CRdiView),
CSize(0, 0),
&createContext);
m_wndSplitter.RecalcLayout();
}
---- 四、最後的修改
---- 為了使這個程式更符合要求,我們還要做一些與更換視無關的修改。在這個程式中我們一共定義了三種類型的文檔,程式啟動時一般要建立一個文檔開始工作,可是它不知道要選擇哪一種,就彈出一個對話框來詢問。而這是我們不希望看到的。修改的方法是不讓VC 選擇新文檔類型,而我們指定建立哪一種類型的文檔,即把CViewSwitcherApp::CViewSwitcherApp() 中的語句
---- if (!ProcessShellCommand(cmdInfo)) return FALSE;
---- 更改為
---- m_pDocTemplate->OpenDocumentFile(NULL)。
讓基于對話框應用程式也有啟動畫面
用MFC的應用向導建立一個有主架構結構的應用程式,要使它具有啟動畫面是很簡單的(下面會體驗到),而要使一個基于對話框的應用程式也有啟動畫面則要費些事了,不過按以下筆者的方法則也是很容易的,我主要介紹方法,對畫面僅采用預設情況,讀者有興趣可自己加工。
一、給一文檔/視圖應用程式做啟動畫面
(一) 建立一單文檔/視圖應用程式Hs
從File菜單選擇New對話,在Projects頁籤中選擇AppWizard(exe)圖示。鍵入Hs作為項目名并單擊OK。在第一步中選中Single Document單選按鈕,其它接受所有預設選項。
(二) 添加啟動畫面
當AppWizard完成,而且Visual C++打開項目的時候,從Project菜單中選擇Add To Project,并單擊位于次級菜單上的Comonents and Controls…,選擇Splash screen元件,如圖1(略)所示,單擊Insert。接受所有的預設設定。
以上幾步就建立起了一個有主架構結構的應用程式,并使它具有了啟動畫面。這是我們要做的準備工作已經完成。
二、給基于對話框應用程式做啟動畫面
(一)建立基于對話框的應用程式Spla
從File菜單選擇New對話,在Projects頁籤中選擇AppWizard(exe)圖示。鍵入Spla 作為項目名并單擊OK。在第一步中選中Dialog Based單選按鈕,其它接受所有預設選項。
(二)做啟動畫面
這裡做啟動畫面如果仍采用前述用Gallery來插入是不行的,因為基于對話框的應用程式沒有主架構。不過我們可以把上面建立起的啟動畫面檔案移植過來,然後,對程式進行少許程式設計修改就行。請按照下面的步驟來做:
1、将Splash.cpp和Splash.h兩個檔案從Hs工程中拷貝到你的工程中。添加如下代碼到CSplaApp的InitInstance()函數中。
#include "Splash.h"//頭檔案請放在開始位置
BOOL CSplaApp::InitInstance()
{
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
CSplashWnd::EnableSplashScreen
(cmdInfo.m_bShowSplash);
...
}
2、接下來,使用ClassWizard來添加OnCreate函數到你的對話框類中,并且添加如下代碼: #include "Splash.h"//頭檔案請放在開始位置
int CSplaDlg::OnCreate
(LPCREATESTRUCT lpCreateStruct)
{
…
CSplashWnd::ShowSplashScreen(this);
…
}
3、将Splash16.bmp檔案從Hs工程中拷貝到你的工程中 蚩猈orkspace的Resouce項,将Splash16.bmp插入。打開Properties将IDB_BITMAP1改為IDB_SPLASH,這個ID值隻要和程式中一緻起來就行,現在這樣改最簡便。
現在可以編譯運作程式了,程式運作時出現如圖2(略)的啟動畫面。這是預設的畫面,你可以打開圖形編輯器自己加工。如果你要改變啟動畫面的停留時間,就修改SetTime中的第二個參數,這裡是750毫秒。
int CSplashWnd::OnCreate
(LPCREATESTRUCT lpCreateStruct)
{
…
// Set a timer to destroy the splash screen.
SetTimer(1, 750, NULL);
return 0;
}
一、給一文檔/視圖應用程式做啟動畫面
(一) 建立一單文檔/視圖應用程式Hs
從File菜單選擇New對話,在Projects頁籤中選擇AppWizard(exe)圖示。鍵入Hs作為項目名并單擊OK。在第一步中選中Single Document單選按鈕,其它接受所有預設選項。
(二) 添加啟動畫面
當AppWizard完成,而且Visual C++打開項目的時候,從Project菜單中選擇Add To Project,并單擊位于次級菜單上的Comonents and Controls…,選擇Splash screen元件,如圖1(略)所示,單擊Insert。接受所有的預設設定。
以上幾步就建立起了一個有主架構結構的應用程式,并使它具有了啟動畫面。這是我們要做的準備工作已經完成。
二、給基于對話框應用程式做啟動畫面
(一)建立基于對話框的應用程式Spla
從File菜單選擇New對話,在Projects頁籤中選擇AppWizard(exe)圖示。鍵入Spla 作為項目名并單擊OK。在第一步中選中Dialog Based單選按鈕,其它接受所有預設選項。
(二)做啟動畫面
這裡做啟動畫面如果仍采用前述用Gallery來插入是不行的,因為基于對話框的應用程式沒有主架構。不過我們可以把上面建立起的啟動畫面檔案移植過來,然後,對程式進行少許程式設計修改就行。請按照下面的步驟來做:
1、将Splash.cpp和Splash.h兩個檔案從Hs工程中拷貝到你的工程中。添加如下代碼到CSplaApp的InitInstance()函數中。
#include "Splash.h"//頭檔案請放在開始位置
BOOL CSplaApp::InitInstance()
{
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
CSplashWnd::EnableSplashScreen
(cmdInfo.m_bShowSplash);
...
}
2、接下來,使用ClassWizard來添加OnCreate函數到你的對話框類中,并且添加如下代碼: #include "Splash.h"//頭檔案請放在開始位置
int CSplaDlg::OnCreate
(LPCREATESTRUCT lpCreateStruct)
{
…
CSplashWnd::ShowSplashScreen(this);
…
}
3、将Splash16.bmp檔案從Hs工程中拷貝到你的工程中 蚩猈orkspace的Resouce項,将Splash16.bmp插入。打開Properties将IDB_BITMAP1改為IDB_SPLASH,這個ID值隻要和程式中一緻起來就行,現在這樣改最簡便。
現在可以編譯運作程式了,程式運作時出現如圖2(略)的啟動畫面。這是預設的畫面,你可以打開圖形編輯器自己加工。如果你要改變啟動畫面的停留時間,就修改SetTime中的第二個參數,這裡是750毫秒。
int CSplashWnd::OnCreate
(LPCREATESTRUCT lpCreateStruct)
{
…
// Set a timer to destroy the splash screen.
SetTimer(1, 750, NULL);
return 0;
}