[WTL/ATL]_[中级]_[自定义按钮2]

场景

  1. 在自定义按钮1里我们通过处理WM_PAINT消息来达到绘制按钮的目的, 并通过BCN_HOTITEMCHANGE通知来处理鼠标进入和离开状态. 按钮控件有没有其他方式来绘制呢?不需要通过WM_PAINT事件, 并且在绘制前就能知道按钮当前的状态?

说明

  1. 当创建按钮的样式增加BS_OWNERDRAW时, 按钮会接收到WM_DRAWITEM事件. 这时候我们可以通过处理WM_DRAWITEM事件来绘制按钮. 这个事件的lParam参数是指向DRAWITEMSTRUCT的指针. 这个结构体按道理应该能通过itemState属性来判断进入和离开的事件, 可是itemState的值在鼠标移动上去时并没有发送WM_DRAWITEM消息. 所以如果要处理鼠标进入和离开的消息, 必须另外添加消息WM_MOUSEMOVE,WM_MOUSEHOVER,WM_MOUSELEAVE处理.
void BASCOwnerDrawButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
  1. 消息WM_MOUSEHOVERWM_MOUSELEAVE默认是不发送的.只有当执行_TrackMouseEvent函数时才会发送. 所以需要在WM_MOUSEMOVE消息处理函数里添加鼠标跟踪, 并增加一个成员变量来作为跟踪启用标识m_bTracking_, 初始化为false.

if (!m_bTracking_)  
{  
    TRACKMOUSEEVENT tme;  
    tme.cbSize = sizeof(TRACKMOUSEEVENT);  
    tme.dwFlags = TME_LEAVE | TME_HOVER;//要触发的消息类型  
    tme.hwndTrack = m_hWnd;  
    tme.dwHoverTime = 10;// 如果不设此参数,无法触发mouseHover  

    if (::_TrackMouseEvent(&tme)) //MOUSELEAVE|MOUSEHOVER消息由此函数触发.  
    {  
        m_bTracking_ = true;     
    }  
}  
return 0;
  1. 跟踪鼠标后,接着就是在WM_MOUSEHOVERWM_MOUSELEAVE理论进行状态status_设置, 因为这两个消息看说明都是鼠标移上去或移出之后Posted的消息,所以即使在消息处理函数里设置handled = FALSE,按钮也不会刷新,所以需要自己调用Invalidate(FALSE)来刷新按钮.

代码

bas_cownerdraw_button.h


#pragma once

#include 
#include 
#include 

#include 
#include 
#include "atlframe.h"
#include 
#include 
#include 
#include 
#include 

class BASCOwnerDrawButton:public CWindowImpl<BASCOwnerDrawButton,CButton>,public COwnerDraw<BASCOwnerDrawButton>
{
public:

	BEGIN_MSG_MAP_EX(BASCOwnerDrawButton)	
		MESSAGE_HANDLER(WM_CREATE, OnCreate)
		MSG_WM_ERASEBKGND(OnEraseBkgnd)
		MESSAGE_HANDLER(WM_MOUSEMOVE,OnMouseMove)
		MESSAGE_HANDLER(WM_MOUSEHOVER,OnMouseHover) 
		MESSAGE_HANDLER(WM_MOUSELEAVE,OnMouseLeave)
		CHAIN_MSG_MAP_ALT(COwnerDraw<BASCOwnerDrawButton>, 1)
		DEFAULT_REFLECTION_HANDLER()
	END_MSG_MAP()

	BASCOwnerDrawButton();
	~BASCOwnerDrawButton();

	void SetInitVisible(bool visible = true);
	HWND CreateButton(HWND hWndParent,_U_MENUorID MenuOrID = 0U);


	inline BASCOwnerDrawButton& SetRadio(bool radio)
	{
		is_radio_button_ = radio;
		return *this;
	}

	inline BASCOwnerDrawButton& SetHMargin(int hmargin)
	{
		hmargin_ = hmargin;
		return *this;
	}

	inline BASCOwnerDrawButton& SetVMargin(int vmargin)
	{
		vmargin_ = vmargin;
		return *this;
	}

	inline BASCOwnerDrawButton& SetColorDisable(DWORD disable_bkg_color,DWORD disable_font_color)
	{
		disable_bkg_color_ = disable_bkg_color;
		disable_font_color_ = disable_font_color;
		return *this;
	}

	inline BASCOwnerDrawButton& SetBackgroundColor(DWORD color)
	{
		color_bg_ = color;
		return *this;
	}

	inline BASCOwnerDrawButton& SetRoundRectPoint(CPoint point)
	{
		round_rect_point_ = point;
		return *this;
	}

