[MFC] 手动美化 MFC 窗体

[MFC] 手动美化 MFC 窗体

去除边框

1.把窗口边框改成无边框:

但只这样做会导致无法拖动窗口。

2.在类向导中添加这个消息:OnHcHitTest

3.修改这个消息处理函数的返回值为:HTCAPTION

添加背景图片

1.准备一张 bmp 格式的图片(其他格式我没有实验,可能会有问题),在项目中添加资源,选择 BitMap ,导入:

2.在 OnPaint() 函数中添加如下代码:

// 上面还有一部分代码,是 vs 自动生成的

else

{

//CDialogEx::OnPaint(); // 这是原本的,注释掉

/*

从这里开始是我们自己的代码 ---------------

*/

CPaintDC dc(this);

CRect rect;

GetClientRect(&rect); // 获取对话框长宽

CDC dcBmp; // 定义并创建一个内存设备环境

dcBmp.CreateCompatibleDC(&dc); // 创建兼容性DC

CBitmap bmpBackground;

bmpBackground.LoadBitmap(IDB_BITMAP1); // 载入资源中图片

BITMAP m_bitmap; // 图片变量

bmpBackground.GetBitmap(&m_bitmap); // 将图片载入位图中

//将位图选入临时内存设备环境

CBitmap* pbmpOld = dcBmp.SelectObject(&bmpBackground);

//调用函数显示图片StretchBlt显示形状可变

dc.SetStretchBltMode(COLORONCOLOR); // 防止 bmp 图片压缩后失真

dc.StretchBlt(0, 0, rect.Width(), rect.Height(), &dcBmp, 0, 0, m_bitmap.bmWidth, m_bitmap.bmHeight, SRCCOPY);

}

注意:背景图片在窗口拉伸时会失真,但配合无边框使用时不需要在意,因为本身无边框窗体不支持拉伸窗口。

添加背景颜色

这个没有添加背景图片好用,但还是记录一下吧。 1.类向导里添加消息处理函数 : WM_CTLCOLOR 2.在 xxxDlg.h 的 protected 里添加成员变量:CBrush m_brush; 3.在 WM_CTLCOLOR 的处理函数 OnCtlColor() 中 ,修改返回值为 m_brush

HBRUSH Ctestmfc2Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)

{

HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);

// TODO: 在此更改 DC 的任何特性

// TODO: 如果默认的不是所需画笔,则返回另一个画笔

return /*hbr*/m_brush;

}

4.在 OnInitDialog() 中添加代码,绘制背景颜色:

m_brush.CreateSolidBrush(RGB(255, 255, 102));

给按钮添加背景图片

比起 bmp 图片,显然按钮必须要用 png 的透明图片更加好看,但是 MFC 对 png 的支持并不太友好,我找了很久,终于在这篇文章的启发下完成了这个功能: https://www.codeproject.com/Articles/26887/A-user-draw-button-that-supports-PNG-files-with-tr

https://github.com/JustLoveI/GdipButton 这里的代码更新,但是我没有调通,所以我用的时上面连接里的代码。

以防链接失效,这里记录下具体实现过程,不愿看英文的人也可以直接看我下面写的。

1.首先需要几个文件 CGdiPlusBitmap.h

#pragma once

class CGdiPlusBitmap

{

public:

Gdiplus::Bitmap* m_pBitmap;

public:

CGdiPlusBitmap() { m_pBitmap = NULL; }

CGdiPlusBitmap(LPCWSTR pFile) { m_pBitmap = NULL; Load(pFile); }

virtual ~CGdiPlusBitmap() { Empty(); }

void Empty() { delete m_pBitmap; m_pBitmap = NULL; }

bool Load(LPCWSTR pFile)

{

Empty();

m_pBitmap = Gdiplus::Bitmap::FromFile(pFile);

return m_pBitmap->GetLastStatus() == Gdiplus::Ok;

}

operator Gdiplus::Bitmap*() const { return m_pBitmap; }

};

class CGdiPlusBitmapResource : public CGdiPlusBitmap

