台灣最大程式設計社群網站
線上人數
2901
 
會員總數:245249
討論主題:189109
歡迎您免費加入會員
討論區列表 >> 專欄文章 >> Windows XP開始功能表編程實現
[]  
[我要回覆]
回應主題 加入我的關注話題 檢舉此篇討論 將提問者加入個人黑名單
Windows XP開始功能表編程實現
價值 : 0 QP  點閱數:838 回應數:0

樓主

ntqhz
門外漢
0 6
30 6
發送站內信

一、 概述
Windows XP 開始菜單在很多場合都有應用,最典型的應用是在QQ的主功能表(您可以右鍵單擊QQ的託盤圖示)。該種類型的功能表根據功能表項的種類不同劃分多個區域,可以在大型的軟體系統應用。

二、 程式分析與設計
2.1 Windows XP開始菜單分析
Windows系統的開始功能表包含以下視窗類:
a) 總的視窗容器類DV2ControlHost 控制項 (帶CS_DROPSHADOW類風格,所以功能表彈出時有陰影)
b) 功能表頭部顯示用戶資訊的視窗容器:Desktop User Pane 控制項
顯示用戶資訊的文本控制項:Static 控制項
c) 功能表左半部分(ProgramList)應用程式列表的視窗容器:DesktopSFTBarHost 控制項
顯示程式圖示和文本的SysListView32控制項
d) 更多程式的視窗容器:Desktop More Programs Pane控制項
更多程式視窗:Button控制項,文本:所有程式(&P)
e) 功能表右半部分(PlaceList)的視窗容器:DesktopSFTBarHost
顯示程式圖示和文本的SysListView32控制項
f) 功能表底部的登陸區的視窗容器:DesktopLogoffPane
登出和關閉電腦作為ToolbarWindow32的按鈕存在
為了減少功能表的GDI資源,我們不再模仿XP的做法,而是對整個功能表用一個視窗來實現,就是所謂的DirectUI技術:所有的內容都作為功能表的一個自畫的物件存在。
在MSN DirectUI和Office2007的Ribbon中均採用這種單個視窗處理所有物件的模式。這種模式的好處是,可以實現表單
的平滑無閃爍顯示,並減少GDI資源,並在以後的應用中還可以實現功能表各種動畫顯示。如果採用多視窗實現模式,就很難實現某些動畫特效。
我們把整個視窗劃分為以下幾個區域,如下圖:
a) 用戶資訊顯示區 User Panel,用於處理用戶區背景色和用戶資訊文本;
b) 應用程式列表Program List;
c) 功能表右半部分 Place List;
d) 菜單底部條 BottomBar。


2.2 功能表設計
2.2.1 我們創建一個CStartPane類處理功能表的視窗創建和所有物件的自畫操作。
a) 我們註冊一個視窗類SKINPP_STARTPANE,並設置其類風格為CS­_DROPSHADOW.
b) 創建功能表視窗:
m_hWndStartPane = ::CreateWindowEx(WS_EX_TOOLWINDOW|WS_EX_TOPMOST,STARTPANE_CLASSNAME,"",
WS_POPUP|WS_VISIBLE,x,y,m_nWidth,m_nHeight,m_hWndParent,
NULL,AfxGetInstanceHandle(),(LPVOID)this);
這塈畯戔N視窗擴展風格指定為WS_EX_TOOLWINDOW,是為了防止在任務欄上顯示功能表視窗的圖示,
將視窗擴展風格指定為WS_EX_TOPMOST,是讓功能表顯示在其他視窗的前面。
c) 功能表視窗,設計以下介面
TrackPopPane 彈出功能表,GetPaneSize獲得視窗尺寸,SetPaneMinSize設置視窗最小尺寸,RegisterStartPaneClass註冊視窗類,
WindowProc 視窗消息處理,DestroyPane 銷毀功能表視窗,GetNextItem 獲得下一個功能表項,GetItemTypeFromPoint 通過滑鼠位置獲得功能表項類型,
更多介面請參見源碼。
c) 用戶區域User Panel,設計以下介面
SetUserPaneHeight / GetUserPaneHeight 設置與獲得用戶區的高度,SetUserPaneIcon 設置用戶的圖示,
SetUserPaneTextInfo/ GetUserPaneTextInfo 設置與獲得用戶文本資訊,SetUserPaneText/ GetUserPaneText 設置與獲得用戶文本,
DrawUserPane 畫用戶面板
d) 應用程式列表Program List ,設計以下介面
SetProgItemHeight / GetProgItemHeight 設置/獲得程式列表項高度,SetProgListWidth / GetProgListWidth 設置/獲得應用程式列表寬度,
SetProgIconSize/GetProgIconSize 設置/獲得應用程式列表圖示的大小,AppendProgItem / InsertProgItem / DeleteProgItem 添加/插入/刪除應用程式項,
DrawProgList 畫程式列表
e) Place List,設計以下介面
SetPlaceItemHeight / GetPlaceItemHeight 設置/獲得Place菜單項高度,SetPlaceIconSize / GetPlaceIconSize 設置/獲得Place圖示的大小,
AppendPlaceItem / InsertPlaceItem / DeletePlaceItem 添加/插入/刪除Place菜單項,
DrawPlaceList 畫Place列表
f) BottomBar,設計以下介面
SetBottomBarHeight / GetBottomBarHeight 設置/獲得BottomBar的高度,SetBottomIconSize / GetBottomIconSize 設置/獲得BottomBar圖示的大小,
AppendBottomItem / InsertBottomItem / DeleteBottomBarItem 添加/插入/刪除BottomBar菜單項,DrawBottomBar 畫BottomBar。

