我们使用过很多的软件, 给我们留下印象很深的是那些界面漂亮且迷人的软件, 国外的软件象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:Song_0962@sina.com"
);
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环境下完成的。
经过实践证明,仅仅画一个窗口很容易,多窗口程序软件实现统一风格很难。如若有机会的话,我会和大家继续探讨如何将设计好的漂亮窗口子类化,让程序所有的窗口有统一风格,美化窗口的其它控件并可自动随窗口改变而调整大小。我想那才是我们大家关心的。对不?