{

protected:

HGLOBAL m_hBuffer;

public:

CGdiPlusBitmapResource() { m_hBuffer = NULL; }

CGdiPlusBitmapResource(LPCTSTR pName, LPCTSTR pType = RT_RCDATA, HMODULE hInst = NULL)

{ m_hBuffer = NULL; Load(pName, pType, hInst); }

CGdiPlusBitmapResource(UINT id, LPCTSTR pType = RT_RCDATA, HMODULE hInst = NULL)

{ m_hBuffer = NULL; Load(id, pType, hInst); }

CGdiPlusBitmapResource(UINT id, UINT type, HMODULE hInst = NULL)

{ m_hBuffer = NULL; Load(id, type, hInst); }

virtual ~CGdiPlusBitmapResource() { Empty(); }

void Empty();

bool Load(LPCTSTR pName, LPCTSTR pType = RT_RCDATA, HMODULE hInst = NULL);

bool Load(UINT id, LPCTSTR pType = RT_RCDATA, HMODULE hInst = NULL)

{ return Load(MAKEINTRESOURCE(id), pType, hInst); }

bool Load(UINT id, UINT type, HMODULE hInst = NULL)

{ return Load(MAKEINTRESOURCE(id), MAKEINTRESOURCE(type), hInst); }

};

inline

void CGdiPlusBitmapResource::Empty()

{

CGdiPlusBitmap::Empty();

if (m_hBuffer)

{

::GlobalUnlock(m_hBuffer);

::GlobalFree(m_hBuffer);

m_hBuffer = NULL;

}

}

inline

bool CGdiPlusBitmapResource::Load(LPCTSTR pName, LPCTSTR pType, HMODULE hInst)

{

Empty();

HRSRC hResource = ::FindResource(hInst, pName, pType);

if (!hResource)

return false;

DWORD imageSize = ::SizeofResource(hInst, hResource);

if (!imageSize)

return false;

const void* pResourceData = ::LockResource(::LoadResource(hInst, hResource));

if (!pResourceData)

return false;

m_hBuffer = ::GlobalAlloc(GMEM_MOVEABLE, imageSize);

if (m_hBuffer)

{

void* pBuffer = ::GlobalLock(m_hBuffer);

if (pBuffer)

{

CopyMemory(pBuffer, pResourceData, imageSize);

IStream* pStream = NULL;

if (::CreateStreamOnHGlobal(m_hBuffer, FALSE, &pStream) == S_OK)

{

m_pBitmap = Gdiplus::Bitmap::FromStream(pStream);

pStream->Release();

if (m_pBitmap)

{

if (m_pBitmap->GetLastStatus() == Gdiplus::Ok)

return true;

delete m_pBitmap;

m_pBitmap = NULL;

}

}

::GlobalUnlock(m_hBuffer);

}

::GlobalFree(m_hBuffer);

m_hBuffer = NULL;

}

return false;

}

GdipButton.h

//

// GdipButton.h : Version 1.0 - see article at CodeProject.com

//

// Author: Darren Sessions

//

//

// Description:

// GdipButton is a CButton derived control that uses GDI+

// to support alternate image formats

//

// History

// Version 1.0 - 2008 June 10

// - Initial public release

//

// License:

// This software is released under the Code Project Open License (CPOL),

// which may be found here: http://www.codeproject.com/info/eula.aspx

// You are free to use this software in any way you like, except that you

// may not sell this source code.

//

// This software is provided "as is" with no expressed or implied warranty.

// I accept no liability for any damage or loss of business that this

// software may cause.

//

///////////////////////////////////////////////////////////////////////////////

#pragma once

// GdipButton.h : header file

//

class CGdiPlusBitmapResource;

/////////////////////////////////////////////////////////////////////////////

// CGdipButton window

class CGdipButton : public CButton

