【MFC自绘窗口】窗口设计第三课

前面我们讲述了重绘窗口的一般方法,但是这种方法有很多的局限性,比如,像360杀毒,QQ这种支持背景图片样式的,由于非客户区的原因,操作起来就会很麻烦,这次,我在讲述另外一种重绘窗口的方法,通过这种方法可以定制出和QQ360杀毒等这一类软件一样的界面。原理也很简单,就是去除窗口的非客户区,实现无边框,无标题栏的窗口,然后自己去模拟窗口的非客户区和客户区,这种方法的不好之处正是他要自己去计算客户区的大小,下面我们就开始讲述实现过程,本次实现一个简易的音乐播放器界面。


设计思路:

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