g) 消息回應:
我們打算在功能表類中響應以下幾種消息類型:
1. WM_PAINT 用於處理功能表的畫圖工作,在處理該消息中:
1.1我們先畫功能表的整體背景;
1.2畫用戶面板資訊,調用DrawUserPane;
1.3畫菜單中間的分割條,調用DrawSep;
1.4畫程式列表,調用DrawProgList;
1.5畫Place列表,調用 DrawPlaceList;
1.6畫BottomBar,調用 DrawBottomBar。
2. WM_MOUSEMOVE 用於處理滑鼠移動時功能表項的高量顯示。如果當然功能表項是彈出式功能表,那麼就彈出其子功能表。
3. WM_MOUSELEAVE 用於處理滑鼠移出功能表邊界時,對高量功能表項進行還原繪製。
4. WM_LBUTTONDOWN 用於處理滑鼠左鍵按下時,執行功能表項命令。
5. WM_LBUTTONUP 用於處理滑鼠左鍵抬起時,執行功能表項命令。一般命令在這個消息中進行發送。
這塈畯怬Q用::SendMessage(功能表父視窗,WM_COMMAND,m_pCurrentItem->uID,0);執行功能表命令。
6. WM_COMMAND 用於某功能表項被選中並調用命令後,關閉整個功能表。
DestroyPane(); ::SendMessage(功能表父視窗,WM_COMMAND,wParam,lParam);
7. WM_TIMER 這個消息是處理彈出子功能表的關鍵代碼段。
8. WM_ACTIVATE 這個消息用於處理,當功能表父視窗失去程式焦點時,關閉功能表操作。
9. WM_DESTROY 這個消息作功能表銷毀時的清理工作。

2.2.2 功能表類執行流程
聲明CStartPane物件後,SKINPP_STARTPANE視窗類即被註冊,接下來對CStartPane物件進行4個部分(UserPanel,ProgList,PlaceList,BottomBar)的菜單項設置,指定它們的圖示,文本,字體,顏色等。最後,我們調用TrackPopPane方法顯示功能表,執行TrackPopPane時,會根據功能表項的設置和滑鼠位置,創建相應位置和大小的功能表視窗。功能表視窗被創建成功後,即進入WindowProc消息處理函數。在該函數中,對WM_PAINT等上面提到的9個消息進行了相應的處理。並一直進行消息迴圈:
MSG Msg;
while ( GetMessage( &Msg, NULL, 0, 0 ) && IsWindow( m_hWndStartPane ) )
{
TranslateMessage( &Msg );
DispatchMessage( &Msg );
}
直到功能表的父視窗失去焦點,或用戶點擊了某功能表項時,功能表被銷毀,並退出消息迴圈。

