上次我们学习了如何美化对话框的界面,这次我们为上次的对话框添加两个按钮,一个是关闭按钮,另一个是最小化按钮,好,现在我们先看一下效果:
是不是很难看,因为我们的对话框美化了,所以我们的按钮也要美化,因为采用贴图的方式来美化,所以,我先给出这两个按钮的PNG格式的图片,该图片支持透明色,具体如下:
关闭按钮效果图:
最小化按钮效果图:
这两张效果图是我自己从网上找的,可能不是很合适,但是用来教学,完全没有问题,它们的尺寸都是108*21,每张图片都有四个小图片,第一张和第四张小图片都是透明的,所以看不见效果,我们使用这两张图片来完成按钮的美化,每张图片从左向右有四张小图片,我们只用前三张,分别对应默认状态,焦点状态,按下状态。
下面,我们来说一下如何美化按钮?
第1步,我们先在对话框上放置两个按钮,一个是关闭按钮,另一个是最小化按钮,它们对应的ID分别是IDC_BUTTON_CLOSE和IDC_BUTTON_MIN,然后将我们的按钮设置为自绘制模式,方法如下:
选择按钮,右键属性,在属性列表中找到Owner Draw选项,将其设置为True,效果图如下:
再为它们添加两个成员变量,具体如下:
1
2
|
CButton m_btnClose; CButton m_btnMin; |
第2步,我们新建一个类,继承自CButton,我们取名为CMyButton,为其添加3个成员变量,分别如下:
1
2
3
4
5
6
|
//按钮背景图像 CImage m_imgButton; //按钮png路径,包括焦点,正常,按下3个状态 CString m_strImgPath; //父窗口背景图片背景路径,透明png需要使用 CString m_strImgParentPath; |
第3步,我们为CMyButton添加3个成员函数,分别如下:
1
2
3
4
5
6
|
//设置按钮背景图片路径,父窗口背景图片路径 void SetImagePath(CString strImgPath, CString strParentImgPath); //初始化按钮,主要是调整按钮的位置,处理透明色 bool InitMyButton( int nX /*左上角X坐标*/ , int nY /*左上角Y坐标*/ , int nW /*图像宽*/ , int nH /*图像高*/ , bool bIsPng /*是否是PNG图片*/ ); //自绘制函数 void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct); |
CMyButton的声明最终如下:
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
|
class CMyButton : public CButton { DECLARE_DYNAMIC(CMyButton) public : CMyButton(); virtual ~CMyButton(); //按钮背景图像 CImage m_imgButton; //按钮png路径,包括焦点,正常,按下3个状态 CString m_strImgPath; //父窗口背景图片背景路径,透明png需要使用 CString m_strImgParentPath; //设置按钮背景图片路径,父窗口背景图片路径 void SetImagePath(CString strImgPath, CString strParentImgPath); //初始化按钮,主要是调整按钮的位置,处理透明色 bool InitMyButton( int nX /*左上角X坐标*/ , int nY /*左上角Y坐标*/ , int nW /*图像宽*/ , int nH /*图像高*/ , bool bIsPng /*是否是PNG图片*/ ); //自绘制函数 void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct); protected : DECLARE_MESSAGE_MAP() }; |
第4步,我们实现SetImagePath函数,它的功能是为按钮背景图片和父窗口背景图片成员函数初始化,具体如下:
1
2
3
4
5
|
void CMyButton::SetImagePath(CString strImgPath, CString strParentImgPath) { m_strImgPath = strImgPath; m_strImgParentPath = strParentImgPath; } |
第5步,我们实现InitMyButton函数,它的功能是调整按钮在对话框上的位置,其中的参数代表该按钮在父窗口的左上角X坐标,Y坐标,宽度,高度,最后一个参数是为PNG格式图片准备的,如果是PNG带透明色的图片,需要对它进行特殊处理,具体定义如下:
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
|
bool CMyButton::InitMyButton( int nX, int nY, int nW, int nH, bool bIsPng) { HRESULT hr = 0; if (m_strImgPath.IsEmpty()) return false ; hr = m_imgButton.Load(m_strImgPath); if (FAILED(hr)) return false ; if (bIsPng) { if (m_imgButton.GetBPP() == 32) { int i = 0; int j = 0; for (i = 0; i < m_imgButton.GetWidth(); i++) { for (j = 0; j < m_imgButton.GetHeight(); j++) { byte * pbyte = (byte *)m_imgButton.GetPixelAddress(i, j); pbyte[0] = pbyte[0] * pbyte[3] / 255; pbyte[1] = pbyte[1] * pbyte[3] / 255; pbyte[2] = pbyte[2] * pbyte[3] / 255; } } } } MoveWindow(nX,nY,nW,nH); return true ; } |
其中MoveWindow函数是用来调整按钮位置的函数,其中的参数分别代表其在父窗口的左上角X坐标,左上角Y坐标,宽度,高度。
第6步,我们实现DrawItem函数,它是美化Button的核心函数,当我们将Button设置为自绘制后,每次按钮需要刷新,重新绘制的时候,MFC框架会调用它的DrawItem函数,在这个函数中,我们可以根据按钮当前的状态为其贴上相应的背景图。当我们按钮按钮的时候,为其贴上被按下的背景图;当我们的按钮获取焦点的时候,为其贴上获取焦点的背景图;当我们的按钮没有焦点,我们为其贴上默认的背景图片,它们对应的位置前面已经说过。为了避免闪烁,我们采用双缓冲的方式,具体代码如下:
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
|
void CMyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { if (!lpDrawItemStruct) return ; HDC hMemDC; HBITMAP bmpMem; HGDIOBJ hOldObj; bmpMem = CreateCompatibleBitmap(lpDrawItemStruct->hDC, lpDrawItemStruct->rcItem.right - lpDrawItemStruct->rcItem.left, lpDrawItemStruct->rcItem.bottom - lpDrawItemStruct->rcItem.top); if (!bmpMem) return ; hMemDC = CreateCompatibleDC(lpDrawItemStruct->hDC); if (!hMemDC) { if (bmpMem) { ::DeleteObject(bmpMem); bmpMem = NULL; } return ; } hOldObj = ::SelectObject(hMemDC, bmpMem); RECT rectTmp = { 0 }; rectTmp = lpDrawItemStruct->rcItem; MapWindowPoints(GetParent(), &rectTmp); int nW = lpDrawItemStruct->rcItem.right - lpDrawItemStruct->rcItem.left; int nH = lpDrawItemStruct->rcItem.bottom - lpDrawItemStruct->rcItem.top; if (lpDrawItemStruct->itemState & ODS_SELECTED) { //按钮被选择 m_imgButton.BitBlt(hMemDC, 0, 0, nW, nH, nW*2, 0, SRCCOPY); } else if (lpDrawItemStruct->itemState & ODS_FOCUS) { //焦点状态 m_imgButton.BitBlt(hMemDC, 0, 0, nW, nH, nW, 0, SRCCOPY); } else { //默认状态 CImage imgParent; imgParent.Load(m_strImgParentPath); imgParent.Draw(hMemDC, 0, 0, nW, nH, rectTmp.left, rectTmp.top, nW, nH); m_imgButton.AlphaBlend(hMemDC, 0, 0, nW, nH, 0, 0, nW, nH); imgParent.Destroy(); } ::BitBlt(lpDrawItemStruct->hDC, 0, 0, nW, nH, hMemDC, 0, 0, SRCCOPY); SelectObject(hMemDC, hOldObj); if (bmpMem) { ::DeleteObject(bmpMem); bmpMem = NULL; } if (hMemDC) { ::DeleteDC(hMemDC); hMemDC = NULL; } return ; } |
这里我们重点说一下默认状态的背景图,因为它是透明的,并且我们采用的是双缓冲,所以,为了避免最终透明色变成黑色,我们先在内存DC上贴上按钮在父窗口位置的背景图,这样可以解决透明色变成黑色的问题,如果你采用GDI+,就不用这么做,但是我们采用的是GDI。
第7步,用CMyButton替代对话框头文件中的CButton。
第8步,在对话框的InitDialog中,对两个按钮进行初始化,具体如下:
1
2
3
4
|
m_btnMin.SetImagePath(_T( "./res/btn_min.png" ), _T( "./res/Background.png" )); m_btnMin.InitMyButton(516, 8, 27, 21, true ); m_btnClose.SetImagePath(_T( "./res/btn_close.png" ),_T( "./res/Background.png" )); m_btnClose.InitMyButton(545,8,27,21, true ); |
第9步,编译程序,最终效果图如下:
今天,我们已经为它添加了最小化,关闭按钮,下次,我们为其添加编辑框!
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。