	inline BASCOwnerDrawButton& SetRoundRect(bool is_round_rect)
	{
		is_round_rect_ = is_round_rect;
		return *this;
	}

	inline BASCOwnerDrawButton& SetColorBorder(DWORD color_border)
	{
		color_border_ = color_border;
		return *this;
	}

	BOOL Init(int nPosx,int nPosy,LPCWSTR text,
		COLORREF normal_bkg_color,COLORREF normal_font_color,
		COLORREF pressed_bkg_color,COLORREF pressed_font_color);

	void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
	void DrawRect(Gdiplus::Graphics& graphics,Gdiplus::Rect rect,DWORD color);

	void ResetStatus();
	void SetPressedStatus();

	BOOL EnableWindow(_In_ BOOL bEnable = TRUE);
	void SetFont(Gdiplus::Font* font,bool deleteOld = true);

protected:
	LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
	LRESULT OnMouseLeave(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
	LRESULT OnMouseHover(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);

	LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
	BOOL OnEraseBkgnd(CDCHandle dc);

private:
	inline void SetTextColor(COLORREF color){
		color_text_ = color;
	}
	void SetTextString(LPCWSTR text);

	int nPosx_;
	int nPosy_;
	bool is_radio_button_;
	Gdiplus::Font *font_;
	COLORREF color_text_;

	std::wstring text_;

	DWORD normal_bkg_color_;
	DWORD normal_font_color_;

	DWORD pressed_bkg_color_;
	DWORD pressed_font_color_;

	DWORD disable_bkg_color_;
	DWORD disable_font_color_;

	int hmargin_;
	int vmargin_;

	int button_width_;
	int button_height_;

	int status_;

	bool m_bTracking_;
	bool pressed_;

	bool is_round_rect_;
	CPoint round_rect_point_;

	COLORREF color_border_;
	COLORREF color_bg_;

	int scaling_; // 缩放系数
	bool visible_; // 是否创建可见窗口.
};

bas_cownerdraw_button.cpp


#include "stdafx.h"
#include "bas_cownerdraw_button.h"
#include 
#include 
#include 
#include "utils.h"

namespace
{
	enum
	{
		kButtonStatusNormal,
		kButtonStatusPressed,
		kButtonStatusHover,
		kButtonStatusDisable
	};
}

void BASCOwnerDrawButton::SetInitVisible(bool visible)
{
	visible_ = visible;
}

BOOL BASCOwnerDrawButton::EnableWindow(BOOL bEnable)
{
	status_ = (bEnable)?kButtonStatusNormal:kButtonStatusDisable;
	return CWindowImpl::EnableWindow(bEnable);
}

BASCOwnerDrawButton::BASCOwnerDrawButton()
{
	disable_bkg_color_ = -1;
	disable_font_color_ = -1;
	color_bg_ = RGB(255,255,255);
	visible_= true;
	hmargin_ = 10;
	vmargin_ = 10;
	is_radio_button_ = true;
	color_border_ = -1;

	disable_bkg_color_ = RGB(127,127,127);
	disable_font_color_ = RGB(255,255,255);
	is_round_rect_ = false;
	round_rect_point_.x = 6.0;
	round_rect_point_.y = 6.0;

	font_ = NULL;
}

void BASCOwnerDrawButton::SetFont(Gdiplus::Font* font,bool deleteOld)
{
	if(deleteOld)
		delete font_;
	font_ = font;
}

BASCOwnerDrawButton::~BASCOwnerDrawButton()
{
	Detach();
}

void BASCOwnerDrawButton::SetPressedStatus()
{
	status_ = kButtonStatusPressed;
	Invalidate(FALSE);
}

void BASCOwnerDrawButton::ResetStatus()
{
	status_ = kButtonStatusNormal;
	Invalidate(FALSE);
}

void BASCOwnerDrawButton::DrawRect(Gdiplus::Graphics& graphics,Gdiplus::Rect rect,DWORD color)
{
	Gdiplus::GraphicsPath m_pPath;
	m_pPath.AddRectangle(rect);
	m_pPath.CloseFigure();
	Gdiplus::Color color_bg(GetRValue(color_bg_),GetGValue(color_bg_),
		GetBValue(color_bg_));
	Gdiplus::SolidBrush brush_bg(color_bg);
	graphics.FillPath(&brush_bg,&m_pPath);

	Gdiplus::Pen *pen = NULL;
	Gdiplus::Color gcolor(GetRValue(color),GetGValue(color),GetBValue(color));
	if(color_border_ == -1){
		pen = new Gdiplus::Pen(gcolor,1);
	}else{
		Gdiplus::Color color_border(GetRValue(color_border_),GetGValue(color_border_),
			GetBValue(color_border_));
		pen = new Gdiplus::Pen(color_border,1);
	}

	Gdiplus::SolidBrush brush_color(gcolor);
	Gdiplus::GraphicsPath path_border;
	if(is_round_rect_){
		//Utils::CreateRoundRect(path_border,rect,round_rect_point_.x);
	}else{
		path_border.AddRectangle(rect);
		path_border.CloseFigure();
	}
	graphics.FillPath(&brush_color,&path_border);

	if(color_border_ != -1)
		graphics.DrawPath(pen,&path_border);
}

void BASCOwnerDrawButton::SetTextString(LPCWSTR text)
{
	text_ = text;

	Gdiplus::Graphics graphics(m_hWnd);
	Gdiplus::RectF rect_text;
	graphics.MeasureString(text_.c_str(),text_.size(),font_,
		Gdiplus::PointF(0,0),&rect_text);

	button_width_ = rect_text.Width+hmargin_*2;
	button_height_ = rect_text.Height + vmargin_*2;
}

LRESULT BASCOwnerDrawButton::OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)  
{  
    if (!m_bTracking_)  
    {  
        TRACKMOUSEEVENT tme;  
        tme.cbSize = sizeof(TRACKMOUSEEVENT);  
        tme.dwFlags = TME_LEAVE | TME_HOVER;//要触发的消息类型  
        tme.hwndTrack = m_hWnd;  
        tme.dwHoverTime = 10;// 如果不设此参数,无法触发mouseHover  
  
        if (::_TrackMouseEvent(&tme)) //MOUSELEAVE|MOUSEHOVER消息由此函数触发.  
        {  
            m_bTracking_ = true;     
        }  
    }
    return 0;  
}