三、 編程實現
限於篇幅,我們只列出關鍵的幾個函數實現,全部實現請參見例子源碼,www.uipower.com/WindowsXPMenu.zip
3.1 註冊視窗類:SKINPP_STARTPANE
BOOL CStartPane::RegisterStartPaneClass( void )
{
WNDCLASS wc;
wc.style = CS_DROPSHADOW;
。。。。。。
wc.lpfnWndProc = WindowProc;
wc.lpszClassName = STARTPANE_CLASSNAME;
if ( !::RegisterClass( &wc ) )
return FALSE;
return TRUE;
}

3.2 彈出功能表函數:TrackPopPane
UINT CStartPane::TrackPopPane(int x,int y,HWND hParent,UINT uFlag)
{
m_nTrackPopPaneX = x;
m_nTrackPopPaneY = y;
m_hWndParent = hParent;
GetPaneSize(m_nWidth,m_nHeight);
ResetDispPoint(x,y);

// Create StartPane Window
m_hWndStartPane = ::CreateWindowEx(WS_EX_TOOLWINDOW|WS_EX_TOPMOST,STARTPANE_CLASSNAME,"",
WS_POPUP|WS_VISIBLE,x,y,m_nWidth,m_nHeight,m_hWndParent,
NULL,AfxGetInstanceHandle(),(LPVOID)this);

if ( !::IsWindow(m_hWndStartPane)) return FALSE;
::ReleaseCapture();
::SetCapture(m_hWndStartPane);
MSG Msg;
while ( GetMessage( &Msg, NULL, 0, 0 ) && IsWindow( m_hWndStartPane ) )
{
TranslateMessage( &Msg );
DispatchMessage( &Msg );
}
。。。。。。
return (UINT)0;
}

3.3 WM_PAINT處理函數OnPaint
LRESULT CStartPane::OnPaint(HDC hDC)
{
PAINTSTRUCT ps;
BeginPaint( m_hWndStartPane, &ps );
HDC hdc = ps.hdc;
CRect rcClient;
::GetClientRect(m_hWndStartPane,&rcClient);

CDC MemDC;
MemDC.Attach(::CreateCompatibleDC(hdc));
CBitmap bmp,*pOldBmp;
bmp.Attach(::CreateCompatibleBitmap(hdc,rcClient.Width(),rcClient.Height()));
pOldBmp = MemDC.SelectObject(&bmp);
CRect rcDraw(rcClient);
//draw border
MemDC.Draw3dRect(rcDraw,GetSysColor(COLOR_BTNFACE),GetSysColor(COLOR_3DDKSHADOW));
rcDraw.DeflateRect(1,1);
MemDC.Draw3dRect(rcDraw,GetSysColor(COLOR_BTNHIGHLIGHT),GetSysColor(COLOR_BTNSHADOW));
rcDraw.DeflateRect(1,1);
MemDC.FillSolidRect(rcDraw,GetSysColor(COLOR_BTNFACE));
//draw userpane
CRect rcUserPane(rcDraw);
rcUserPane.DeflateRect(1,1);
rcUserPane.bottom = rcUserPane.top + m_nUserPaneHeight;
DrawUserPane(MemDC,rcUserPane);

//draw Middle Sep
COLORREF clrSep[2];
clrSep[0] = GetSysColor(COLOR_BTNHIGHLIGHT);
clrSep[1] = GetSysColor(COLOR_BTNSHADOW);
CRect rcMiddle(rcDraw);
rcMiddle.DeflateRect(1,1);
rcMiddle.top += m_nUserPaneHeight;
rcMiddle.bottom -= m_nBottomBarHeight;
rcMiddle.left += m_nProgWidth;
rcMiddle.right = rcMiddle.left + 2;
DrawSep(MemDC,CPoint(rcMiddle.right+1,rcMiddle.top),CPoint(rcMiddle.right+1,rcMiddle.bottom),FALSE,clrSep);

//draw ProgList Item
CRect rcProgList(rcDraw);
rcProgList.DeflateRect(1,m_nUserPaneHeight+1,1,m_nBottomBarHeight+1);
rcProgList.right = rcProgList.left + m_nProgWidth;
DrawProgList(MemDC,rcProgList);

//draw PlaceList Item
CRect rcPlaceList(rcDraw);
rcPlaceList.DeflateRect(1,m_nUserPaneHeight+1,1,m_nBottomBarHeight+1);
rcPlaceList.left = rcProgList.right + 2;
DrawPlaceList(MemDC,rcPlaceList);

//draw bottombar
CRect rcBottomBar(rcDraw);
rcBottomBar.DeflateRect(1,1);
rcBottomBar.top = rcBottomBar.bottom - m_nBottomBarHeight;
DrawBottomBar(MemDC,rcBottomBar);

::BitBlt(hdc,0,0,rcClient.Width(),rcClient.Height(),MemDC.m_hDC,0,0,SRCCOPY);

MemDC.SelectObject(pOldBmp);
MemDC.DeleteDC();
bmp.DeleteObject();

EndPaint( m_hWndStartPane, &ps );

return TRUE;
}

