Lesson7:定製應用程序外觀
應用程序外觀是用戶體驗中一個重要因素,優美的程序外觀可以提升程序逼格。本文主要講解了MFC中應用程序外觀的修改,主要包括任務欄、工具欄、啓動畫面等。
1. 程序窗口的圖標、背景、光標修改
1.1 窗口創建之前修改
因爲應用程序的外觀主要是在程序的框架窗口上,通過框架類體現的,所以在定製程序外觀的時候,主要是在框架類進行修改。如果要修改應用程序框架外觀大小,通常在窗口創建之前。可以在CMainFrame類裏的PreCreateWindow()函數中實現。如果我們要修改窗口的圖標、背景、光標,應該怎麼修改?窗口的類型,大小是在創建窗口設定的,但前面Windows程序運行機制我們瞭解瞭如何創建一個窗口,它是通過一個窗口類,在這個類裏設計窗口的時候,就設計好了圖標、背景、光標,現有的窗口類的設計是MFC幫我們在底層已經設計好了,雖然我們不能改變這個底層代碼,但我們可以重新設計一個窗口類,然後用自己的這個窗口類進行修改圖標、背景、光標。
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT&cs) //窗口創建之前修改應用程序框架大小
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
// TODO: 在此處通過修改
// CREATESTRUCT cs 來修改窗口類或樣式
/********************* 窗口創建之前修改應用程序框架大小 *****************/
cs.cx = 300; //修改窗口大小(300*400大小)
cs.cy = 400;
//cs.style = ~FWS_ADDTOTITLE; //修改標題欄名字,因爲會選用默認的名字,所以將默認的選項取反
//cs.style = cs.style & ~FWS_ADDTOTITLE; //這種方式和上面方式的效果一樣,選一種即可
cs.style = WS_OVERLAPPEDWINDOW; //這種方式和上面方式的效果一樣,選一種即可
cs.lpszName = "http"; //窗口名稱
/********************** 自定義窗口類,然後註冊使用 *****************/
WNDCLASS wndcls;
wndcls.cbClsExtra = 0; //類的額外內存不需要
wndcls.cbWndExtra = 0; //窗口的類的額外內存不需要
wndcls.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); //窗口背景
wndcls.hCursor=LoadCursor(NULL,IDC_HELP); //窗口光標
wndcls.hIcon = LoadIcon(NULL,IDI_ERROR); //窗口tubiao
wndcls.hInstance = AfxGetInstanceHandle(); //獲得當前應用程序的句柄
wndcls.lpfnWndProc = ::DefWindowProc; //窗口過程函數
wndcls.lpszClassName ="SUNXING"; //類的名稱
wndcls.lpszMenuName = NULL; //菜單的名稱,雖然設置爲NULL,但會用到MFC自帶的菜單
wndcls.style = CS_HREDRAW | CS_VREDRAW; //窗口類的類型,水平和垂直重繪
RegisterClass(&wndcls); //註冊窗口
cs.lpszClass = "SUNXING"; //修改cs結構體成員變量的類名,用它來重新設置窗口樣式
//cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW| CS_VREDRAW, 0, 0, LoadIcon(NULL, IDI_WARNING)); //不用註冊窗口類,直接修改窗口樣式
return TRUE;
}
上面我們通過窗口類重新設計與註冊使用後發現,只有圖標發生了改變,而光標和背景沒有改變,這是因爲,程序在外觀上是view類覆蓋在frame類之上的,光標和背景消息首先被view類在獲得響應,所以我們在frame類裏修改後看不到效果。所以我們在view類裏的PreCreateWindow()函數裏添加一句代碼,修改窗口類爲我們以設計的類,就可以看到效果。 cs.lpszClass = "SUNXING";
如果我們僅僅需要修改圖標、光標、背景而去重新設計窗口類,那就太麻煩了,MFC提供了取件函數進行直接修改。
LPCTSTR AFXAPIAfxRegisterWndClass( UINT nClassStyle, HCURSORhCursor= 0, HBRUSH hbrBackground = 0,HICON hIcon = 0 );
上面註釋起來的就是這個函數的應用,同理這個函數可以在view類裏使用。
1.2 窗口創建之後修改
如果我們已經創建了窗口,那麼在窗口創建之後如果要修改窗口外觀,需要在CMainFrame類和View類裏的OnCreate()裏通過SetWindowLong()函數進行操作,下面是兩個例子的具體代碼。
SetWindowLong(m_hWnd,GWL_STYLE, WS_OVERLAPPEDWINDOW); //取消了窗口名稱
SetWindowLong(m_hWnd,GWL_STYLE, GetWindowLong(m_hWnd, GWL_STYLE) & ~WS_MAXIMIZEBOX); //取消了最大化
1.3 循環顯示窗口的圖標
如果我們需要在窗口顯示中有幾幅圖標輪流顯示,該怎麼做呢?首先設計我們要加載的圖標,然後加載圖標到圖標數組中。假設我們添加了三幅圖標,這裏需要添加含有三個元素的圖標數組,
HICON m_hIcons[3]。並通過添加timer函數定時地不斷地切換圖標。循環顯示圖標顯然是在窗口建立了以後進行的,所以理所當然的在CMainFrame類的OnCreate()函數中添加代碼。
int CMainFrame::OnCreate(LPCREATESTRUCTlpCreateStruct)
{
//三種方式加載圖標,三選一就行
m_hIcons[0] = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_ICON1));
m_hIcons[1] = LoadIcon(theApp.m_hInstance,MAKEINTRESOURCE(IDI_ICON2));//這裏需要將另一個源文件定義的theApp變量聲明一下,延伸作用域。
m_hIcons[2] = LoadIcon(AfxGetApp()->m_hInstance,MAKEINTRESOURCE(IDI_ICON3));
SetClassLong(m_hWnd, GCL_HICON, (LONG)m_hIcons[0]);//先將第一幅圖標加載上
SetTimer(1,1000,NULL); //設置定時器 調用定時器函數timer()
return 0;
}
添加timer函數。
void CMainFrame::OnTimer(UINT_PTRnIDEvent)
{
// TODO: 在此添加消息處理程序代碼和/或調用默認值
static int index = 2; //index定義爲靜態變量
SetClassLong(m_hWnd, GCL_HICON, (LONG)m_hIcons[index]);
index = (++index) % 3;
CFrameWnd::OnTimer(nIDEvent);
}
2. 狀態欄中顯示鼠標位置和系統時間
2.1 顯示鼠標位置
View類窗口覆蓋在frame類窗口之上,鼠標是在view類窗口裏動作的,所以這裏捕獲鼠標消息首先要在view類裏添加一個鼠標移動的消息,來獲得鼠標的位置。
void CStyleView::OnMouseMove(UINTnFlags, CPoint point)
{
// TODO: 在此添加消息處理程序代碼和/或調用默認值
CString str;
str.Format("x=%d,y=%d",point.x,point.y);
//幾種在狀態欄裏獲得放置鼠標位置的方式
//((CMainFrame*)GetParent())->m_wndStatusBar.SetWindowText(str);//通過獲取框架類窗口的指針,調用框架類定義的狀態欄對象,用對象的方法設定文本
//((CMainFrame*)GetParent())->SetMessageText(str);
//((CMainFrame*)GetParent())->GetMessageBar()->SetWindowText(str);
GetParent()->GetDescendantWindow(AFX_IDW_STATUS_BAR)->SetWindowText(str);
CView::OnMouseMove(nFlags, point);
}
2.2 顯示系統時間
顯示系統時間同樣是在窗口建立之後的動作,所以需要在OnCreate()函數裏不斷的調用timer()函數,這裏只需要在OnCreate()函數裏添加一句調用定時器的代碼,其餘代碼都是嚮導自動生成的。SetTimer(1, 1000, NULL); //設置定時器, 調用定時器函數timer()
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if(CFrameWnd::OnCreate(lpCreateStruct) == -1)
return-1;
if(!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER| CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
TRACE0("未能創建工具欄\n");
return-1; // 未能創建
}
if(!m_wndStatusBar.Create(this))
{
TRACE0("未能創建狀態欄\n");
return-1; // 未能創建
}
m_wndStatusBar.SetIndicators(indicators,sizeof(indicators)/sizeof(UINT));
//TODO: 如果不需要可停靠工具欄,則刪除這三行
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);
SetTimer(1,1000, NULL); //設置定時器, 調用定時器函數timer()
return0;
}
添加定時器函數,在這個函數裏進行響應
void CMainFrame::OnTimer(UINT_PTR nIDEvent)
{
//TODO: 在此添加消息處理程序代碼和/或調用默認值
CTimet = CTime::GetCurrentTime();
CStringstr = t.Format("%H:%M:%S");
CClientDCdc(this); //定義一個dc對象,用來獲得str的尺寸,調整狀態欄裏時鐘顯示框的大小
CSizesz = dc.GetTextExtent(str);
//intindex = m_wndStatusBar.CommandToIndex(IDS_TIMER); //如果不知道時鐘在狀態欄的第幾個窗格,可以這樣找到
m_wndStatusBar.SetPaneInfo(1,IDS_TIMER, SBPS_NORMAL, sz.cx);
m_wndStatusBar.SetPaneText(1,str); //1就是前面的可以用index代替的數,通過狀態欄的對象調用函數,其中函數有缺省的參數值
CFrameWnd::OnTimer(nIDEvent);
}
3. 添加啓動畫面
添加啓動畫面時,需要準備一幅位圖資源,當我們操作資源的時候,一般是和類相關聯的。
①添加一個類CWzdSplash,基類是CWnd,並在類裏定義一個位圖變量CBitmapm_bitmap;
②通過嚮導添加消息響應函數OnPaint()和 手動添加自己的函數Create()。
準備工作做好後我們來實現啓動畫面三部曲。
①在frame類的OnCreate()函數裏添加代碼
int CMainFrame::OnCreate(LPCREATESTRUCTlpCreateStruct)
{
CWzdSplashwndSplash; //創建啓動窗口類的實例
wndSplash.Create(); //CWzdSplash類裏定義的函數
wndSplash.CenterWindow(); //啓動畫面出現在屏幕中間
wndSplash.UpdateWindow(); //send WM_PAINT
Sleep(2000); //畫面停留時間2s
wndSplash.DestroyWindow(); //銷燬初始畫面窗口
}
②在CWzdSplash的OnPaint()函數中添加代碼
void CWzdSplash::OnPaint()
{
CPaintDCdc(this); // device context for painting
//TODO: 在此處添加消息處理程序代碼
//不爲繪圖消息調用 CWnd::OnPaint()
BITMAPbitmap;
m_bitmap.GetBitmap(&bitmap);
CDCdcComp;
dcComp.CreateCompatibleDC(&dc);
dcComp.SelectObject(&m_bitmap);
//draw bitmap
dc.BitBlt(0,0, bitmap.bmWidth, bitmap.bmHeight, &dcComp, 0, 0, SRCCOPY);
}
③在CWzdSplash的Create()函數中添加代碼
void CWzdSplash::Create()
{
m_bitmap.LoadBitmap(IDB_BITMAP1);
BITMAPbitmap;
m_bitmap.GetBitmap(&bitmap);
//CreateEx(0,AfxRegisterWndClass(0),"",WS_POPUP|WS_VISIBLE|WS_BORDER,0,0,bitmap.bmWidth,bitmap.bmHeight,NULL,0);
CreateEx(0,AfxRegisterWndClass(0, AfxGetApp()->LoadStandardCursor(IDC_ARROW)),
NULL,WS_POPUP | WS_VISIBLE, 0, 0, bitmap.bmWidth, bitmap.bmHeight, NULL, NULL);
}