{

public:

CGdipButton();

virtual ~CGdipButton();

// image types

enum {

STD_TYPE = 0,

ALT_TYPE,

DIS_TYPE

};

// sets the image type

void SetImage(int type);

BOOL LoadAltImage(UINT id, LPCTSTR pType);

BOOL LoadStdImage(UINT id, LPCTSTR pType);

// if false, disables the press state and uses grayscale image if it exists

void EnableButton(BOOL bEnable = TRUE) { m_bIsDisabled = !bEnable; }

// in toggle mode each press toggles between std and alt images

void EnableToggle(BOOL bEnable = TRUE);

// return the enable/disable state

BOOL IsDisabled(void) {return (m_bIsDisabled == TRUE); }

void SetBkGnd(CDC* pDC);

void SetToolTipText(CString spText, BOOL bActivate = TRUE);

void SetToolTipText(UINT nId, BOOL bActivate = TRUE);

void SetHorizontal(bool ImagesAreLaidOutHorizontally = FALSE);

void DeleteToolTip();

protected:

void PaintBk(CDC* pDC);

void PaintBtn(CDC* pDC);

BOOL m_bHaveAltImage;

BOOL m_bHaveBitmaps;

BOOL m_bIsDisabled;

BOOL m_bIsToggle;

BOOL m_bIsHovering;

BOOL m_bIsTracking;

int m_nCurType;

CGdiPlusBitmapResource* m_pAltImage;

CGdiPlusBitmapResource* m_pStdImage;

CString m_tooltext;

CToolTipCtrl* m_pToolTip;

void InitToolTip();

virtual void PreSubclassWindow();

virtual void DrawItem(LPDRAWITEMSTRUCT /*lpDrawItemStruct*/);

virtual BOOL PreTranslateMessage(MSG* pMsg);

//{{AFX_MSG(CGdipButton)

afx_msg HBRUSH CtlColor(CDC* pDC, UINT nCtlColor);

afx_msg BOOL OnEraseBkgnd(CDC* pDC);

afx_msg void OnMouseMove(UINT nFlags, CPoint point);

afx_msg LRESULT OnMouseLeave(WPARAM wparam, LPARAM lparam);

afx_msg LRESULT OnMouseHover(WPARAM wparam, LPARAM lparam) ;

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

private:

CDC m_dcBk; // button background

CDC m_dcStd; // standard button

CDC m_dcStdP; // standard button pressed

CDC m_dcStdH; // standard button hot

CDC m_dcAlt; // alternate button

CDC m_dcAltP; // alternate button pressed

CDC m_dcAltH; // alternate button hot

CDC m_dcGS; // grayscale button (does not have a hot or pressed state)

CDC* m_pCurBtn; // current pointer to one of the above

};

MemDC.h

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// CMemDC - memory DC

//

// Author: Keith Rule, keithr@europa.com, Copyright 1996-1997, Keith Rule

//

// You may freely use or modify this code provided this copyright is included in all derived versions.

//

// History - 10/3/97 Fixed scrolling bug.

// Added print support.

// - 14/7/99 Added optional clip rect parameter [jgh]

//

// - 06/06/08 Added option to copy screen on construction

//

#if !defined(AFX_CaMemDC_H__F666A491_3847_11D3_A58E_00805FC1DE10__INCLUDED_)

#define AFX_CaMemDC_H__F666A491_3847_11D3_A58E_00805FC1DE10__INCLUDED_

class CaMemDC : public CDC { // 高版本的 vs 这里 CMemDC 会报重定义错误,我这里改成了 CaMemDC

private:

CBitmap m_bitmap; // Offscreen bitmap

CBitmap* m_oldBitmap; // bitmap originally found in CMemDC

CDC* m_pDC; // Saves CDC passed in constructor

CRect m_rect; // Rectangle of drawing area.

BOOL m_bMemDC; // TRUE if CDC really is a Memory DC.

public:

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Function Header

CaMemDC(CDC* pDC, CRect rect = CRect(0,0,0,0), BOOL bCopyFirst = FALSE) : CDC(), m_oldBitmap(NULL), m_pDC(pDC)

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

{

ASSERT(m_pDC != NULL); // If you asserted here, you passed in a NULL CDC.

m_bMemDC = !pDC->IsPrinting();

if (m_bMemDC){

// Create a Memory DC

CreateCompatibleDC(pDC);

if ( rect == CRect(0,0,0,0) )

pDC->GetClipBox(&m_rect);

else

m_rect = rect;

m_bitmap.CreateCompatibleBitmap(pDC, m_rect.Width(), m_rect.Height());

m_oldBitmap = SelectObject(&m_bitmap);

SetWindowOrg(m_rect.left, m_rect.top);

if(bCopyFirst)

{

this->BitBlt(m_rect.left, m_rect.top, m_rect.Width(), m_rect.Height(),

m_pDC, m_rect.left, m_rect.top, SRCCOPY);

}

} else {

// Make a copy of the relevent parts of the current DC for printing

m_bPrinting = pDC->m_bPrinting;

m_hDC = pDC->m_hDC;

m_hAttribDC = pDC->m_hAttribDC;

}

}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Function Header

~CaMemDC()

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

{

if (m_bMemDC) {

// Copy the offscreen bitmap onto the screen.

m_pDC->BitBlt(m_rect.left, m_rect.top, m_rect.Width(), m_rect.Height(),

this, m_rect.left, m_rect.top, SRCCOPY);

//Swap back the original bitmap.

SelectObject(m_oldBitmap);

} else {

// All we need to do is replace the DC with an illegal value,

// this keeps us from accidently deleting the handles associated with

// the CDC that was passed to the constructor.

m_hDC = m_hAttribDC = NULL;

}

}

// Allow usage as a pointer

CaMemDC* operator->() {return this;}

// Allow usage as a pointer

operator CaMemDC*() {return this;}

};

