windows虽然有自带的调色板,但是无奈那种老式的对话框对于截图来说并不会那么美观,因此需要自己实现一个调色板,幸好在以前的项目中有过类似的经验(以前实现过一个调色板)这次很顺利的就完成了代码的编写。

先把头文件提供下

#pragma once


// COwnerDrawPallet
#define  COLORWIDTHCOUNT	8
#define  COLORHIGHCOUNT		2
class COwnerDrawPallet : public CStatic
{
	DECLARE_DYNAMIC(COwnerDrawPallet)

public:
	COwnerDrawPallet();
	virtual ~COwnerDrawPallet();
	void OnPaint();
	void OnLButtonDown(UINT nFlags, CPoint point);
	void OnMouseMove(UINT nFlags, CPoint point);
	COLORREF GetSelectColor();
	struct ColorInfo
	{
		CRect	 rt;
		COLORREF color;
		ColorInfo()
		{
			rt = CRect(0,0,0,0);
			color = -1;
		}

		ColorInfo & operator = (const ColorInfo &clr)
		{
			rt = clr.rt;
			color = clr.color;
			return *this;
		}
	};
protected:

	void DrawSelectColor( CDC &memDC );
	void DrawMoveOverColor( CDC * pDC );
	void GetColorInfo( CDC &memDC );
	afx_msg LRESULT OnMouseHover(WPARAM wParam, LPARAM lParam);
	afx_msg LRESULT OnMouseLeave(WPARAM wParam, LPARAM lParam);

protected:
	DECLARE_MESSAGE_MAP()
	CBitmap		m_Bitmap;
	BITMAP		m_bm;
	CRect		m_rtShowColor;
	BOOL		m_bLeftBtnDown;
	BOOL		m_bFirst;
	COLORREF    m_clrSelect;
	ColorInfo	m_MouseInRect;
	ColorInfo	m_ColorInfo[COLORHIGHCOUNT][COLORWIDTHCOUNT];
};



下面一步一步说怎么实现这个调色板,可以更改图片,我吧实现这类调色板的原理讲清楚,就可以以不变应万变了。


自定义调色板首先需要一张调色板的位图例如

PictureSelector 截图回调_i++

先把图片载入

COwnerDrawPallet::COwnerDrawPallet()
{
	m_Bitmap.LoadBitmap(IDB_BMP_COLOR);
	m_Bitmap.GetBitmap(&m_bm);
	m_rtShowColor = CRect(CPoint(9,5), CPoint(33,29));//最大色块位置
	m_bFirst = TRUE;
	m_bLeftBtnDown = FALSE;
	m_clrSelect = -1;
}

接下去获取每个色块的位置、颜色保存到数组m_ColorInfo中

void COwnerDrawPallet::GetColorInfo( CDC &memDC )
{
	
	if (m_bFirst)
	{
		for (int i = 0; i < COLORHIGHCOUNT; i++)
		{
			for (int j = 0; j < COLORWIDTHCOUNT; j++)
			{
				m_ColorInfo[i][j].rt = CRect(CPoint(40 + 15*j, 4 + 15*i), CSize(11,11));
				CPoint ptCenter = m_ColorInfo[i][j].rt.CenterPoint();
				m_ColorInfo[i][j].color = memDC.GetPixel(ptCenter);
			}
		}
		m_bFirst = FALSE;
	}
}



上面代码CRect(CPoint(40 + 15*j, 4 + 15*i), CSize(11,11))这个是关键其中40是第一个小色块的左上角坐标X坐标,4为Y坐标(不包括色块的边框),色块的大小(因为是正方形)11,左右 已经上下2个色块的距离都是15,大家可以根据自己的调色板的色块具体位置调整相应的大小,取到了色块的位置之后,去色块中心点颜色就调用GetPixel函数实现

接下去就是显示选择的颜色(预览颜色)如上图最大的那个色块

