0
点赞
收藏
分享

微信扫一扫

[MFC] 手动美化 MFC 窗体

佃成成成成 2022-07-27 阅读 245

去除边框

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

[MFC] 手动美化 MFC 窗体_ide

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

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

[MFC] 手动美化 MFC 窗体_gdi+_02

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

[MFC] 手动美化 MFC 窗体_ide_03

添加背景图片

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

[MFC] 手动美化 MFC 窗体_ide_04

[MFC] 手动美化 MFC 窗体_sed_05

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

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

// 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
#define

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
#define
#undef
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 <gdiplus.h>
#pragma
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"));

效果图:

[MFC] 手动美化 MFC 窗体_sed_06

修改普通按钮风格

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);

效果图:

[MFC] 手动美化 MFC 窗体_sed_07

取消 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 控件变量



举报

相关推荐

0 条评论