#endif

// End CMemDC

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

GdipButton.cpp

//

// GdipButton.cpp : Version 1.0 - see article at CodeProject.com

//

// Author: Darren Sessions

//

//

// Description:

// GdipButton is a CButton derived control that uses GDI+

// to support alternate image formats

//

// History

// Version 1.0 - 2008 June 10

// - Initial public release

//

// License:

// This software is released under the Code Project Open License (CPOL),

// which may be found here: http://www.codeproject.com/info/eula.aspx

// You are free to use this software in any way you like, except that you

// may not sell this source code.

//

// This software is provided "as is" with no expressed or implied warranty.

// I accept no liability for any damage or loss of business that this

// software may cause.

//

///////////////////////////////////////////////////////////////////////////////

#include "pch.h" // 低版本的 vs 使用 stdafx.h

#include "GdipButton.h"

#include "CGdiPlusBitmap.h"

#include "MemDC.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

/////////////////////////////////////////////////////////////////////////////

// CGdipButton

CGdipButton::CGdipButton()

{

m_pStdImage = NULL;

m_pAltImage = NULL;

m_bHaveBitmaps = FALSE;

m_bHaveAltImage = FALSE;

m_pCurBtn = NULL;

m_bIsDisabled = FALSE;

m_bIsToggle = FALSE;

m_bIsHovering = FALSE;

m_bIsTracking = FALSE;

m_nCurType = STD_TYPE;

m_pToolTip = NULL;

}

CGdipButton::~CGdipButton()

{

if(m_pStdImage) delete m_pStdImage;

if(m_pAltImage) delete m_pAltImage;

if(m_pToolTip) delete m_pToolTip;

}

BEGIN_MESSAGE_MAP(CGdipButton, CButton)

//{{AFX_MSG_MAP(CGdipButton)

ON_WM_DRAWITEM()

ON_WM_ERASEBKGND()

ON_WM_CTLCOLOR_REFLECT()

ON_WM_MOUSEMOVE()

ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)

ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover)

//}}AFX_MSG_MAP

END_MESSAGE_MAP()

//=============================================================================

//

// LoadStdImage()

//

// Purpose: The LoadStdImage() Loads the image for the button. This

// function must be called at a minimum or the button wont do

// anything.

//

// Parameters:

// [IN] id

// resource id, one of the resources already imported with the

// resource editor, usually begins with IDR_

//

// [IN] pType

// pointer to string describing the resource type

//

// Returns: BOOL

// Non zero if successful, otherwise zero

//

//=============================================================================

BOOL CGdipButton::LoadStdImage(UINT id, LPCTSTR pType)

{

m_pStdImage = new CGdiPlusBitmapResource;

return m_pStdImage->Load(id, pType);

}

//=============================================================================

//

// LoadAltImage()

//

// Purpose: The LoadAltImage() Loads the altername image for the button.

// This function call is optional

// Parameters:

// [IN] id

// resource id, one of the resources already imported with the

// resource editor, usually begins with IDR_

//

// [IN] pType

// pointer to string describing the resource type

//

// Returns: BOOL

// Non zero if successful, otherwise zero

//

//=============================================================================

BOOL CGdipButton::LoadAltImage(UINT id, LPCTSTR pType)

{

m_bHaveAltImage = TRUE;

m_pAltImage = new CGdiPlusBitmapResource;

return (m_pAltImage->Load(id, pType));

}

//=============================================================================

//

// The framework calls this member function when a child control is about to

// be drawn. All the bitmaps are created here on the first call. Every thing

// is done with a memory DC except the background, which get's it's information

// from the parent. The background is needed for transparent portions of PNG

// images. An always on top app (such as Task Manager) that is in the way can

// cause it to get an incorrect background. To avoid this, the parent should

// call the SetBkGnd function with a memory DC when it creates the background.

//

//=============================================================================

HBRUSH CGdipButton::CtlColor(CDC* pScreenDC, UINT nCtlColor)