void COwnerDrawPallet::DrawSelectColor( CDC &memDC )
{
	if (m_bLeftBtnDown)
	{
		memDC.FillSolidRect(m_rtShowColor, m_MouseInRect.color);
		m_bLeftBtnDown = FALSE;
		m_clrSelect = m_MouseInRect.color;
	}
void COwnerDrawPallet::OnPaint()
{
	CStatic::OnPaint();
	CDC *pDC = GetDC();
	CDC memDC;
	memDC.CreateCompatibleDC(pDC);
	CBitmap *pOldBitmp = memDC.SelectObject(&m_Bitmap);
	GetColorInfo(memDC);
	DrawSelectColor(memDC);

	pDC->BitBlt(0,0,m_bm.bmWidth,m_bm.bmHeight, &memDC,0,0,SRCCOPY);
	DrawMoveOverColor(pDC);

	memDC.SelectObject(pOldBitmp);
	memDC.DeleteDC();
	ReleaseDC(pDC);
}



选择颜色时通知onpaint()

void COwnerDrawPallet::OnLButtonDown(UINT nFlags, CPoint point)
{
	for (int i = 0; i < COLORHIGHCOUNT; i++)
	{
		for (int j = 0; j < COLORWIDTHCOUNT; j++)
		{
			if (m_ColorInfo[i][j].rt.PtInRect(point))
			{
				m_MouseInRect = m_ColorInfo[i][j];
				m_bLeftBtnDown = TRUE;
				Invalidate(FALSE);
				break;
			}
		}
	}
	
	CStatic::OnLButtonDown(nFlags, point);
}

这样基本都完成,那么如果还想增加一些效果如当鼠标在色块上移动显示框住这个色块的效果

void COwnerDrawPallet::OnMouseMove(UINT nFlags, CPoint point)
{
	TRACKMOUSEEVENT csTME;
	csTME.cbSize = sizeof (csTME);
	csTME.dwFlags = TME_LEAVE|TME_HOVER;
	csTME.hwndTrack = m_hWnd ;// 指定要 追踪 的窗口 
	csTME.dwHoverTime = 5;  // 鼠标在按钮上停留超过 5ms ,才认为状态为 HOVER
	::_TrackMouseEvent (&csTME); // 开启 Windows 的 WM_MOUSELEAVE , WM_MOUSEHOVER 事件支持

	CStatic::OnMouseMove(nFlags, point);
}

LRESULT COwnerDrawPallet::OnMouseHover(WPARAM wParam, LPARAM lParam)
{
	CPoint point(0,0);
	GetCursorPos(&point);
	ScreenToClient(&point);
	for (int i = 0; i < COLORHIGHCOUNT; i++)
	{
		for (int j = 0; j < COLORWIDTHCOUNT; j++)
		{
			if (m_ColorInfo[i][j].rt.PtInRect(point))
			{
				m_MouseInRect = m_ColorInfo[i][j];
				Invalidate(TRUE);
				break;
			}
		}
	}
	return TRUE;
}
LRESULT COwnerDrawPallet::OnMouseLeave(WPARAM wParam, LPARAM lParam)
{
	m_MouseInRect.color = -1;
	m_MouseInRect.rt = CRect(0,0,0,0);
	Invalidate(FALSE);
	return TRUE;
}

void COwnerDrawPallet::DrawMoveOverColor( CDC * pDC )
{
	if (m_MouseInRect.color != -1)
	{
		CPen pen1, pen2;
		pen1.CreatePen(PS_SOLID, 1, RGB(0,0,255));
		CPen*pOldPen = pDC->SelectObject(&pen1);
		CRect rtDrawSelect = m_MouseInRect.rt;
		rtDrawSelect.TopLeft().Offset(-2, -2);
		rtDrawSelect.BottomRight().Offset(2, 2);
		pDC->SelectStockObject(NULL_BRUSH);
		pDC->Rectangle(rtDrawSelect);
		pDC->SelectObject(pOldPen);
		pen1.DeleteObject();
		pen2.CreatePen(PS_SOLID, 1, RGB(255,255,255));
		pOldPen = pDC->SelectObject(&pen2);
		rtDrawSelect = m_MouseInRect.rt;
		rtDrawSelect.TopLeft().Offset(-1, -1);
		rtDrawSelect.BottomRight().Offset(1, 1);
		pDC->Rectangle(rtDrawSelect);
		pDC->SelectObject(pOldPen);	
		pen2.DeleteObject();
	}
}

先激活鼠标的2个响应

OnMouseLeave鼠标离开控件时响应

OnMouseHover鼠标在控件上移动时响应



最后我们需要知道我们选择了什么颜色方便外部使用

COLORREF COwnerDrawPallet::GetSelectColor()
{
	if (m_clrSelect != -1)
	{
		return m_clrSelect;
	}
	return RGB(255,0,0);
}

OK原理就是这样的


下面把完整的.cpp贴下

/********************************************************
/* 
/* 文件名称:OwnerDrawPallet.cpp
/* 摘    要:自绘调色板控件实现
/* 当前版本:1.0
/* 作    者:wangzhiyong
/* 创建日期:2013年5月7日星期二
*********************************************************/
// OwnerDrawPallet.cpp : 实现文件
//

#include "stdafx.h"
#include "OwnerDrawPallet.h"


#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
// COwnerDrawPallet

IMPLEMENT_DYNAMIC(COwnerDrawPallet, CStatic)

COwnerDrawPallet::COwnerDrawPallet()
{
	m_Bitmap.LoadBitmap(IDB_BMP_COLOR);
	m_Bitmap.GetBitmap(&m_bm);
	m_rtShowColor = CRect(CPoint(9,5), CPoint(33,29));
	m_bFirst = TRUE;
	m_bLeftBtnDown = FALSE;
	m_clrSelect = -1;
}

COwnerDrawPallet::~COwnerDrawPallet()
{
	m_Bitmap.DeleteObject();
}

void COwnerDrawPallet::OnPaint()
{
	CStatic::OnPaint();
	CDC *pDC = GetDC();
	CDC memDC;
	memDC.CreateCompatibleDC(pDC);
	CBitmap *pOldBitmp = memDC.SelectObject(&m_Bitmap);
	GetColorInfo(memDC);
	DrawSelectColor(memDC);

	pDC->BitBlt(0,0,m_bm.bmWidth,m_bm.bmHeight, &memDC,0,0,SRCCOPY);
	DrawMoveOverColor(pDC);

	memDC.SelectObject(pOldBitmp);
	memDC.DeleteDC();
	ReleaseDC(pDC);
}

BEGIN_MESSAGE_MAP(COwnerDrawPallet, CStatic)
	ON_WM_PAINT()
	ON_WM_MOUSEMOVE()
	ON_WM_LBUTTONDOWN()
	ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover)
	ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
