我們使用過很多的軟件, 給我們留下印象很深的是那些界面漂亮且迷人的軟件, 國外的軟件象QuickTime,國產的象金山詞霸等,它們的軟件界面設計風格都有獨特之處。本人跟據自已的經驗和大家探討一下軟件的漂亮界面實現的原理並提供DEMO程序。本人經驗不多,經常從VCKBASE.COM吸取知識,共同學習,如有不足之處,請指正!也歡迎和我聯繫。下面就開始吧!
一、漂亮界面實現的原理
用圖象元素自繪窗口標題樣欄,邊框,系統按鈕(最大化、最小化、關閉按鈕)還有按窗口中的控件。圖象當然是美工畫的,但要你教美工怎麼去畫,是不是不能理解? ,呆會我會告訴你你如何去教美工畫.請先仔細看下圖。
明白了吧,被紅線包括的部分都是要畫的圖象。畫得好不好會直接影響你的軟件界面。
二、原理說玩了,來說一下實現的基本知識
1、VC軟件繪圖技術:
01.
CBitmap*
pBitmap =
new
CBitmap;
02.
BITMAP
BmpInfo;
03.
CBitmap*
pOldBitmap;
04.
CDC*
pDisplayMemDC=
new
CDC;
05.
pDisplayMemDC->CreateCompatibleDC(pDC);
06.
07.
pBitmap->LoadBitmap(IDB_TITLE_LEFT);
08.
pOldBitmap=(CBitmap*)pDisplayMemDC->SelectObject(pBitmap);
09.
pBitmap->GetBitmap(&BmpInfo);
10.
11.
//
x,y爲繪圖位置 ,必要時此語句要有For(..;..;..)控制
12.
pDC->BitBlt(x,y,
BmpInfo.bmWidth, BmpInfo.bmHeight, pDisplayMemDC, 0, 0, SRCCOPY);
13.
14.
pDisplayMemDC->SelectObject(pOldBitmap);
15.
pBitmap->DeleteObject();
16.
17.
ReleaseDC(pDisplayMemDC);
//記得執行以下的語句
18.
delete
pDisplayMemDC;
19.
delete
pBitmap;
2、座標的概念:點、窗口座標和屏幕座標及轉換,很重要!如不清楚請先複習相關知識。
下圖是我寫在一個界面,就是基於上述原理實現的:
下面介紹軟件如何實現的:
①、重載對話框的消息函數:
1.
void
OnNcLButtonDown(
UINT
nHitTest,
CPoint point);
//單擊標題欄時是響應
1.
void
OnNcMouseMove(
UINT
nHitTest,
CPoint point);
//Mous 在標題移動時響應
1.
LRESULT
DefWindowProc(
UINT
message,
WPARAM
wParam,
LPARAM
lParam)
②、添加關鍵成員函數爲:BOOL DrawTitleBar(CDC *pDC)
③、添加完消息涵數後,在.cpp中實現它們的代碼:
001.
void
CTitleBarDlg::OnNcMouseMove(
UINT
nHitTest,
CPoint point)
002.
{
003.
CDC*
pDC = GetWindowDC();
004.
CDC*
pDisplayMemDC=
new
CDC;
005.
pDisplayMemDC->CreateCompatibleDC(pDC);
006.
CBitmap*
pBitmap =
new
CBitmap;
007.
CBitmap*
pOldBitmap;
008.
CRect
rtWnd, rtButton;
009.
010.
if
(pDC)
011.
{
012.
CString
StrTemp =
""
;
013.
GetWindowRect(&rtWnd);
014.
//mouse座標轉化爲本窗口座標
重要
015.
point.x
= point.x - rtWnd.left;
016.
point.y
= point.y - rtWnd.top;
017.
//判斷mouse是否移到系統按鈕上
018.
if
(m_rtButtExit.PtInRect(point))
019.
{
020.
pBitmap->LoadBitmap(IDB_EXIT_FOCUS);
021.
StrTemp
= _T(
"關閉"
);
022.
}
023.
else
024.
{
025.
if
(m_rtButtMin.PtInRect(point))
026.
{
027.
pBitmap->LoadBitmap(IDB_MIN_FOCUS);
028.
StrTemp
= _T(
"最小化窗口"
);
029.
}
030.
else
031.
{
032.
if
(m_rtButtMax.PtInRect(point))
033.
{
034.
pBitmap->LoadBitmap(IDB_MAX_FOCUS);
035.
if
(IsZoomed())
036.
{
037.
StrTemp
= _T(
"還原窗口"
);
038.
}
039.
else
040.
{
041.
StrTemp
= _T(
"最化大窗口"
);
042.
}
043.
}
044.
else
045.
{
046.
pBitmap->LoadBitmap(IDB_NORMAL);
047.
}
048.
}
049.
}
050.
051.
rtButton
= m_rtButtMin;
052.
BITMAP
BmpInfo;
053.
pBitmap->GetBitmap(&BmpInfo);
054.
pOldBitmap=(CBitmap*)pDisplayMemDC->SelectObject(pBitmap);
055.
pDC->BitBlt(rtButton.left-6,
056.
rtButton.top-2,
057.
BmpInfo.bmWidth,
058.
BmpInfo.bmHeight,
059.
pDisplayMemDC,
060.
0,
061.
0,
062.
SRCCOPY);
063.
pDisplayMemDC->SelectObject(pOldBitmap);
064.
pBitmap->DeleteObject();
065.
CRect
ShowTipRec;
066.
ShowTipRec
= m_rtButtMin;
067.
if
(!StrTemp.IsEmpty())
068.
{
069.
//
ScreenToClient(&ShowTipRec);
070.
//
m_ToolTip.AddToolTip(IDD_TITLEBAR_DIALOG,&ShowTipRec,StrTemp);
071.
//
m_ToolTip.SetDelayTime(200);
072.
}
073.
}
074.
075.
ReleaseDC(pDisplayMemDC);
076.
ReleaseDC(pDC);
077.
delete
pDisplayMemDC;
078.
delete
pBitmap;
079.
080.
CDialog::OnNcMouseMove(nHitTest,
point);
081.
}
082.
083.
//此爲關鍵函數
084.
void
CTitleBarDlg::DrawTitleBar(CDC
*pDC)
085.
{
086.
if
(m_hWnd)
087.
{
088.
CBrush
Brush(RGB(0,100,255));
089.
CBrush*
pOldBrush = pDC->SelectObject(&Brush);
090.
091.
CRect
rtWnd, rtTitle, rtButtons;
092.
GetWindowRect(&rtWnd);
093.
//取得標題欄的位置
094.
rtTitle.left
= GetSystemMetrics(SM_CXFRAME);
095.
rtTitle.top
= GetSystemMetrics(SM_CYFRAME);
096.
rtTitle.right
= rtWnd.right - rtWnd.left - GetSystemMetrics(SM_CXFRAME);
097.
rtTitle.bottom
= rtTitle.top + GetSystemMetrics(SM_CYSIZE);
098.
099.
//計算最小化按鈕的位置,位圖大小爲15X15
100.
rtButtons.left
= rtTitle.right-60;
101.
rtButtons.top=
rtTitle.top+2;
102.
rtButtons.right
= rtTitle.right-42;
103.
rtButtons.bottom
= rtTitle.top+20;
104.
m_rtButtMin
= rtButtons;
105.
//計算最大化按鈕的位置,位圖大小爲15X15
106.
m_rtButtMax.left
= m_rtButtMin.left + 18;
107.
m_rtButtMax.top
= m_rtButtMin.top;
108.
m_rtButtMax.right
= m_rtButtMin.right + 16;
109.
m_rtButtMax.bottom
= m_rtButtMin.bottom;
110.
111.
m_rtButtExit.left
= m_rtButtMax.left + 18;
112.
m_rtButtExit.top
= m_rtButtMax.top;
113.
m_rtButtExit.right
= m_rtButtMax.right + 16;
114.
m_rtButtExit.bottom
= m_rtButtMax.bottom;
115.
116.
//準備
117.
CBitmap*
pBitmap =
new
CBitmap;
118.
BITMAP
BmpInfo;
119.
CBitmap*
pOldBitmap;
120.
CDC*
pDisplayMemDC=
new
CDC;
121.
pDisplayMemDC->CreateCompatibleDC(pDC);
122.
123.
//重畫Caption
124.
POINT
DrawPonit;
125.
DrawPonit.x
= rtTitle.left-4;
126.
DrawPonit.y
= rtTitle.top-4;
127.
pBitmap->LoadBitmap(IDB_TITLE_LEFT);
128.
pOldBitmap=(CBitmap*)pDisplayMemDC->SelectObject(pBitmap);
129.
pBitmap->GetBitmap(&BmpInfo);
130.
pDC->BitBlt(rtTitle.left-4,
131.
rtTitle.top-4,
132.
BmpInfo.bmWidth,
133.
BmpInfo.bmHeight,
134.
pDisplayMemDC,
135.
0,
136.
0,
137.
SRCCOPY);
138.
DrawPonit.x
= DrawPonit.x + BmpInfo.bmWidth;
139.
pDisplayMemDC->SelectObject(pOldBitmap);
140.
pBitmap->DeleteObject();
141.
142.
pBitmap->LoadBitmap(IDB_TOP);
143.
pOldBitmap=(CBitmap*)pDisplayMemDC->SelectObject(pBitmap);
144.
pBitmap->GetBitmap(&BmpInfo);
145.
while
(DrawPonit.x<=
m_rtButtMin.left-66)
146.
{
147.
pDC->BitBlt(DrawPonit.x,
148.
DrawPonit.y,
149.
BmpInfo.bmWidth,
150.
BmpInfo.bmHeight,
151.
pDisplayMemDC,
152.
0,
153.
0,
154.
SRCCOPY);
155.
DrawPonit.x
= DrawPonit.x + BmpInfo.bmWidth;
156.
}
157.
pDisplayMemDC->SelectObject(pOldBitmap);
158.
pBitmap->DeleteObject();
159.
160.
pBitmap->LoadBitmap(IDB_TITLE_RIGHT);
161.
pOldBitmap=(CBitmap*)pDisplayMemDC->SelectObject(pBitmap);
162.
pBitmap->GetBitmap(&BmpInfo);
163.
pDC->BitBlt(DrawPonit.x,
164.
DrawPonit.y,
165.
BmpInfo.bmWidth,
166.
BmpInfo.bmHeight,
167.
pDisplayMemDC,
168.
0,
169.
0,
170.
SRCCOPY);
171.
172.
pDisplayMemDC->SelectObject(pOldBitmap);
173.
pBitmap->DeleteObject();
174.
175.
176.
177.
//重畫最小化button
178.
rtButtons
= m_rtButtMin;
179.
pBitmap->LoadBitmap(IDB_NORMAL);
180.
pBitmap->GetBitmap(&BmpInfo);
181.
pOldBitmap=(CBitmap*)pDisplayMemDC->SelectObject(pBitmap);
182.
pDC->BitBlt(rtButtons.left-6,
183.
rtButtons.top-2,
184.
BmpInfo.bmWidth,
185.
BmpInfo.bmHeight,
186.
pDisplayMemDC,
187.
0,
188.
0,
189.
SRCCOPY);
190.
pDisplayMemDC->SelectObject(pOldBitmap);
191.
pBitmap->DeleteObject();
192.
193.
int
nOldMode
= pDC->SetBkMode(TRANSPARENT);
194.
COLORREF
clOldText=pDC->GetTextColor();
195.
CFont
titleFont;
196.
titleFont.CreateFont(
12,
//
nHeight
197.
8,
//
nWidth
198.
0,
//
nEscapement文本行逆時針旋轉角度
199.
0,
//
nOrientation字體角度
200.
FW_BOLD,
//
nWeight字體粗細程度
201.
FALSE,
//
bItalic
202.
FALSE,
//
bUnderline
203.
0,
//
cStrikeOut 刪除線
204.
ANSI_CHARSET,
//
nCharSet
205.
OUT_DEFAULT_PRECIS,
//
nOutPrecision
206.
CLIP_DEFAULT_PRECIS,
//
nClipPrecision
207.
DEFAULT_QUALITY,
//
nQuality
208.
DEFAULT_PITCH
| FF_SWISS,
//
nPitchAndFamily
209.
_T(
"隸書"
));
//
lpszFac pDC->SelectStockObject(SYSTEM_FIXED_FONT);
210.
CFont
*OldFont;
211.
OldFont
= pDC->SelectObject(&titleFont);
212.
CString
m_StrTitle;
213.
GetWindowText(m_StrTitle);
214.
pDC->SetTextColor(RGB(80,255,25));
215.
if
(m_ShowTitle)
216.
{
217.
pDC->TextOut(65,10,m_StrTitle);
218.
}
219.
else
220.
{
221.
m_StrTitle
= m_StrTitle.Left(6);
222.
m_StrTitle
+=
"..."
;
223.
pDC->TextOut(30,10,m_StrTitle);
224.
}
225.
pDC->SetBkMode(nOldMode);
226.
pDC->SetTextColor(clOldText);
227.
pDC->SelectObject(OldFont);
228.
//pDC->TextOut(60,60,m_StrTitle);
229.
//重畫左邊框
230.
pBitmap->LoadBitmap(IDB_LEFTDOWN);
231.
pBitmap->GetBitmap(&BmpInfo);
232.
pOldBitmap=(CBitmap*)pDisplayMemDC->SelectObject(pBitmap);
233.
int
i
;
234.
for
(i=
20;i<=rtWnd.bottom;i=i+BmpInfo.bmHeight-3)
235.
{
236.
pDC->BitBlt(0,
rtButtons.top+i,
237.
BmpInfo.bmWidth,
238.
BmpInfo.bmHeight,
239.
pDisplayMemDC,
240.
0,
241.
0,
242.
SRCCOPY);
243.
}
244.
pDisplayMemDC->SelectObject(pOldBitmap);
245.
pBitmap->DeleteObject();
246.
247.
//重畫右邊框
248.
pBitmap->LoadBitmap(IDB_RIGHTDOWN);
249.
pBitmap->GetBitmap(&BmpInfo);
250.
pOldBitmap=(CBitmap*)pDisplayMemDC->SelectObject(pBitmap);
251.
GetClientRect(&rtWnd);
252.
for
(i=
25;i<=rtWnd.bottom+27;i=i+BmpInfo.bmHeight-3)
253.
{
254.
pDC->BitBlt(rtWnd.right,
i,
255.
BmpInfo.bmWidth,
256.
BmpInfo.bmHeight,
257.
pDisplayMemDC,
258.
0,
259.
0,
260.
SRCCOPY);
261.
}
262.
pDisplayMemDC->SelectObject(pOldBitmap);
263.
pBitmap->DeleteObject();
264.
265.
//重畫底邊框
266.
pBitmap->LoadBitmap(IDB_DOWN);
267.
pBitmap->GetBitmap(&BmpInfo);
268.
pOldBitmap=(CBitmap*)pDisplayMemDC->SelectObject(pBitmap);
269.
GetClientRect(&rtWnd);
270.
for
(i=
9; i<=rtWnd.right; i=i+2)
271.
{
272.
GetClientRect(&rtWnd);
273.
pDC->BitBlt(i,rtWnd.bottom+26,
274.
BmpInfo.bmWidth,
275.
BmpInfo.bmHeight,
276.
pDisplayMemDC,
277.
0,
278.
0,
279.
SRCCOPY);
280.
}
281.
pDisplayMemDC->SelectObject(pOldBitmap);
282.
pBitmap->DeleteObject();
283.
284.
285.
ReleaseDC(pDisplayMemDC);
286.
delete
pDisplayMemDC;
287.
delete
pBitmap;
288.
}
289.
}
290.
291.
void
CTitleBarDlg::OnNcLButtonDown(
UINT
nHitTest,
CPoint point)
292.
{
293.
294.
CRect
rtWnd;
295.
GetWindowRect(&rtWnd);
296.
297.
//mouse座標轉化爲本窗口座標
重要
298.
point.x
= point.x - rtWnd.left;
299.
point.y
= point.y - rtWnd.top;
300.
//檢測各按鈕是否按到
301.
if
(m_rtIcon.PtInRect(point))
302.
AfxMessageBox(
"界面軟件設計者:朱一鬆
EMail:[email protected]"
);
303.
else
304.
{
305.
if
(m_rtButtHelp.PtInRect(point))
306.
{
307.
SendMessage(WM_HELP);
308.
}
309.
else
310.
{
311.
if
(m_rtButtExit.PtInRect(point))
312.
{
313.
SendMessage(WM_CLOSE);
314.
}
315.
else
316.
{
317.
if
(m_rtButtMin.PtInRect(point))
318.
{
319.
m_ShowTitle
= FALSE;
320.
SendMessage(WM_SYSCOMMAND,
321.
SC_MINIMIZE,
322.
MAKELPARAM(point.x,
point.y));
323.
}
324.
else
325.
{
326.
if
(m_rtButtMax.PtInRect(point))
327.
{
328.
m_ShowTitle
= TRUE;
329.
if
(IsZoomed())
330.
SendMessage(WM_SYSCOMMAND,
331.
SC_RESTORE,
332.
MAKELPARAM(point.x,
point.y));
333.
else
334.
{
335.
SendMessage(WM_SYSCOMMAND,
336.
SC_MAXIMIZE,
337.
MAKELPARAM(point.x,
point.y));
338.
Invalidate();
339.
}
340.
}
341.
else
342.
{
343.
if
(!IsZoomed())
344.
{
345.
Default();
346.
}
347.
}
348.
}
349.
}
350.
}
351.
}
352.
}
353.
354.
//******************************************************
355.
LRESULT
CTitleBarDlg::DefWindowProc(
UINT
message,
WPARAM
wParam,
LPARAM
lParam)
356.
{
357.
LRESULT
lrst=CDialog::DefWindowProc(message,
wParam, lParam);
358.
359.
if
(!::IsWindow(m_hWnd))
360.
return
lrst;
361.
362.
if
(message==WM_MOVE||
363.
message==WM_PAINT||
364.
message==WM_NCPAINT||
365.
message==WM_NCACTIVATE||
366.
message
== WM_NOTIFY)
367.
{
368.
CDC*
pWinDC = GetWindowDC();
369.
if
(pWinDC)
370.
DrawTitleBar(pWinDC);
371.
ReleaseDC(pWinDC);
372.
}
373.
return
lrst;
374.
}
好了運行你的程序,即可出現漂亮的界面。說明在設計對話框時最好只選上Title Bars,其它不要.消息函數要手動添加。只本程序是在VC++6.0 +WinXP環境下完成的。
經過實踐證明,僅僅畫一個窗口很容易,多窗口程序軟件實現統一風格很難。如若有機會的話,我會和大家繼續探討如何將設計好的漂亮窗口子類化,讓程序所有的窗口有統一風格,美化窗口的其它控件並可自動隨窗口改變而調整大小。我想那纔是我們大家關心的。對不?