{

if(!m_bHaveBitmaps)

{

if(!m_pStdImage)

{

return NULL; // Load the standard image with LoadStdImage()

}

CBitmap bmp, *pOldBitmap;

CRect rect;

GetClientRect(rect);

// do everything with mem dc

CaMemDC pDC(pScreenDC, rect);

Gdiplus::Graphics graphics(pDC->m_hDC);

// background

if (m_dcBk.m_hDC == NULL)

{

CRect rect1;

CClientDC clDC(GetParent());

GetWindowRect(rect1);

GetParent()->ScreenToClient(rect1);

m_dcBk.CreateCompatibleDC(&clDC);

bmp.CreateCompatibleBitmap(&clDC, rect.Width(), rect.Height());

pOldBitmap = m_dcBk.SelectObject(&bmp);

m_dcBk.BitBlt(0, 0, rect.Width(), rect.Height(), &clDC, rect1.left, rect1.top, SRCCOPY);

bmp.DeleteObject();

}

// standard image

if (m_dcStd.m_hDC == NULL)

{

PaintBk(pDC);

graphics.DrawImage(*m_pStdImage, 0, 0);

m_dcStd.CreateCompatibleDC(pDC);

bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());

pOldBitmap = m_dcStd.SelectObject(&bmp);

m_dcStd.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, 0, 0, SRCCOPY);

bmp.DeleteObject();

// standard image pressed

if (m_dcStdP.m_hDC == NULL)

{

PaintBk(pDC);

graphics.DrawImage(*m_pStdImage, 1, 1);

m_dcStdP.CreateCompatibleDC(pDC);

bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());

pOldBitmap = m_dcStdP.SelectObject(&bmp);

m_dcStdP.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, 0, 0, SRCCOPY);

bmp.DeleteObject();

}

// standard image hot

if(m_dcStdH.m_hDC == NULL)

{

PaintBk(pDC);

ColorMatrix HotMat = { 1.05f, 0.00f, 0.00f, 0.00f, 0.00f,

0.00f, 1.05f, 0.00f, 0.00f, 0.00f,

0.00f, 0.00f, 1.05f, 0.00f, 0.00f,

0.00f, 0.00f, 0.00f, 1.00f, 0.00f,

0.05f, 0.05f, 0.05f, 0.00f, 1.00f };

ImageAttributes ia;

ia.SetColorMatrix(&HotMat);

float width = (float)m_pStdImage->m_pBitmap->GetWidth();

float height = (float)m_pStdImage->m_pBitmap->GetHeight();

RectF grect; grect.X=0, grect.Y=0; grect.Width = width; grect.Height = height;

graphics.DrawImage(*m_pStdImage, grect, 0, 0, width, height, UnitPixel, &ia);

m_dcStdH.CreateCompatibleDC(pDC);

bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());

pOldBitmap = m_dcStdH.SelectObject(&bmp);

m_dcStdH.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, 0, 0, SRCCOPY);

bmp.DeleteObject();

}

// grayscale image

if(m_dcGS.m_hDC == NULL)

{

PaintBk(pDC);

ColorMatrix GrayMat = { 0.30f, 0.30f, 0.30f, 0.00f, 0.00f,

0.59f, 0.59f, 0.59f, 0.00f, 0.00f,

0.11f, 0.11f, 0.11f, 0.00f, 0.00f,

0.00f, 0.00f, 0.00f, 1.00f, 0.00f,

0.00f, 0.00f, 0.00f, 0.00f, 1.00f };

ImageAttributes ia;

ia.SetColorMatrix(&GrayMat);

float width = (float)m_pStdImage->m_pBitmap->GetWidth();

float height = (float)m_pStdImage->m_pBitmap->GetHeight();

RectF grect; grect.X=0, grect.Y=0; grect.Width = width; grect.Height = height;

graphics.DrawImage(*m_pStdImage, grect, 0, 0, width, height, UnitPixel, &ia);

m_dcGS.CreateCompatibleDC(pDC);

bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());

pOldBitmap = m_dcGS.SelectObject(&bmp);

m_dcGS.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, 0, 0, SRCCOPY);

bmp.DeleteObject();

}

}

// alternate image

if( (m_dcAlt.m_hDC == NULL) && m_bHaveAltImage )

{

PaintBk(pDC);

graphics.DrawImage(*m_pAltImage, 0, 0);

m_dcAlt.CreateCompatibleDC(pDC);

bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());

pOldBitmap = m_dcAlt.SelectObject(&bmp);

m_dcAlt.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, 0, 0, SRCCOPY);

bmp.DeleteObject();

// alternate image pressed

if( (m_dcAltP.m_hDC == NULL) && m_bHaveAltImage )

{

PaintBk(pDC);

graphics.DrawImage(*m_pAltImage, 1, 1);

m_dcAltP.CreateCompatibleDC(pDC);

bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());

pOldBitmap = m_dcAltP.SelectObject(&bmp);

m_dcAltP.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, 0, 0, SRCCOPY);

bmp.DeleteObject();

}

// alternate image hot

if(m_dcAltH.m_hDC == NULL)