END_MESSAGE_MAP()

void COwnerDrawPallet::GetColorInfo( CDC &memDC )
{
	
	if (m_bFirst)
	{
		for (int i = 0; i < COLORHIGHCOUNT; i++)
		{
			for (int j = 0; j < COLORWIDTHCOUNT; j++)
			{
				m_ColorInfo[i][j].rt = CRect(CPoint(40 + 15*j, 4 + 15*i), CSize(11,11));
				CPoint ptCenter = m_ColorInfo[i][j].rt.CenterPoint();
				m_ColorInfo[i][j].color = memDC.GetPixel(ptCenter);
			}
		}
		m_bFirst = FALSE;
	}
}



// COwnerDrawPallet 消息处理程序
void COwnerDrawPallet::OnLButtonDown(UINT nFlags, CPoint point)
{
	for (int i = 0; i < COLORHIGHCOUNT; i++)
	{
		for (int j = 0; j < COLORWIDTHCOUNT; j++)
		{
			if (m_ColorInfo[i][j].rt.PtInRect(point))
			{
				m_MouseInRect = m_ColorInfo[i][j];
				m_bLeftBtnDown = TRUE;
				Invalidate(FALSE);
				break;
			}
		}
	}
	
	CStatic::OnLButtonDown(nFlags, point);
}