四、菜單類的調用與運行
4.1 菜單類的調用
a) 聲明CStartPane類對象
在調用者頭檔中聲明:CStartPane m_wndStartPane;
b) 設置功能表項資訊
//User Panel
_TextInfo textInfo;
m_wndStartPane.GetUserPaneTextInfo(textInfo);
textInfo.clrShadow = GetSysColor(COLOR_BTNSHADOW);
textInfo.nOffsetX = 1;
textInfo.nOffsetY = 1;
textInfo.logFontUserText.lfItalic = TRUE;
m_wndStartPane.SetUserPaneTextInfo(textInfo);
m_wndStartPane.SetUserPaneText(_T("Skin++"));
m_wndStartPane.SetUserPaneIcon(IDI_ICON_START_USER,CSize(48,48));

//ProgList
m_wndStartPane.AppendProgItem(_T("Internet/nInternet Explorer"),ID_PROGLIST_IE,0,SPITEM_DEFAULT,IDI_ICON_IE);
m_wndStartPane.AppendProgItem(_T("E-mail/nMicrosoft Office Outlook"),ID_PROGLIST_OUTLOOK,0,SPITEM_DEFAULT,IDI_ICON_OFFICE);
……
m_wndStartPane.AppendProgItem(_T(""),0,0,SPITEM_SEPARATOR,0);
m_wndStartPane.AppendProgItem(_T("Microsoft Paint"),ID_PROGLIST_MSPAINT,0,SPITEM_NORMAL,IDI_ICON_MSPAINT);
…..
m_wndStartPane.AppendProgItem(_T(""),0,0,SPITEM_SEPARATOR,0);
m_wndStartPane.AppendProgItem(_T("All Programs"),IDR_MENU_TEST,0,SPITEM_BOTTOM|SPITEM_POPUP,0);

//PlaceList
m_wndStartPane.AppendPlaceItem(_T("My Documents"),ID_PLACELIST_MYDOCUMENT,0,
SPITEM_DEFAULT,IDI_ICON_MYDOCUMENT);

m_wndStartPane.AppendPlaceItem(_T("Recent Document"),IDR_MENU_TEST,0,
SPITEM_DEFAULT|SPITEM_POPUP,IDI_ICON_MYRECENTDOCUMENT);
……
//Bottom Bar
CImageList lst;
lst.Create(IDB_STARTPANE_BUTTONS,24,3,RGB(192,192,192));
HICON hIconTurnOff = lst.ExtractIcon(2);
m_wndStartPane.AppendBottomItem(_T("Turn Off Computer"),ID_BOTTOMBAR_TURNOFFCOMPUTER,0,SPITEM_NORMAL,0,hIconTurnOff);
HICON hIconLogOff = lst.ExtractIcon(1);
m_wndStartPane.AppendBottomItem(_T("Log Off"),ID_BOTTOMBAR_LOGOFF,0,SPITEM_NORMAL,0,hIconLogOff);

c) 彈出菜單
CPoint point;
ClientToScreen(&point);
m_wndStartPane.TrackPopPane(point.x,point.y,m_hWndDlg,0);

4.2 運行
運行效果圖如下:


四、 結束語
4.1目前該類CStartPane已經在很多系統中得到廣泛的應用,也可以用於您的項目中;
4.2 Skin++ 可以支援對CStartPane的換膚。可以下載例子源碼獲得。
4.3 本案例源碼請到Skin++網站下載:www.uipower.com
Skin++換膚後效果圖:


搜尋相關Tags的文章: [ Skin ] , [ GUI ] , [ UI ] , [ 介面 ] , [ Windows開始菜單 ] , [ DirectUI技術 ] ,
本篇文章發表於2007-07-01 07:25
別忘捐VP感謝幫助你的人 新手會員瞧一瞧
目前尚無任何回覆
   

回覆
如要回應,請先登入.