前面我们讲述了重绘窗口的一般方法,但是这种方法有很多的局限性,比如,像360杀毒,QQ这种支持背景图片样式的,由于非客户区的原因,操作起来就会很麻烦,这次,我在讲述另外一种重绘窗口的方法,通过这种方法可以定制出和QQ,360杀毒等这一类软件一样的界面。原理也很简单,就是去除窗口的非客户区,实现无边框,无标题栏的窗口,然后自己去模拟窗口的非客户区和客户区,这种方法的不好之处正是他要自己去计算客户区的大小,下面我们就开始讲述实现过程,本次实现一个简易的音乐播放器界面。
设计思路:
1:先去除窗口的非客户区
2:划分客户区和非客户区
3:重绘Button
实现过程:
1:创建一个对话框程序,取名Music
2:创建一个MFC类,取名CDialogEx,基类CDialog
3:修改CMusicDlg类,引用头文件#include "DialogEx.h",将父类CDialog全部修改成CDialogEx
4:完善CDialogEx的功能,属性面板添加OnInitDialog函数,在这里我们去除窗口边框和标题栏,添加代码如下
1
2
3
4
5
6
7
8
|
DWORD
dwStyle = GetStyle();
DWORD
dwNewStyle = WS_OVERLAPPED | WS_VISIBLE| WS_SYSMENU |WS_MINIMIZEBOX|WS_MAXIMIZEBOX|WS_CLIPCHILDREN|WS_CLIPSIBLINGS;
dwNewStyle&=dwStyle;
SetWindowLong(m_hWnd,GWL_STYLE,dwNewStyle);
DWORD
dwExStyle = GetExStyle();
DWORD
dwNewExStyle = WS_EX_LEFT |WS_EX_LTRREADING |WS_EX_RIGHTSCROLLBAR;
dwNewExStyle&=dwExStyle;
SetWindowLong(m_hWnd,GWL_EXSTYLE,dwNewExStyle);
|
上一篇,我们封装过CImage,这次我们重新在封装一遍CImageEx,这次采用Gdiplus,在封装这个图片类之前,我们先做一下Gdiplus的准备工作,(PS:有兴趣的朋友可以自学一下Gdiplus教程,这里不过多的去做说明了,如果有不明白Gdiplus的,可以找我询问)
打开stdafx.h,添加如下代码
1
2
3
4
|
#include <GdiPlus.h>
using
namespace
Gdiplus;
#pragma comment(lib,"Gdiplus.lib")
|
顺便在添加以下宏定义,下文会用到
1
2
3
4
|
//删除指针
#define SafeDelete(pData) { try { delete pData; } catch (...) { ASSERT(FALSE); } pData=NULL; }
//接口释放
#define SafeRelease(pObject) { if (pObject!=NULL) { pObject->Release(); pObject=NULL; } }
|
打开Music.h,添加变量
1
|
ULONG_PTR
m_GdiplusToken;
|
再打开Music.cpp,在InitInstance函数中初始化Gdiplus
1
2
|
GdiplusStartupInput input;
GdiplusStartup(&m_GdiplusToken,&input,NULL);
|
既然有加载那 肯定就会有释放,打开属性面板,添加ExitInstance
1
2
3
4
5
6
|
int
CMusicApp::ExitInstance()
{
GdiplusShutdown(m_GdiplusToken);
return
CWinApp::ExitInstance();
}
|
到这里Gdiplus的准备工作全部完成,现在咱们在开始封装CImageEx
新建类向导,创建CImageEx类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
#ifndef IMAGE_HEAD_FILE
#define IMAGE_HEAD_FILE
#pragma once
//
//图片对象
class
CImageEx
{
//变量定义
protected
:
Image * m_pImage;
//图片对象
TCHAR
m_strImageName[MAX_PATH];
//图片名称
//函数定义
public
:
//构造函数
CImageEx();
//析构函数
virtual
~CImageEx();
//状态函数
public
:
//是否加载
bool
IsNull();
//获取宽度
INT
GetWidth();
//获取高度
INT
GetHeight();
//管理函数
public
:
//销毁图片
bool
DestroyImage();
//加载图片
bool
LoadImage(
LPCTSTR
pszFileName);
//加载图片
bool
LoadImage(
HINSTANCE
hInstance,
LPCTSTR
pszResourceName);
//绘画函数
public
:
//绘画图像
bool
DrawImage(CDC * pDC,
INT
nXPos,
INT
nYPos);
//绘画图像
bool
DrawImage(CDC * pDC,
INT
nXPos,
INT
nYPos,
INT
nDestWidth,
INT
nDestHeight);
//绘画图像
bool
DrawImage(CDC * pDC,
INT
nXDest,
INT
nYDest,
INT
nDestWidth,
INT
nDestHeight,
INT
nXScr,
INT
nYSrc);
//绘画图像
bool
DrawImage(CDC * pDC,
INT
nXDest,
INT
nYDest,
INT
nDestWidth,
INT
nDestHeight,
INT
nXScr,
INT
nYSrc,
INT
nSrcWidth,
INT
nSrcHeight);
//透明绘画
public
:
//混合绘画
bool
AlphaDrawImage(CDC * pDestDC,
INT
xDest,
INT
yDest,
BYTE
cbAlphaDepth);
//混合绘画
bool
AlphaDrawImage(CDC * pDestDC,
INT
xDest,
INT
yDest,
INT
cxDest,
INT
cyDest,
INT
xSrc,
INT
ySrc,
BYTE
cbAlphaDepth);
//混合绘画
bool
AlphaDrawImage(CDC * pDestDC,
INT
xDest,
INT
yDest,
INT
cxDest,
INT
cyDest,
INT
xSrc,
INT
ySrc,
INT
cxSrc,
INT
cySrc,
BYTE
cbAlphaDepth);
};
//
#endif
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
|
#include "StdAfx.h"
#include "ImageEx.h"
//
//构造函数
CImageEx::CImageEx()
{
//设置变量
m_pImage=NULL;
ZeroMemory(m_strImageName,
sizeof
(m_strImageName) );
return
;
}
//析构函数
CImageEx::~CImageEx()
{
//销毁图片
DestroyImage();
return
;
}
//是否加载
bool
CImageEx::IsNull()
{
//加载判断
if
(m_pImage==NULL)
return
true
;
if
(m_pImage->GetLastStatus()!=Ok)
return
true
;
return
false
;
}
//获取宽度
INT
CImageEx::GetWidth()
{
//加载判断
ASSERT(IsNull()==
false
);
if
(IsNull()==
true
)
return
0;
//获取宽度
return
m_pImage->GetWidth();
}
//获取高度
INT
CImageEx::GetHeight()
{
//加载判断
ASSERT(IsNull()==
false
);
if
(IsNull()==
true
)
return
0;
//获取高度
return
m_pImage->GetHeight();
}
//销毁图片
bool
CImageEx::DestroyImage()
{
//删除对象
if
(m_pImage!=NULL) SafeDelete(m_pImage);
return
true
;
}
//加载图片
bool
CImageEx::LoadImage(
LPCTSTR
pszFileName)
{
//加载判断
ASSERT(m_pImage==NULL);
if
(m_pImage!=NULL)
return
false
;
//加载文件
CT2CW strFileName(pszFileName);
m_pImage=Image::FromFile((
LPCWSTR
)strFileName);
//错误判断
if
((m_pImage==NULL)||(m_pImage->GetLastStatus()!=Ok))
{
DestroyImage();
return
false
;
}
return
true
;
}
//加载图片
bool
CImageEx::LoadImage(
HINSTANCE
hInstance,
LPCTSTR
pszResourceName)
{
//加载判断
ASSERT(m_pImage==NULL);
if
(m_pImage!=NULL)
return
false
;
//查找资源
HRSRC
hResource=FindResource(hInstance,pszResourceName,TEXT(
"IMAGE"
));
if
(hResource==NULL)
return
false
;
//读取资源
DWORD
dwImageSize=SizeofResource(hInstance,hResource);
LPVOID
pImageBuffer=LoadResource(hInstance,hResource);
//创建数据
IStream * pIStream=NULL;
if
(CreateStreamOnHGlobal(NULL,TRUE,&pIStream)!=S_OK)
{
ASSERT(FALSE);
return
false
;
}
//写入数据
pIStream->Write(pImageBuffer,dwImageSize,NULL);
//创建位图
m_pImage=Image::FromStream(pIStream);
//释放资源
SafeRelease(pIStream);
//错误判断
if
((m_pImage==NULL)||(m_pImage->GetLastStatus()!=Ok))
{
ASSERT(FALSE);
return
false
;
}
return
true
;
}
//绘画图像
bool
CImageEx::DrawImage(CDC * pDC,
INT
nXPos,
INT
nYPos)
{
//加载判断
ASSERT(m_pImage!=NULL);
if
(m_pImage==NULL)
return
false
;
//创建屏幕
ASSERT(pDC!=NULL);
Graphics graphics(pDC->GetSafeHdc());
//获取属性
INT
nImageWidth=m_pImage->GetWidth();
INT
nImageHeight=m_pImage->GetHeight();
//构造位置
RectF rcDrawRect;
rcDrawRect.X=(REAL)nXPos;
rcDrawRect.Y=(REAL)nYPos;
rcDrawRect.Width=(REAL)nImageWidth;
rcDrawRect.Height=(REAL)nImageHeight;
//绘画图像
graphics.DrawImage(m_pImage,rcDrawRect,0,0,(REAL)nImageWidth,(REAL)nImageHeight,UnitPixel);
return
true
;
}
//绘画图像
bool
CImageEx::DrawImage( CDC * pDC,
INT
nXPos,
INT
nYPos,
INT
nDestWidth,
INT
nDestHeight )
{
//加载判断
ASSERT(m_pImage!=NULL);
if
(m_pImage==NULL)
return
false
;
//创建屏幕
ASSERT(pDC!=NULL);
Graphics graphics(pDC->GetSafeHdc());
//构造位置
RectF rcDrawRect;
rcDrawRect.X=(REAL)nXPos;
rcDrawRect.Y=(REAL)nYPos;
rcDrawRect.Width=(REAL)nDestWidth;
rcDrawRect.Height=(REAL)nDestHeight;
//绘画图像
graphics.DrawImage(m_pImage,rcDrawRect,0,0,(REAL)GetWidth(),(REAL)GetHeight(),UnitPixel);
return
true
;
}
//绘画图像
bool
CImageEx::DrawImage(CDC * pDC,
INT
nXDest,
INT
nYDest,
INT
nDestWidth,
INT
nDestHeight,
INT
nXScr,
INT
nYSrc)
{
//加载判断
ASSERT(m_pImage!=NULL);
if
(m_pImage==NULL)
return
false
;
//创建屏幕
ASSERT(pDC!=NULL);
Graphics graphics(pDC->GetSafeHdc());
//构造位置
RectF rcDrawRect;
rcDrawRect.X=(REAL)nXDest;
rcDrawRect.Y=(REAL)nYDest;
rcDrawRect.Width=(REAL)nDestWidth;
rcDrawRect.Height=(REAL)nDestHeight;
//绘画图像
graphics.DrawImage(m_pImage,rcDrawRect,(REAL)nXScr,(REAL)nYSrc,(REAL)nDestWidth,(REAL)nDestHeight,UnitPixel);
return
true
;
}
//绘画图像
bool
CImageEx::DrawImage(CDC * pDC,
INT
nXDest,
INT
nYDest,
INT
nDestWidth,
INT
nDestHeight,
INT
nXScr,
INT
nYSrc,
INT
nSrcWidth,
INT
nSrcHeight)
{
//加载判断
ASSERT(m_pImage!=NULL);
if
(m_pImage==NULL)
return
false
;
//创建屏幕
ASSERT(pDC!=NULL);
Graphics graphics(pDC->GetSafeHdc());
//构造位置
RectF rcDrawRect;
rcDrawRect.X=(REAL)nXDest;
rcDrawRect.Y=(REAL)nYDest;
rcDrawRect.Width=(REAL)nDestWidth;
rcDrawRect.Height=(REAL)nDestHeight;
//绘画图像
graphics.DrawImage(m_pImage,rcDrawRect,(REAL)nXScr,(REAL)nYSrc,(REAL)nSrcWidth,(REAL)nSrcHeight,UnitPixel);
return
true
;
}
//混合绘画
bool
CImageEx::AlphaDrawImage(CDC * pDestDC,
INT
xDest,
INT
yDest,
BYTE
cbAlphaDepth)
{
//透明绘画
AlphaDrawImage(pDestDC,xDest,yDest,GetWidth(),GetHeight(),0,0,cbAlphaDepth);
return
true
;
}
//混合绘画
bool
CImageEx::AlphaDrawImage(CDC * pDestDC,
INT
xDest,
INT
yDest,
INT
cxDest,
INT
cyDest,
INT
xSrc,
INT
ySrc,
BYTE
cbAlphaDepth)
{
//加载判断
ASSERT(m_pImage!=NULL);
if
(m_pImage==NULL)
return
false
;
//创建屏幕
ASSERT(pDestDC!=NULL);
Graphics graphics(pDestDC->GetSafeHdc());
//构造位置
RectF rcDrawRect;
rcDrawRect.X=(REAL)xDest;
rcDrawRect.Y=(REAL)yDest;
rcDrawRect.Width=(REAL)cxDest;
rcDrawRect.Height=(REAL)cyDest;
//透明矩阵
ColorMatrix Matrix=
{
1.0f,0.0f,0.0f,0.0f,0.0f,
0.0f,1.0f,0.0f,0.0f,0.0f,
0.0f,0.0f,1.0f,0.0f,0.0f,
0.0f,0.0f,0.0f,cbAlphaDepth/255.0f,0.0f,
0.0f,0.0f,0.0f,0.0f,1.0f
};
//设置属性
ImageAttributes Attributes;
Attributes.SetColorMatrix(&Matrix,ColorMatrixFlagsDefault,ColorAdjustTypeBitmap);
//绘画图像
graphics.DrawImage(m_pImage,rcDrawRect,(REAL)xSrc,(REAL)ySrc,(REAL)cxDest,(REAL)cyDest,UnitPixel,&Attributes);
return
true
;
}
//混合绘画
bool
CImageEx::AlphaDrawImage(CDC * pDestDC,
INT
xDest,
INT
yDest,
INT
cxDest,
INT
cyDest,
INT
xSrc,
INT
ySrc,
INT
cxSrc,
INT
cySrc,
BYTE
cbAlphaDepth)
{
//创建缓冲
if
((cxDest!=cxSrc)||(cyDest!=cySrc))
{
//加载判断
ASSERT(m_pImage!=NULL);
if
(m_pImage==NULL)
return
false
;
//创建屏幕
ASSERT(pDestDC!=NULL);
Graphics graphics(pDestDC->GetSafeHdc());
//构造位置
RectF rcDrawRect;
rcDrawRect.X=(REAL)xDest;
rcDrawRect.Y=(REAL)yDest;
rcDrawRect.Width=(REAL)cxDest;
rcDrawRect.Height=(REAL)cyDest;
//透明矩阵
ColorMatrix Matrix=
{
1.0f,0.0f,0.0f,0.0f,0.0f,
0.0f,1.0f,0.0f,0.0f,0.0f,
0.0f,0.0f,1.0f,0.0f,0.0f,
0.0f,0.0f,0.0f,cbAlphaDepth/255.0f,0.0f,
0.0f,0.0f,0.0f,0.0f,1.0f
};
//设置属性
ImageAttributes Attributes;
Attributes.SetColorMatrix(&Matrix,ColorMatrixFlagsDefault,ColorAdjustTypeBitmap);
//绘画图像
graphics.DrawImage(m_pImage,rcDrawRect,(REAL)xSrc,(REAL)ySrc,(REAL)cxSrc,(REAL)cySrc,UnitPixel,&Attributes);
}
else
{
//普通调用
AlphaDrawImage(pDestDC,xDest,yDest,cxDest,cyDest,xSrc,ySrc,cbAlphaDepth);
}
return
true
;
}
//
|
上面代码咱们用到了Gdiplus里面的Image类,对Gdiplus有兴趣的朋友可以翻翻Gdiplus的资料,上一篇,我们写了一个CImage的派生类,其实这个类里面也用到了Gdiplus,当然你可以查看CImage 的源码,源码在你vs安装目录下Microsoft Visual Studio 9.0\VC\atlmfc\include\atlimage.h中,如果你的 编译器是vs2003,对应的就是就是Microsoft Visual Studio 7.1,vs2005就是Microsoft Visual Studio 8.0
言归正传, 开始加载窗口的背景图片,为了调用方便,我们写两个函数
1
2
3
4
|
//背景图片
bool
LoadBackSkin(
LPCTSTR
pszResourcePath);
//加载图片
bool
LoadBackSkin(
HINSTANCE
hInstance,
LPCTSTR
pszResourceName);
|
先在CDialogEx类中添加图片变量
1
|
CImageEx m_ImageBack;
|
完成上面写的两个函数
1
2
3
4
5
6
7
8
9
|
bool
CDialogEx::LoadBackSkin(
LPCTSTR
pszResourcePath )
{
return
m_ImageBack.LoadImage(pszResourcePath);
}
bool
CDialogEx::LoadBackSkin(
HINSTANCE
hInstance,
LPCTSTR
pszResourceName )
{
return
m_ImageBack.LoadImage(hInstance,pszResourceName);
}
|
添加WM_PAINT消息,
1
2
3
4
5
6
7
8
9
10
11
12
|
void
CDialogEx::OnPaint()
{
CPaintDC dc(
this
);
// device context for painting
CRect rcClient;
GetClientRect(&rcClient);
if
( !m_ImageBack.IsNull() )
{
m_ImageBack.DrawImage(&dc,0,0,rcClient.Width(),rcClient.Height());
}
}
|
我们返回我们的CMusicDlg,在OnInitDialog函数中,我们调用刚才写的函数
1
2
|
LoadBackSkin(
"d:\\windows\\Desktop\\BACK_VIEW.jpg"
);
SetWindowPos(NULL,0,0,310,490,SWP_NOMOVE);
|
当然你也可以像上节那样,从内存加载图片资源,运行一下,窗口没有了标题栏也没有了边框, 整个窗口贴了一张图片,那接下来就是往窗口上添加按钮了,之前我跟大家说过本节窗口去除了非客户区的概念,因此我们就可以在客户区中任意放置按钮。这样也就好办了,我们继承CButton
打开类向导, 创建mfc类,继承CButton,取名CButtonEx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
#pragma once
// CButtonEx
#include "ImageEx.h"
class
CButtonEx :
public
CButton
{
DECLARE_DYNAMIC(CButtonEx)
//变量定义
protected
:
bool
m_bExpand;
//是否拉伸
bool
m_bHovering;
//盘旋标志
COLORREF
m_crTextColor;
//字体颜色
CImageEx m_ImageBack;
//按钮位图
//函数定义
public
:
//构造函数
CButtonEx();
//析构函数
virtual
~CButtonEx();
//重载函数
protected
:
//控件子类化
virtual
void
PreSubclassWindow();
//界面绘画函数
virtual
void
DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
//设置函数
public
:
//调整位置
bool
FixButtonSize();
//设置颜色
bool
SetTextColor(
COLORREF
crTextColor);
//加载位图
bool
SetButtonImage(
LPCTSTR
pszFileName,
bool
bExpandImage=
false
);
//加载位图
bool
SetButtonImage(
HINSTANCE
hInstance,
LPCTSTR
pszResourceName,
bool
bExpandImage=
false
);
//消息函数
protected
:
//建立消息
afx_msg
int
OnCreate(LPCREATESTRUCT lpCreateStruct);
//鼠标移动
afx_msg
void
OnMouseMove(
UINT
nFlags, CPoint point);
//鼠标离开
afx_msg
LRESULT
OnMouseLeave(
WPARAM
wparam,
LPARAM
lparam);
//重绘背景
afx_msg
BOOL
OnEraseBkgnd(CDC* pDC);
protected
:
DECLARE_MESSAGE_MAP()
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
|
// ButtonEx.cpp : 实现文件
//
#include "stdafx.h"
#include "Music.h"
#include "ButtonEx.h"
// CButtonEx
IMPLEMENT_DYNAMIC(CButtonEx, CButton)
CButtonEx::CButtonEx()
{
m_bExpand=
true
;
m_bHovering=
false
;
m_crTextColor = RGB(0,0,0);
}
CButtonEx::~CButtonEx()
{
}
BEGIN_MESSAGE_MAP(CButtonEx, CButton)
ON_WM_CREATE()
ON_WM_MOUSEMOVE()
ON_MESSAGE(WM_MOUSELEAVE,OnMouseLeave)
ON_WM_ERASEBKGND()
END_MESSAGE_MAP()
// CButtonEx 消息处理程序
//对象附加到现有窗口
void
CButtonEx::PreSubclassWindow()
{
__super::PreSubclassWindow();
SetButtonStyle(GetButtonStyle()|BS_OWNERDRAW);
}
//建立消息
int
CButtonEx::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if
(__super::OnCreate(lpCreateStruct)==-1)
return
-1;
SetButtonStyle(GetButtonStyle()|BS_OWNERDRAW);
return
0;
}
//加载位图
bool
CButtonEx::SetButtonImage(
LPCTSTR
pszFileName,
bool
bExpandImage)
{
//效验参数
ASSERT(pszFileName);
if
(pszFileName==NULL)
return
false
;
//加载位图
m_bExpand=bExpandImage;
m_ImageBack.LoadImage(pszFileName);
//调整位置
if
(m_bExpand==
false
) FixButtonSize();
if
(GetSafeHwnd()) Invalidate(FALSE);
return
true
;
}
//加载位图
bool
CButtonEx::SetButtonImage(
HINSTANCE
hInstance,
LPCTSTR
pszResourceName,
bool
bExpandImage)
{
//加载位图
ASSERT(pszResourceName!=0);
if
(pszResourceName==NULL)
return
false
;
m_bExpand=bExpandImage;
m_ImageBack.LoadImage(hInstance,pszResourceName);
//调整位置
if
(m_bExpand==
false
) FixButtonSize();
if
(GetSafeHwnd()) Invalidate(FALSE);
return
true
;
}
//调整位置
bool
CButtonEx::FixButtonSize()
{
if
(!m_ImageBack.IsNull()&&GetSafeHwnd())
{
SetWindowPos(NULL,0,0,m_ImageBack.GetWidth()/4,m_ImageBack.GetHeight(),SWP_NOMOVE);
return
true
;
}
return
false
;
}
//鼠标移动消息
void
CButtonEx::OnMouseMove(
UINT
nFlags, CPoint point)
{
if
(m_bHovering==
false
)
{
//注册消息
m_bHovering=
true
;
Invalidate(FALSE);
TRACKMOUSEEVENT TrackMouseEvent;
TrackMouseEvent.cbSize=
sizeof
(TrackMouseEvent);
TrackMouseEvent.dwFlags=TME_LEAVE;
TrackMouseEvent.hwndTrack=GetSafeHwnd();
TrackMouseEvent.dwHoverTime=HOVER_DEFAULT;
_TrackMouseEvent(&TrackMouseEvent);
}
__super::OnMouseMove(nFlags, point);
}
//鼠标离开消息
LRESULT
CButtonEx::OnMouseLeave(
WPARAM
wparam,
LPARAM
lparam)
{
m_bHovering=
false
;
Invalidate(FALSE);
return
0;
}
//设置颜色
bool
CButtonEx::SetTextColor(
COLORREF
crTextColor)
{
m_crTextColor=crTextColor;
if
(GetSafeHwnd()) Invalidate(FALSE);
return
true
;
}
//界面绘画函数
void
CButtonEx::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
//定义变量
CRect rcClient;
GetClientRect(&rcClient);
bool
bDisable=((lpDrawItemStruct->itemState&ODS_DISABLED)!=0);
bool
bButtonDown=((lpDrawItemStruct->itemState&ODS_SELECTED)!=0);
CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
//获取文字
CString strText;
GetWindowText(strText);
//绘制父窗口背景
CRect rcWindow;
CClientDC clDC(GetParent());
GetWindowRect(rcWindow);
GetParent()->ScreenToClient(rcWindow);
CDC dcParent;
CBitmap bmp,*pOldBitmap;
dcParent.CreateCompatibleDC(&clDC);
bmp.CreateCompatibleBitmap(&clDC,rcClient.Width(),rcClient.Height());
pOldBitmap = dcParent.SelectObject(&bmp);
dcParent.BitBlt(0,0,rcClient.Width(),rcClient.Height(),&clDC,rcWindow.left,rcWindow.top,SRCCOPY);
bmp.DeleteObject();
//加载背景图
if
( !m_ImageBack.IsNull() )
{
//计算位图位置
int
nWidth=m_ImageBack.GetWidth()/4,nDrawPos=0;
if
(bDisable) nDrawPos=nWidth*3;
else
if
(bButtonDown) nDrawPos=nWidth*2;
else
if
(m_bHovering) nDrawPos=nWidth*1;
//绘画背景图
if
(m_bExpand==
false
) m_ImageBack.DrawImage(pDC,0,0,rcClient.Width(),rcClient.Height(),nDrawPos,0);
else
m_ImageBack.DrawImage(pDC,0,0,rcClient.Width(),rcClient.Height(),nDrawPos,0,nWidth,m_ImageBack.GetHeight());
}
else
{
//绘画默认界面
pDC->FillSolidRect(&rcClient,GetSysColor(COLOR_BTNFACE));
if
(bButtonDown) pDC->Draw3dRect(&rcClient,GetSysColor(COLOR_WINDOWFRAME),GetSysColor(COLOR_3DHILIGHT));
else
pDC->Draw3dRect(&rcClient,GetSysColor(COLOR_3DHILIGHT),GetSysColor(COLOR_WINDOWFRAME));
}
//绘画字体
rcClient.top+=1;
pDC->SetBkMode(TRANSPARENT);
if
(bDisable) pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT));
else
pDC->SetTextColor(m_crTextColor);
pDC->DrawText(strText,strText.GetLength(),rcClient,DT_CENTER|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS);
return
;
}
BOOL
CButtonEx::OnEraseBkgnd(CDC* pDC)
{
return
TRUE;
// return CButton::OnEraseBkgnd(pDC);
}
|
这里大家就先根据我这里写的做,等着我在控件应用那一章中会详细的介绍一些控件的重绘方法和注意事项,这里大家暂时不用去钻研为什么,只是这里需要注意一点,虽然这里我们写的CButtonEx可以加载png格式的资源,但是这并不表示就支持png的半透明效果,这里注意一下, 以后会讲到。
返回CDialogEx,添加按钮变量
1
2
3
4
5
|
//控件变量
protected
:
CButtonEx m_btClose;
//关闭按钮
CButtonEx m_btMax;
//最大化按钮
CButtonEx m_btMin;
//最小化按钮
|
同时添加一个变量, 此变量用来判断按钮是否已经Create,别忘了初始化为false
1
2
3
|
//基本变量
protected
:
bool
m_bIsInit;
//是否已经初始化
|
打开OnInitDialog函数,创建这三个按钮控件,按钮都需要一个ID去响应事件,拖到文件最上面,添加自定义消息宏
1
2
|
#define IDC_WND_MAX WM_USER+0x01
#define IDC_WND_MIN WM_USER+0x02
|
1
2
3
4
5
6
7
8
9
10
11
12
|
CRect rcControl(0,0,0,0);
m_btClose.Create(NULL,WS_CHILD|WS_VISIBLE,rcControl,
this
,IDCANCEL);
m_btClose.SetButtonImage(AfxGetInstanceHandle(),TEXT(
"CLOSESSIZE"
));
m_btMax.Create(NULL,WS_CHILD|WS_VISIBLE,rcControl,
this
,IDC_WND_MAX);
m_btMax.SetButtonImage(AfxGetInstanceHandle(),TEXT(
"MAXSSIZE"
));
m_btMin.Create(NULL,WS_CHILD|WS_VISIBLE,rcControl,
this
,IDC_WND_MIN);
m_btMin.SetButtonImage(AfxGetInstanceHandle(),TEXT(
"MINSSIZE"
));
m_bIsInit =
true
;
|
在这里,你也发现了,我设置按钮大小的时候,指定了空矩形,这样一来按钮就不会显示出来了,上一节我们也说过,这三个按钮是会跟着窗口大小变化的,所以还是添加WM_SIZE消息,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
void
CDialogEx::OnSize(
UINT
nType,
int
cx,
int
cy)
{
CDialog::OnSize(nType, cx, cy);
//如果按钮还没创建出来就走到下面,要出大事的
if
( !m_bIsInit )
return
;
//变量定义
const
UINT
uFlags=SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOCOPYBITS|SWP_NOSIZE;
//锁定屏幕
LockWindowUpdate();
//移动控件
HDWP
hDwp=BeginDeferWindowPos(32);
CRect rcButton;
m_btClose.GetWindowRect(&rcButton);
DeferWindowPos(hDwp,m_btClose,NULL,cx-rcButton.Width()-2,2,0,0,uFlags);
DeferWindowPos(hDwp,m_btMax,NULL,cx-rcButton.Width()*2-2,2,0,0,uFlags);
DeferWindowPos(hDwp,m_btMin,NULL,cx-rcButton.Width()*3-2,2,0,0,uFlags);
EndDeferWindowPos(hDwp);
//重画界面
Invalidate(FALSE);
UpdateWindow();
//解除锁定
UnlockWindowUpdate();
}
|
到现在为止,运行一下看看效果,基本的样子已经完成,但是按钮没有事件响应,添加事件响应函数
打开属性 面板,找到OnCommand
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
BOOL
CDialogEx::OnCommand(
WPARAM
wParam,
LPARAM
lParam)
{
switch
(LOWORD(wParam))
{
case
IDC_WND_MAX:
//最大化消息
{
//如果子类没有自己的操作就执行默认操作
if
( !OnMaxSize())
{
static
CRect rcClient(0,0,0,0);
if
( m_bIsZoomed )
{
MoveWindow(&rcClient);
m_bIsZoomed =
false
;
}
else
{
GetWindowRect(&rcClient);
CRect rc;
SystemParametersInfo(SPI_GETWORKAREA,0,&rc,0);
MoveWindow(&rc);
m_bIsZoomed =
true
;
}
}
break
;
}
case
IDC_WND_MIN:
//最小化消息
{
ShowWindow(SW_MINIMIZE);
break
;
}
}
return
CDialog::OnCommand(wParam, lParam);
}
|
在这里,你可以发现了不同的地方,上一节,我们最大化的时候用到的是SendMessage函数,为什么这里却要自己保存原先窗口大小,自己设置窗口大小呢,甚至你会说用ShowWindow(SW_MAX),这么一句话不就可以么,其实,这样做的确可以,但是由于我们去除了窗口固有的风格样式,这里采用上面的说法,最大化就等于全屏,会盖住系统的任务栏,这应该是你不希望看见的,所以我们这里并没有直接采用API的方式去做。
上面别忘记在头文件添加,并初始化为false
1
|
bool
m_bIsZoomed;
//是否最大化
|
在运行一下看看效果,效果和上一篇讲的效果一样了,但是仍然有两点没有做到
1:窗口不可以拖动
2:窗口不可随意拉伸大小
先解决窗口不可以拖动,添加WM_LBUTTONDOWN消息,
1
2
3
4
5
6
7
8
9
10
11
12
13
|
void
CDialogEx::OnLButtonDown(
UINT
nFlags, CPoint point)
{
//模拟标题
if
(!m_bIsZoomed)
{
//模拟标题
PostMessage(WM_NCLBUTTONDOWN,HTCAPTION,MAKELPARAM(point.x,point.y));
return
;
}
CDialog::OnLButtonDown(nFlags, point);
}
|
这里我们采用的是向系统发送诱骗消息,骗取系统,这个窗口是标题栏,可以任意拖动移动
再解决第二个问题,拉伸窗口,可能有的朋友做过无边框的窗口拉伸,其实原理很简单,只需要处理WM_NCHITTEST和WM_NCLBUTTONDOWN
我们先添加一个变量,是否可以拉伸,默认初始化为true即可,毕竟窗口拉伸是窗口基本的属性之一
1
|
bool
m_bExtrude;
//是否可以拉伸
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
LRESULT
CDialogEx::OnNcHitTest(CPoint point)
{
if
( m_bExtrude )
{
CRect rcWindow;
GetWindowRect(&rcWindow);
if
((point.x <= rcWindow.left+BORDERWIDTH) && (point.y>BORDERWIDTH) && (point.y<rcWindow.bottom-BORDERWIDTH*2) )
return
HTLEFT;
else
if
((point.x >= rcWindow.right-BORDERWIDTH) && (point.y>BORDERWIDTH) && (point.y<rcWindow.bottom-BORDERWIDTH*2) )
return
HTRIGHT;
else
if
((point.y <= rcWindow.top+BORDERWIDTH) && (point.x>BORDERWIDTH) && (point.x<rcWindow.right-BORDERWIDTH*2))
return
HTTOP;
else
if
((point.y >= rcWindow.bottom-BORDERWIDTH) && (point.x>BORDERWIDTH) && (point.x<rcWindow.right-BORDERWIDTH*2))
return
HTBOTTOM;
else
if
((point.x <= rcWindow.left+BORDERWIDTH*2) && (point.y <= rcWindow.top+BORDERWIDTH*2))
return
HTTOPLEFT;
else
if
((point.x >= rcWindow.right-BORDERWIDTH*2) && (point.y <= rcWindow.top+BORDERWIDTH*2))
return
HTTOPRIGHT;
else
if
((point.x <= rcWindow.left+BORDERWIDTH*2) && (point.y >= rcWindow.bottom-BORDERWIDTH*2))
return
HTBOTTOMLEFT;
else
if
((point.x >= rcWindow.right-BORDERWIDTH*2) && (point.y >= rcWindow.bottom-BORDERWIDTH*2))
return
HTBOTTOMRIGHT;
else
return
CWnd::OnNcHitTest(point);
return
0;
}
else
return
CDialog::OnNcHitTest(point);
}
void
CDialogEx::OnNcLButtonDown(
UINT
nHitTest, CPoint point)
{
if
( m_bExtrude )
{
if
(nHitTest == HTTOP)
SendMessage( WM_SYSCOMMAND, SC_SIZE | WMSZ_TOP, MAKELPARAM(point.x, point.y));
else
if
(nHitTest == HTBOTTOM)
SendMessage( WM_SYSCOMMAND, SC_SIZE | WMSZ_BOTTOM, MAKELPARAM(point.x, point.y));
else
if
(nHitTest == HTLEFT)
SendMessage( WM_SYSCOMMAND, SC_SIZE | WMSZ_LEFT, MAKELPARAM(point.x, point.y));
else
if
(nHitTest == HTRIGHT)
SendMessage( WM_SYSCOMMAND, SC_SIZE | WMSZ_RIGHT, MAKELPARAM(point.x, point.y));
else
if
(nHitTest == HTTOPLEFT)
SendMessage( WM_SYSCOMMAND, SC_SIZE | WMSZ_TOPLEFT, MAKELPARAM(point.x, point.y));
else
if
(nHitTest == HTTOPRIGHT)
SendMessage( WM_SYSCOMMAND, SC_SIZE | WMSZ_TOPRIGHT, MAKELPARAM(point.x, point.y));
else
if
(nHitTest == HTBOTTOMLEFT)
SendMessage( WM_SYSCOMMAND, SC_SIZE | WMSZ_BOTTOMLEFT, MAKELPARAM(point.x, point.y));
else
if
(nHitTest == HTBOTTOMRIGHT)
SendMessage(WM_SYSCOMMAND, SC_SIZE | WMSZ_BOTTOMRIGHT, MAKELPARAM(point.x, point.y));
else
if
(nHitTest==HTCAPTION)
SendMessage(WM_SYSCOMMAND, SC_MOVE | 4, MAKELPARAM(point.x, point.y));
}
CDialog::OnNcLButtonDown(nHitTest, point);
}
|
好,到现在为止,和上一节一模一样的窗口就完成了,下一节,我们把播放器的界面填充完成。下一节我们再见