void COwnerDrawPallet::OnMouseMove(UINT nFlags, CPoint point)
{
	TRACKMOUSEEVENT csTME;
	csTME.cbSize = sizeof (csTME);
	csTME.dwFlags = TME_LEAVE|TME_HOVER;
	csTME.hwndTrack = m_hWnd ;// 指定要 追踪 的窗口 
	csTME.dwHoverTime = 5;  // 鼠标在按钮上停留超过 5ms ,才认为状态为 HOVER
	::_TrackMouseEvent (&csTME); // 开启 Windows 的 WM_MOUSELEAVE , WM_MOUSEHOVER 事件支持

	CStatic::OnMouseMove(nFlags, point);
}

LRESULT COwnerDrawPallet::OnMouseHover(WPARAM wParam, LPARAM lParam)
{
	CPoint point(0,0);
	GetCursorPos(&point);
	ScreenToClient(&point);
	for (int i = 0; i < COLORHIGHCOUNT; i++)
	{
		for (int j = 0; j < COLORWIDTHCOUNT; j++)
		{
			if (m_ColorInfo[i][j].rt.PtInRect(point))
			{
				m_MouseInRect = m_ColorInfo[i][j];
				Invalidate(TRUE);
				break;
			}
		}
	}
	return TRUE;
}
LRESULT COwnerDrawPallet::OnMouseLeave(WPARAM wParam, LPARAM lParam)
{
	m_MouseInRect.color = -1;
	m_MouseInRect.rt = CRect(0,0,0,0);
	Invalidate(FALSE);
	return TRUE;
}

void COwnerDrawPallet::DrawMoveOverColor( CDC * pDC )
{
	if (m_MouseInRect.color != -1)
	{
		CPen pen1, pen2;
		pen1.CreatePen(PS_SOLID, 1, RGB(0,0,255));
		CPen*pOldPen = pDC->SelectObject(&pen1);
		CRect rtDrawSelect = m_MouseInRect.rt;
		rtDrawSelect.TopLeft().Offset(-2, -2);
		rtDrawSelect.BottomRight().Offset(2, 2);
		pDC->SelectStockObject(NULL_BRUSH);
		pDC->Rectangle(rtDrawSelect);
		pDC->SelectObject(pOldPen);
		pen1.DeleteObject();
		pen2.CreatePen(PS_SOLID, 1, RGB(255,255,255));
		pOldPen = pDC->SelectObject(&pen2);
		rtDrawSelect = m_MouseInRect.rt;
		rtDrawSelect.TopLeft().Offset(-1, -1);
		rtDrawSelect.BottomRight().Offset(1, 1);
		pDC->Rectangle(rtDrawSelect);
		pDC->SelectObject(pOldPen);	
		pen2.DeleteObject();
	}
}

/*************************************************************/
//  函数名称:COwnerDrawPallet::DrawSelectColor
//  参    数:CDC &memDC    
//  返    回:void
//  函数作用:绘制预览颜色
//  修改日期:2013年5月7日星期二  By wangzhiyong
/*************************************************************/
void COwnerDrawPallet::DrawSelectColor( CDC &memDC )
{
	if (m_bLeftBtnDown)
	{
		memDC.FillSolidRect(m_rtShowColor, m_MouseInRect.color);
		m_bLeftBtnDown = FALSE;
		m_clrSelect = m_MouseInRect.color;
	}
}

COLORREF COwnerDrawPallet::GetSelectColor()
{
	if (m_clrSelect != -1)
	{
		return m_clrSelect;
	}
	return RGB(255,0,0);
}

把上面我给的图片加入资源,基本不用改代码就能使用了,如果觉得图片不好,纳闷可以自己绘制一个根据前面讲的稍微修改下色块的位置 注意一行中色块间距必须保持一致,行和行间最好保持一致免得多次取位置