{

PaintBk(pDC);

ColorMatrix HotMat = { 1.05f, 0.00f, 0.00f, 0.00f, 0.00f,

0.00f, 1.05f, 0.00f, 0.00f, 0.00f,

0.00f, 0.00f, 1.05f, 0.00f, 0.00f,

0.00f, 0.00f, 0.00f, 1.00f, 0.00f,

0.05f, 0.05f, 0.05f, 0.00f, 1.00f };

ImageAttributes ia;

ia.SetColorMatrix(&HotMat);

float width = (float)m_pStdImage->m_pBitmap->GetWidth();

float height = (float)m_pStdImage->m_pBitmap->GetHeight();

RectF grect; grect.X=0, grect.Y=0; grect.Width = width; grect.Height = height;

graphics.DrawImage(*m_pAltImage, grect, 0, 0, width, height, UnitPixel, &ia);

m_dcAltH.CreateCompatibleDC(pDC);

bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());

pOldBitmap = m_dcAltH.SelectObject(&bmp);

m_dcAltH.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, 0, 0, SRCCOPY);

bmp.DeleteObject();

}

}

if(m_pCurBtn == NULL)

{

m_pCurBtn = &m_dcStd;

}

m_bHaveBitmaps = TRUE;

}

return NULL;

}

//=============================================================================

// paint the background

//=============================================================================

void CGdipButton::PaintBk(CDC *pDC)

{

CRect rect;

GetClientRect(rect);

pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &m_dcBk, 0, 0, SRCCOPY);

}

//=============================================================================

// paint the bitmap currently pointed to with m_pCurBtn

//=============================================================================

void CGdipButton::PaintBtn(CDC *pDC)

{

CRect rect;

GetClientRect(rect);

pDC->BitBlt(0, 0, rect.Width(), rect.Height(), m_pCurBtn, 0, 0, SRCCOPY);

}

//=============================================================================

// enables the toggle mode

// returns if it doesn't have the alternate image

//=============================================================================

void CGdipButton::EnableToggle(BOOL bEnable)

{

if(!m_bHaveAltImage) return;

m_bIsToggle = bEnable;

// this actually makes it start in the std state since toggle is called before paint

if(bEnable) m_pCurBtn = &m_dcAlt;

else m_pCurBtn = &m_dcStd;

}

//=============================================================================

// sets the image type and disabled state then repaints

//=============================================================================

void CGdipButton::SetImage(int type)

{

m_nCurType = type;

(type == DIS_TYPE) ? m_bIsDisabled = TRUE : m_bIsDisabled = FALSE;

Invalidate();

}

//=============================================================================

// set the control to owner draw

//=============================================================================

void CGdipButton::PreSubclassWindow()

{

// Set control to owner draw

ModifyStyle(0, BS_OWNERDRAW, SWP_FRAMECHANGED);

CButton::PreSubclassWindow();

}

//=============================================================================

// disable double click

//=============================================================================

BOOL CGdipButton::PreTranslateMessage(MSG* pMsg)

{

if (pMsg->message == WM_LBUTTONDBLCLK)

pMsg->message = WM_LBUTTONDOWN;

if (m_pToolTip != NULL)

{

if (::IsWindow(m_pToolTip->m_hWnd))

{

m_pToolTip->RelayEvent(pMsg);

}

}

return CButton::PreTranslateMessage(pMsg);

}

//=============================================================================

// overide the erase function

//=============================================================================

BOOL CGdipButton::OnEraseBkgnd(CDC* pDC)

{

return TRUE;

}

//=============================================================================

// Paint the button depending on the state of the mouse

//=============================================================================

void CGdipButton::DrawItem(LPDRAWITEMSTRUCT lpDIS)

{

CDC* pDC = CDC::FromHandle(lpDIS->hDC);

// handle disabled state

if(m_bIsDisabled)

{

m_pCurBtn = &m_dcGS;

PaintBtn(pDC);

return;

}

BOOL bIsPressed = (lpDIS->itemState & ODS_SELECTED);

// handle toggle button

if(m_bIsToggle && bIsPressed)

{

(m_nCurType == STD_TYPE) ? m_nCurType = ALT_TYPE : m_nCurType = STD_TYPE;

}

if(bIsPressed)

{

if(m_nCurType == STD_TYPE)

m_pCurBtn = &m_dcStdP;

else

m_pCurBtn = &m_dcAltP;

}

else if(m_bIsHovering)

{

if(m_nCurType == STD_TYPE)

m_pCurBtn = &m_dcStdH;

else

m_pCurBtn = &m_dcAltH;

}

else

{

if(m_nCurType == STD_TYPE)

m_pCurBtn = &m_dcStd;

else

m_pCurBtn = &m_dcAlt;

}

// paint the button

PaintBtn(pDC);

}