LRESULT BASCOwnerDrawButton::OnMouseHover(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)  
{  
	if (!is_radio_button_){
		status_ = kButtonStatusHover;
	}else if(status_ != kButtonStatusPressed){
		status_ = kButtonStatusHover;
	}
	Invalidate(FALSE);
	bHandled = FALSE;
	return 0;
}  
  
LRESULT BASCOwnerDrawButton::OnMouseLeave(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)  
{  
	status_ = kButtonStatusNormal;
	Invalidate(FALSE);
	m_bTracking_ = false;
	bHandled = FALSE;
	return TRUE; 
}  

void BASCOwnerDrawButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
	// Ellipse
	HDC hdc = lpDrawItemStruct->hDC;
	
	CRect rect;
	GetClientRect(&rect);
	Gdiplus::Bitmap bmp(int(rect.Width()),int(rect.Height()));
	Gdiplus::Graphics graphics(&bmp);
	graphics.SetTextRenderingHint(Gdiplus::TextRenderingHintSystemDefault);
	graphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);

	// 按钮窗口创建可能有1像素的边框区域,需要夸扩大左上区域1像素才可以把边界绘制到.
	// 不清楚原因.
	rect.InflateRect(1,1,0,0);
	Gdiplus::Rect client_rect(rect.left,rect.top,rect.Width(),rect.Height());
	switch(status_)
	{
	case kButtonStatusNormal:
		{
			SetTextColor(normal_font_color_);
			DrawRect(graphics,client_rect,normal_bkg_color_);
			break;
		}
	case kButtonStatusPressed:
		{
			SetTextColor(pressed_font_color_);
			DrawRect(graphics,client_rect,pressed_bkg_color_);
			break;
		}
	case kButtonStatusHover:
		{
			SetTextColor(pressed_font_color_);
			DrawRect(graphics,client_rect,pressed_bkg_color_);
			break;
		}
	case kButtonStatusDisable:
		{
			SetTextColor(disable_font_color_);
			DrawRect(graphics,client_rect,disable_bkg_color_);
			break;
		}
	}

	if(text_.size()){
		Gdiplus::RectF rect(client_rect.X,client_rect.Y,client_rect.Width,client_rect.Height);
		Gdiplus::Color color_text;
		color_text.SetFromCOLORREF(color_text_);
		Utils::DrawTextCenter(graphics,rect,text_.c_str(),font_,&color_text);
	}

	///*Drawing on DC*/
	Gdiplus::Graphics graphics1(hdc);
	///*Important! Create a CacheBitmap object for quick drawing*/
	Gdiplus::CachedBitmap cachedBmp(&bmp,&graphics1);
	graphics1.DrawCachedBitmap(&cachedBmp,0,0);

	SetMsgHandled(FALSE);
}

