Visual C++ 界面編程

 
[前言:]擁有美麗的外觀,軟件就成功了一半。界面由控件、工具欄、菜單、窗體等元素組成,對他們進行美化就能得到一個美麗的界面。
讓控件更醒目

在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的子類,並且擁有自繪能力。它可以向你提供以下的功能:

  1. 設置字體顏色。
  2. 設置高亮度顏色。
  3. 設置高亮度時的風格。
  4. 設置選中時和在普通狀態下的菜單顯示的圖標。
  5. 設置顯示圖標大小。

  在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的子類,並且擁有自繪能力。它可以向你提供以下的功能:

  1. 設置字體顏色。
  2. 設置高亮度顏色。
  3. 設置高亮度時的風格。
  4. 設置選中時和在普通狀態下的菜單顯示的圖標。
  5. 設置顯示圖標大小。

  在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的子類,並且擁有自繪能力。它可以向你提供以下的功能:

  1. 設置字體顏色。
  2. 設置高亮度顏色。
  3. 設置高亮度時的風格。
  4. 設置選中時和在普通狀態下的菜單顯示的圖標。
  5. 設置顯示圖標大小。

  在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的子類,並且擁有自繪能力。它可以向你提供以下的功能:

  1. 設置字體顏色。
  2. 設置高亮度顏色。
  3. 設置高亮度時的風格。
  4. 設置選中時和在普通狀態下的菜單顯示的圖標。
  5. 設置顯示圖標大小。

  在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的子類,並且擁有自繪能力。它可以向你提供以下的功能:

  1. 設置字體顏色。
  2. 設置高亮度顏色。
  3. 設置高亮度時的風格。
  4. 設置選中時和在普通狀態下的菜單顯示的圖標。
  5. 設置顯示圖標大小。

  在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的子類,並且擁有自繪能力。它可以向你提供以下的功能:

  1. 設置字體顏色。
  2. 設置高亮度顏色。
  3. 設置高亮度時的風格。
  4. 設置選中時和在普通狀態下的菜單顯示的圖標。
  5. 設置顯示圖標大小。

  在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的子類,並且擁有自繪能力。它可以向你提供以下的功能:

  1. 設置字體顏色。
  2. 設置高亮度顏色。
  3. 設置高亮度時的風格。
  4. 設置選中時和在普通狀態下的菜單顯示的圖標。
  5. 設置顯示圖標大小。

  在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的子類,並且擁有自繪能力。它可以向你提供以下的功能:

  1. 設置字體顏色。
  2. 設置高亮度顏色。
  3. 設置高亮度時的風格。
  4. 設置選中時和在普通狀態下的菜單顯示的圖標。
  5. 設置顯示圖標大小。

  在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的子類,並且擁有自繪能力。它可以向你提供以下的功能:

  1. 設置字體顏色。
  2. 設置高亮度顏色。
  3. 設置高亮度時的風格。
  4. 設置選中時和在普通狀態下的菜單顯示的圖標。
  5. 設置顯示圖標大小。

  在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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章