//=============================================================================

LRESULT CGdipButton::OnMouseHover(WPARAM wparam, LPARAM lparam)

//=============================================================================

{

m_bIsHovering = TRUE;

Invalidate();

DeleteToolTip();

// Create a new Tooltip with new Button Size and Location

SetToolTipText(m_tooltext);

if (m_pToolTip != NULL)

{

if (::IsWindow(m_pToolTip->m_hWnd))

{

//Display ToolTip

m_pToolTip->Update();

}

}

return 0;

}

//=============================================================================

LRESULT CGdipButton::OnMouseLeave(WPARAM wparam, LPARAM lparam)

//=============================================================================

{

m_bIsTracking = FALSE;

m_bIsHovering = FALSE;

Invalidate();

return 0;

}

//=============================================================================

void CGdipButton::OnMouseMove(UINT nFlags, CPoint point)

//=============================================================================

{

if (!m_bIsTracking)

{

TRACKMOUSEEVENT tme;

tme.cbSize = sizeof(tme);

tme.hwndTrack = m_hWnd;

tme.dwFlags = TME_LEAVE|TME_HOVER;

tme.dwHoverTime = 1;

m_bIsTracking = _TrackMouseEvent(&tme);

}

CButton::OnMouseMove(nFlags, point);

}

//=============================================================================

//

// Call this member function with a memory DC from the code that paints

// the parents background. Passing the screen DC defeats the purpose of

// using this function.

//

//=============================================================================

void CGdipButton::SetBkGnd(CDC* pDC)

{

CRect rect, rectS;

CBitmap bmp, *pOldBitmap;

GetClientRect(rect);

GetWindowRect(rectS);

GetParent()->ScreenToClient(rectS);

m_dcBk.DeleteDC();

m_dcBk.CreateCompatibleDC(pDC);

bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());

pOldBitmap = m_dcBk.SelectObject(&bmp);

m_dcBk.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, rectS.left, rectS.top, SRCCOPY);

bmp.DeleteObject();

}

//=============================================================================

// Set the tooltip with a string resource

//=============================================================================

void CGdipButton::SetToolTipText(UINT nId, BOOL bActivate)

{

// load string resource

m_tooltext.LoadString(nId);

// If string resource is not empty

if (m_tooltext.IsEmpty() == FALSE)

{

SetToolTipText(m_tooltext, bActivate);

}

}

//=============================================================================

// Set the tooltip with a CString

//=============================================================================

void CGdipButton::SetToolTipText(CString spText, BOOL bActivate)

{

// We cannot accept NULL pointer

if (spText.IsEmpty()) return;

// Initialize ToolTip

InitToolTip();

m_tooltext = spText;

// If there is no tooltip defined then add it

if (m_pToolTip->GetToolCount() == 0)

{

CRect rectBtn;

GetClientRect(rectBtn);

m_pToolTip->AddTool(this, m_tooltext, rectBtn, 1);

}

// Set text for tooltip

m_pToolTip->UpdateTipText(m_tooltext, this, 1);

m_pToolTip->SetDelayTime(2000);

m_pToolTip->Activate(bActivate);

}

//=============================================================================

void CGdipButton::InitToolTip()

//=============================================================================

{

if (m_pToolTip == NULL)

{

m_pToolTip = new CToolTipCtrl;

// Create ToolTip control

m_pToolTip->Create(this);

m_pToolTip->Activate(TRUE);

}

}

//=============================================================================

void CGdipButton::DeleteToolTip()

//=============================================================================

{

// Destroy Tooltip incase the size of the button has changed.

if (m_pToolTip != NULL)

{

delete m_pToolTip;

m_pToolTip = NULL;

}

}

2.在 pch.h (或者是 stdafx.h)里添加上对 GDI+ 的引用:

```cpp

#include

#pragma comment(lib, "gdiplus.lib")

using namespace Gdiplus;

3.在 InitInstance() 函数的 CWinApp::InitInstance(); 之前中启用 GDI+:

// ....

GdiplusStartup(&GdiToken, &gdiplusstartupinput, NULL); // 激活 GDI (应该在程序退出时应该调用 GdiplusShutdown 关闭)

CWinApp::InitInstance(); // 要在这一句上面,否则会导致以后窗口不能自动重绘、不能使用字体等一系列问题。

// .....

4.关闭 GDI+ : 应该在 ExitInstance() 里处理 ,但是我没有找到这个函数,可能是高版本里被废除掉了,所以我尝试用 OnDestroy 来代替它完成清理工作,但这么做会触发一个异常。

GdiplusShutdown(GdiToken);

关于 GDI+ 的内容,参考自:https://wenku.baidu.com/view/c662b31514791711cc791776.html

5.在 OnInitDialog() 函数里添加如下代码(m_cPlay 是你按钮的变量名,IDR_PLAY 是你的 png 资源名)

m_cPlay.LoadStdImage(IDR_PLAY, _T("PNG"));

效果图:

修改普通按钮风格

1 . 为按钮添加一个成员变量,类型为 CMFCButton 关于 CMFCButton 的定义,参考 : https://docs.microsoft.com/zh-cn/cpp/mfc/reference/cmfcbutton-class?view=msvc-160

2 . 在 OnInitDialog() 函数里添加按钮的属性:

btn_1.SetWindowTextW(_T("LYSM")); // 设置文本内容

btn_1.SetTextColor(RGB(255, 255, 255)); // 设置文本颜色

btn_1.SetTextHotColor(RGB(0, 0, 0)); // 设置文本被点击时的颜色

btn_1.SetMouseCursorHand(); // 鼠标经过时变成手指

btn_1.m_bTransparent = FALSE; // 按钮透明

btn_1.SetFaceColor(RGB(100, 0, 0)); // 更改背景颜色

btn_1.m_bDontUseWinXPTheme = TRUE; // 使用 XP 风格,否则颜色不显示

btn_1.m_bDrawFocus = FALSE; // 去除周围黑框

2 . 类向导里添加一个时钟事件,相应鼠标悬停逻辑:

void Ctestmfc1Dlg::OnTimer(UINT_PTR nIDEvent)

{

// TODO: 在此添加消息处理程序代码和/或调用默认值

if (1 == nIDEvent)

{

POINT p;

GetCursorPos(&p);

CWnd* hwndMouse = WindowFromPoint(p);

CWnd* hwndBtn1 = GetDlgItem(IDC_BUTTON1);

if (hwndMouse == hwndBtn1)

{

btn_1.SetFaceColor(RGB(0, 100, 0)); // 鼠标悬停改变按钮颜色

}

else

{

btn_1.SetFaceColor(RGB(100, 0, 0)); // 否则使用默认颜色

}

}

CDialogEx::OnTimer(nIDEvent);

}

最后不要忘了在 OnInitDialog() 里初始化时钟:

SetTimer(1, 100, NULL);

效果图:

取消 ESC、ENTER 后程序退出

mfc 对话框程序,按以上两个键会退出程序,所以需要处理一下。

1 . 类向导里添加虚函数 : PreTranslateMessage

2 . 添加如下代码,接管按键事件:

BOOL Ctestmfc1Dlg::PreTranslateMessage(MSG* pMsg)

{

// TODO: 在此添加专用代码和/或调用基类

if (pMsg->message == WM_KEYDOWN)

{

switch (pMsg->wParam)

{

case VK_RETURN : // 屏蔽回车

return TRUE;

case VK_ESCAPE : // 屏蔽 Esc

return TRUE;

}

}

return CDialogEx::PreTranslateMessage(pMsg);

}

edit control 修改字体大小

1 .OnInitDialog() 里,添加如下代码:

CFont my_Font;

my_Font.CreatePointFont(300, L"Arial"); // 300 :字体大小 ,Arial : 字体样式

edit_1.SetFont(&my_Font); // edit_1 :edit control 控件变量

相关推荐

盘点柳公权十大书法作品,每一件都堪称传世经典、无价之宝
集成电路测试(一)
365bet体育在线中文

集成电路测试(一)

07-13 👁️ 6021
世界杯明星 生日快乐
365bet足球盘

世界杯明星 生日快乐

07-02 👁️ 2823
1986年世界杯决赛阿根廷对
完美体育365官方网站

1986年世界杯决赛阿根廷对

07-07 👁️ 6322
秽土转生能一直活着吗
365bet足球盘

秽土转生能一直活着吗

07-18 👁️ 6118
黄家驹一共有几把吉他
365bet体育在线中文

黄家驹一共有几把吉他

07-06 👁️ 2992
多国限制和禁止电商发展!电商真的会被市场淘汰吗?你怎么看?
斗鱼卡卡被封,发微博称退出直播界,观众:斗鱼三骚再无
魔兽世界飞行解锁需要多少时间(wow9.0怎么解锁飞行)