【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
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_NCHITTESTWM_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);
}

好,到现在为止,和上一节一模一样的窗口就完成了,下一节,我们把播放器的界面填充完成。下一节我们再见

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值