效果图:
头文件声明(CSWSliderCtrl.h):
#pragma once
// CSWSliderCtrl
namespace sw {
class CSWSliderCtrl :
public CWnd
{
DECLARE_DYNAMIC(CSWSliderCtrl)
public:
CSWSliderCtrl();
virtual ~CSWSliderCtrl();
void SetToolTipText(LPCSTR lpszToolTipText, BOOL bActivate = TRUE);
void ActivateTooltip(BOOL bActivate = TRUE);
void NoticeBkgrRefresh();
void CommonConstruct(); // 初始化
void InitToolTip();
private:
CToolTipCtrl m_ToolTip; // 鼠标停留提醒
CDC m_dcBkgr; // 背景DC(保持了父窗体背景图像)
BOOL m_bTransParent; // 背景透明
HCURSOR m_hCursor; // 鼠标手势
CBrush m_brBkgr; // 背景画刷
uint64_t m_nRangeMax; // 最大值
uint64_t m_nRangeMin; // 最小值
uint64_t m_nPos; // 当前位置
HWND m_hParentWnd; // 父窗口句柄
BOOL m_bMouseStay; // 鼠标停留
BOOL m_bDraging; // 正在拖拽
Gdiplus::Bitmap* m_pBitmapSlider;
protected:
virtual BOOL PreTranslateMessage(MSG* pMsg);
protected:
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
afx_msg void OnPaint();
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnMouseLeave();
DECLARE_MESSAGE_MAP()
public:
BOOL Create(UINT nID, CWnd* pParent);
BOOL Create(HWND hParentWnd);
// 设置窗口背景图片
void SetBackgroundColor(COLORREF color, BOOL bRedraw = FALSE);
// 设置鼠标
void SetCursor(HCURSOR hCursor = NULL);
int GetRangeMax() const;
// Retrieves the minimum position for the slider in the trackbar control.
int GetRangeMin() const;
// Retrieves the minimum and maximum positions for the slider in the trackbar control.
void GetRange(_Out_ int& nMin, _Out_ int& nMax) const;
// Sets the minimum position for the slider in the trackbar control.
void SetRangeMin(_In_ int nMin, _In_ BOOL bRedraw = FALSE);
// Sets the maximum position for the slider in the trackbar control.
void SetRangeMax(_In_ int nMax, _In_ BOOL bRedraw = FALSE);
// Sets the minimum and maximum positions for the slider in the trackbar control.
void SetRange(_In_ int nMin, _In_ int nMax, _In_ BOOL bRedraw = FALSE);
// Retrieves the current logical position of the slider in the trackbar control.
int GetPos() const;
// Sets the current logical position of the slider in the trackbar control.
void SetPos(_In_ int nPos);
};
}
源码实现(CSWSliderCtrl.cpp):
#include "pch.h"
#include "CSWSliderCtrl.h"
// CSWSliderCtrl
namespace sw {
CSWSliderCtrl::CSWSliderCtrl()
{
CommonConstruct();
}
CSWSliderCtrl::~CSWSliderCtrl()
{
m_brBkgr.DeleteObject();
}
IMPLEMENT_DYNAMIC(CSWSliderCtrl, CWnd)
BEGIN_MESSAGE_MAP(CSWSliderCtrl, CWnd)
ON_WM_ERASEBKGND()
ON_WM_SETCURSOR()
ON_WM_PAINT()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_MOUSEMOVE()
ON_WM_MOUSELEAVE()
END_MESSAGE_MAP()
void CSWSliderCtrl::CommonConstruct()
{
m_ToolTip.m_hWnd = NULL;
m_brBkgr.CreateSolidBrush(::GetSysColor(COLOR_WINDOW));
m_bTransParent = FALSE;
m_hCursor = NULL;
m_nRangeMax = 0;
m_nRangeMin = 0;
m_nPos = 0;
m_hParentWnd = NULL;
m_bMouseStay = FALSE;
m_pBitmapSlider = sw::CSWImageFactory::getSingletonPtr()->GetSkinItemImage(_T("./skins/滑块.png"));
}
BOOL CSWSliderCtrl::Create(UINT nID, CWnd* pParent)
{
CStatic wndStatic;
if (!wndStatic.SubclassDlgItem(nID, pParent))
return FALSE;
m_hParentWnd = pParent->GetSafeHwnd();
CRect rcWindow;
wndStatic.GetWindowRect(&rcWindow);
pParent->ScreenToClient(&rcWindow);
wndStatic.DestroyWindow();
return CWnd::Create(NULL,
NULL,
(WS_CHILD | WS_VISIBLE),
rcWindow,
pParent,
nID,
NULL);
}
BOOL CSWSliderCtrl::Create(HWND hParentWnd)
{
if (::IsWindow(GetSafeHwnd()))
{
if (hParentWnd != m_hParentWnd)
::SetParent(GetSafeHwnd(), hParentWnd);
return TRUE; //已经创建了就不再创建了
}
m_hParentWnd = hParentWnd;
static LPCTSTR lpszWndClass = NULL;
if (lpszWndClass == NULL)
lpszWndClass = AfxRegisterWndClass(CS_DBLCLKS,
LoadCursor(NULL, IDC_ARROW),
(HBRUSH)::GetStockObject(GRAY_BRUSH));
if (lpszWndClass == NULL)
{
TRACE("AfxRegisterWndClass() Failed!\n");
return FALSE;
}
if (!CreateEx(0,
lpszWndClass,
_T("CSWSliderCtrl"),
WS_CHILD | WS_VISIBLE,
0, 0, 0, 0,
m_hParentWnd,
0))
{
TRACE("CSWSliderCtrl::Create() Failed!\n");
return FALSE;
}
if (GetSafeHwnd() == NULL)//再检查一遍,窗口没有创建成功
{
TRACE("CSWSliderCtrl::Create() Failed!\n");
return FALSE;
}
return TRUE;
}
void CSWSliderCtrl::InitToolTip()
{
if (m_ToolTip.m_hWnd == NULL)
{
m_ToolTip.Create(this, TTS_ALWAYSTIP | TTS_BALLOON);
m_ToolTip.Activate(FALSE);
}
}
void CSWSliderCtrl::SetToolTipText(LPCSTR lpszToolTipText, BOOL bActivate/* = TRUE*/)
{
if (lpszToolTipText == NULL)
return;
InitToolTip();
if (m_ToolTip.GetToolCount() == 0)
{
CRect rcClient;
GetClientRect(rcClient);
m_ToolTip.AddTool(this, (LPCTSTR)lpszToolTipText, rcClient, 1);
}
m_ToolTip.UpdateTipText((LPCTSTR)lpszToolTipText, this, 1);
m_ToolTip.Activate(bActivate);
}
void CSWSliderCtrl::ActivateTooltip(BOOL bActivate/* = TRUE*/)
{
if (m_ToolTip.GetToolCount() == 0)
return;
m_ToolTip.Activate(bActivate);
}
BOOL CSWSliderCtrl::PreTranslateMessage(MSG* pMsg)
{
// TODO: 在此添加专用代码和/或调用基类
InitToolTip();
m_ToolTip.RelayEvent(pMsg);
return CWnd::PreTranslateMessage(pMsg);
}
void CSWSliderCtrl::OnPaint()
{
if (GetSafeHwnd() == NULL)
return;
CPaintDC dc(this); // device context for painting
// TODO: 在此处添加消息处理程序代码
// 不为绘图消息调用 CWnd::OnPaint()
// 获取窗口大小
CRect rcClient;
GetWindowRect(rcClient);
rcClient.OffsetRect(-rcClient.left, -rcClient.top);
// 使用双缓存
sw::CMemDC dcMem(dc, this);
// GDI+构建画布
Gdiplus::Graphics g(dcMem);
// 防止画图产生渐变
g.SetInterpolationMode(InterpolationModeNearestNeighbor);
g.SetPixelOffsetMode(PixelOffsetModeHalf);
// 背景填充黑色
if (!m_bTransParent)
{
LOGBRUSH logBrush;
m_brBkgr.GetLogBrush(&logBrush);
g.Clear(Gdiplus::Color(GetRValue(logBrush.lbColor), GetGValue(logBrush.lbColor), GetBValue(logBrush.lbColor)));
}
ColorMatrix colorMartrix = {
1,0,0,0,0,
0,1,0,0,0,
0,0,1,0,0,
0,0,0,float(255) / 255,0,
0,0,0,0,1 };
ImageAttributes imageAttr;
imageAttr.SetColorMatrix(&colorMartrix);
imageAttr.SetWrapMode(Gdiplus::WrapMode::WrapModeTileFlipXY);
// 进度条背景
Gdiplus::Bitmap* pBitmap = sw::CSWImageFactory::getSingletonPtr()->GetSkinItemImage(_T("./skins/进度条背景.png"));
CSize sz(pBitmap->GetWidth(), pBitmap->GetHeight());
CRect rcClip = { 0, 0, sz.cx / 3, sz.cy };
//Rect rcImage(0, 0, pBitmap->GetWidth() / 3, pBitmap->GetHeight());
//TextureBrush brush(pBitmap, Gdiplus::WrapMode::WrapModeTileFlipX, rcImage);
//g.FillRectangle(&brush, RectF(0, 3, rcClient.Width(), rcClient.Height()));
g.DrawImage(pBitmap,
RectF(0, 3, rcClient.Width(), rcClient.Height() - 6),
rcClip.left, rcClip.top, rcClip.Width(), rcClip.Height(),
UnitPixel, &imageAttr, NULL, NULL);
int nRangeMax = max(m_nRangeMax, 1);
// 进度
if (m_nPos > 0)
{
rcClip = CRect(sz.cx / 3, 0, sz.cx * 2 / 3, sz.cy);
g.DrawImage(pBitmap,
RectF(0, 3, rcClient.Width(), rcClient.Height() - 6),
rcClip.left, rcClip.top, rcClip.Width(), rcClip.Height(),
UnitPixel, &imageAttr, NULL, NULL);
rcClip = CRect(sz.cx * 2 / 3, 0, sz.cx, sz.cy);
g.DrawImage(pBitmap,
RectF(0, 3, m_nPos * rcClient.Width() / nRangeMax, rcClient.Height() - 6),
rcClip.left, rcClip.top, rcClip.Width(), rcClip.Height(),
UnitPixel, &imageAttr, NULL, NULL);
}
// 滑块
if (m_nPos >= 0)
{
if (m_bMouseStay)
rcClip = CRect(m_pBitmapSlider->GetWidth() / 2, 0, m_pBitmapSlider->GetWidth(), m_pBitmapSlider->GetHeight());
else
rcClip = CRect(0, 0, m_pBitmapSlider->GetWidth() / 2, m_pBitmapSlider->GetHeight());
g.DrawImage(m_pBitmapSlider,
RectF(max(0, min(rcClient.Width() - rcClip.Width(), m_nPos * rcClient.Width() / nRangeMax)), 0, rcClip.Width(), rcClip.Height()),
rcClip.left, rcClip.top, rcClip.Width(), rcClip.Height(),
UnitPixel, &imageAttr, NULL, NULL);
}
}
// CSWSliderCtrl message handlers
void CSWSliderCtrl::NoticeBkgrRefresh()
{
if (m_bTransParent)
{
if (m_dcBkgr.m_hDC != NULL)
m_dcBkgr.DeleteDC();
InvalidateRect(NULL, FALSE);
}
}
BOOL CSWSliderCtrl::OnEraseBkgnd(CDC* pDC)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if (m_bTransParent)
{
CRect rcClient;
GetClientRect(&rcClient);
CRect rcWindow; GetWindowRect(rcWindow);
GetParent()->ScreenToClient(rcWindow);
if (m_dcBkgr.m_hDC == NULL)
{
// 复制父窗口背景
CClientDC dcParentBkgr(GetParent());
m_dcBkgr.CreateCompatibleDC(&dcParentBkgr);
HBITMAP hBmp = ::CreateCompatibleBitmap(dcParentBkgr.GetSafeHdc(), rcClient.Width(), rcClient.Height());
HBITMAP hOldBmp = (HBITMAP)m_dcBkgr.SelectObject(hBmp);
m_dcBkgr.BitBlt(0, 0, rcClient.Width(), rcClient.Height(), &dcParentBkgr, rcWindow.left, rcWindow.top, SRCCOPY);
DeleteObject(hBmp);
}
pDC->BitBlt(0, 0, rcClient.Width(), rcClient.Height(), &m_dcBkgr, 0, 0, SRCCOPY);
}
return TRUE;// CWnd::OnEraseBkgnd(pDC);
}
void CSWSliderCtrl::SetBackgroundColor(COLORREF color, BOOL bRedraw)
{
if (m_brBkgr.GetSafeHandle() != NULL)
{
m_brBkgr.DeleteObject();
}
if (color != (COLORREF)-1)
{
m_brBkgr.CreateSolidBrush(color);
}
if (!m_bTransParent && bRedraw && GetSafeHwnd() != NULL)
{
Invalidate();
UpdateWindow();
}
}
void CSWSliderCtrl::SetCursor(HCURSOR hCursor)
{
m_hCursor = hCursor;
if (m_hCursor == NULL)
{
m_hCursor = ::LoadCursor(NULL, IDC_HAND);
}
}
BOOL CSWSliderCtrl::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if (m_hCursor)
{
::SetCursor(m_hCursor);
return TRUE;
}
else
{
if (m_bMouseStay)
::SetCursor(::LoadCursor(NULL, IDC_HAND));
}
return FALSE;
}
int CSWSliderCtrl::GetRangeMax() const
{
return m_nRangeMax;
}
// Retrieves the minimum position for the slider in the trackbar control.
int CSWSliderCtrl::GetRangeMin() const
{
return m_nRangeMin;
}
// Retrieves the minimum and maximum positions for the slider in the trackbar control.
void CSWSliderCtrl::GetRange(_Out_ int& nMin, _Out_ int& nMax) const
{
nMin = m_nRangeMin;
nMax = m_nRangeMax;
}
// Sets the minimum position for the slider in the trackbar control.
void CSWSliderCtrl::SetRangeMin(_In_ int nMin, _In_ BOOL bRedraw/* = FALSE*/)
{
m_nRangeMin = nMin;
if (bRedraw)
{
Invalidate();
UpdateWindow();
}
}
// Sets the maximum position for the slider in the trackbar control.
void CSWSliderCtrl::SetRangeMax(_In_ int nMax, _In_ BOOL bRedraw/* = FALSE*/)
{
m_nRangeMax = nMax;
if (bRedraw)
{
Invalidate();
UpdateWindow();
}
}
// Sets the minimum and maximum positions for the slider in the trackbar control.
void CSWSliderCtrl::SetRange(_In_ int nMin, _In_ int nMax, _In_ BOOL bRedraw/* = FALSE*/)
{
m_nRangeMin = nMin;
m_nRangeMax = nMax;
if (bRedraw)
{
Invalidate();
UpdateWindow();
}
}
// Retrieves the current logical position of the slider in the trackbar control.
int CSWSliderCtrl::GetPos() const
{
return m_nPos;
}
// Sets the current logical position of the slider in the trackbar control.
void CSWSliderCtrl::SetPos(_In_ int nPos)
{
if (!m_bDraging)
{
m_nPos = nPos;
Invalidate();
UpdateWindow();
}
}
}
void sw::CSWSliderCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if (m_bMouseStay)
{
CRect rcClient;
GetClientRect(rcClient);
m_nPos = point.x * m_nRangeMax / rcClient.Width();
::PostMessage(m_hParentWnd, TBM_SETPOS, GetDlgCtrlID(), m_nPos);
InvalidateRect(NULL);
UpdateWindow();
m_bDraging = TRUE;
SetCapture();
}
else
{
if ((MK_LBUTTON & nFlags) && m_nRangeMax > 0)
{
CRect rcClient;
GetClientRect(rcClient);
m_nPos = point.x * m_nRangeMax / rcClient.Width();
::PostMessage(m_hParentWnd, TBM_SETPOS, GetDlgCtrlID(), m_nPos);
InvalidateRect(NULL);
UpdateWindow();
}
}
CWnd::OnLButtonDown(nFlags, point);
}
void sw::CSWSliderCtrl::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if (m_bDraging)
{
m_bDraging = FALSE;
ReleaseCapture();
}
m_bMouseStay = FALSE;
CWnd::OnLButtonUp(nFlags, point);
}
void sw::CSWSliderCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
int nRangeMax = max(m_nRangeMax, 1);
CRect rcClient;
GetClientRect(rcClient);
CRect rcSlider = CRect(m_nPos * rcClient.Width() / nRangeMax, 0, m_pBitmapSlider->GetWidth() / 2, m_pBitmapSlider->GetHeight());
rcSlider.right += rcSlider.left;
rcSlider.bottom += rcSlider.top;
point.x = max(0, point.x);
if (PtInRect(&rcSlider, point))
{
if (!m_bMouseStay)
{
m_bMouseStay = TRUE;
InvalidateRect(NULL);
UpdateWindow();
}
else if (m_bDraging && (MK_LBUTTON & nFlags) && m_nRangeMax > 0)
{
m_nPos = point.x * m_nRangeMax / rcClient.Width();
::PostMessage(m_hParentWnd, TBM_SETPOS, GetDlgCtrlID(), m_nPos);
InvalidateRect(NULL);
UpdateWindow();
}
}
else if (m_bMouseStay && (MK_LBUTTON & nFlags) && m_nRangeMax > 0)
{
m_nPos = max(0, point.x * m_nRangeMax / rcClient.Width());
::PostMessage(m_hParentWnd, TBM_SETPOS, GetDlgCtrlID(), m_nPos);
InvalidateRect(NULL);
UpdateWindow();
}
CWnd::OnMouseMove(nFlags, point);
}
void sw::CSWSliderCtrl::OnMouseLeave()
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
m_bMouseStay = FALSE;
CWnd::OnMouseLeave();
}