HWND BASCOwnerDrawButton::CreateButton(HWND hWndParent,_U_MENUorID MenuOrID)
{
	m_bTracking_ = false;
	status_ = kButtonStatusNormal;

	// BS_NOTIFY WS_CLIPCHILDREN
	int flag = WS_CHILD | WS_CLIPCHILDREN |BS_OWNERDRAW;
	if(visible_)
		flag |= WS_VISIBLE;

	
	return Create(hWndParent,NULL,L"",flag,0,MenuOrID);
}

BOOL BASCOwnerDrawButton::OnEraseBkgnd(CDCHandle dc)
{
	return TRUE;
}

LRESULT BASCOwnerDrawButton::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	Gdiplus::FontFamily fontFamily(L"Arial");
	font_ = new Gdiplus::Font(&fontFamily,16,
			(false)?Gdiplus::FontStyleBold:Gdiplus::FontStyleRegular,Gdiplus::UnitPixel);
	color_text_ = RGB(0,0,0);
	bHandled = FALSE;
	return 0;
}

BOOL BASCOwnerDrawButton::Init(int nPosx,int nPosy,LPCWSTR text,
		COLORREF normal_bkg_color,COLORREF normal_font_color,
		COLORREF pressed_bkg_color,COLORREF pressed_font_color)
{
	normal_bkg_color_ = normal_bkg_color;
	normal_font_color_ = normal_font_color;
	pressed_bkg_color_ = pressed_bkg_color;
	pressed_font_color_ = pressed_font_color;

	nPosx_ = nPosx;
	nPosy_ = nPosy;

	SetTextString(text);

	HWND wndparent=::GetParent(m_hWnd);
	RECT rcClient;
	::GetClientRect(wndparent,&rcClient);

	if (nPosx<0)
	{
		nPosx=rcClient.right+nPosx-button_width_;
	}
	if (nPosy<0)
	{
		nPosy=rcClient.bottom+nPosy-button_height_;
	}

	SetWindowPos(NULL,nPosx,nPosy,button_width_,button_height_,SWP_FRAMECHANGED);
	return 0;
}

utils.h


#ifndef UTILS_H
#define UTILS_H

#include 
#include 
#include 

class Utils
{
public:
	static std::wstring GetProductBinDir()
	{
		wchar_t szbuf[MAX_PATH];  
		::GetModuleFileNameW(NULL,szbuf,MAX_PATH);  
		::PathRemoveFileSpecW(szbuf);
		int length = lstrlen(szbuf);
		szbuf[length] = L'\\';
		szbuf[length+1] = 0;
		return szbuf;
	}

	static void DrawText(Gdiplus::Graphics& graphics,
		Gdiplus::RectF rect,LPCWSTR text,Gdiplus::Font* font,
		const Gdiplus::StringFormat* format,
		Gdiplus::Color* color)
	{
		graphics.DrawString(text,wcslen(text),
			font,rect,format,
			&Gdiplus::SolidBrush(*color));
	}

	static void DrawTextCenter(Gdiplus::Graphics& graphics,
		Gdiplus::RectF rect,LPCWSTR text,Gdiplus::Font* font,
		Gdiplus::Color* color)
	{
		Gdiplus::StringFormat stringFormat;
		stringFormat.SetAlignment(Gdiplus::StringAlignmentCenter);
		stringFormat.SetLineAlignment(Gdiplus::StringAlignmentCenter);
		Utils::DrawText(graphics,rect,text,font,&stringFormat,color);
	}
};


#endif

调用方式

auto color_normal = RGB(13,164,230);
auto button = new BASCOwnerDrawButton();
button->SetInitVisible(true);
button->CreateButton(m_hWnd,(_id)?_id:++window_id_);
button->SetFont(font_16_normal_gdi_);
button->SetHMargin(30)
	.SetVMargin(8)
	.SetRadio(false)
	.SetColorBorder(color_normal)
	.SetRoundRect(false)
	.SetBackgroundColor(RGB(255,255,255));
button->Init(0,0,title,color_normal,RGB(255,255,255),
	RGB(234,245,249),RGB(0,0,0));

图1: 正常状态
在这里插入图片描述
图2: 鼠标进入状态
在这里插入图片描述

参考

WM_DRAWITEM message

NM_CUSTOMDRAW

Button Styles

电子书 Programming Windows by Charles Petzold

TrackMouseEvent function

TRACKMOUSEEVENT

WM_MOUSEHOVER

WM_MOUSELEAVE message

你可能感兴趣的:(ATL/WTL界面开发)