请根据我提供的滚动条控件源码,重构一个更高效的控件给我
#ifndef _SCROLL_BAR_H_
#define _SCROLL_BAR_H_
#include "BaseWnd/CBaseWnd.h"
class ScrollBar : public MFC::CBaseWnd
{
public:
ScrollBar();
~ScrollBar();
BOOL Create(const int _iBar, const CRect& _Rect, CWnd* _pParent, const int _iArrowFlags = ESB_ENABLE_BOTH, const int _iSpace = 3, const int _iMinSlider = 10);
BOOL SetScrollInfo(SCROLLINFO& _refScrollInfo, BOOL _bRedraw = TRUE);
BOOL SetScrollPos(int _iPos, BOOL _bRedraw = TRUE);
void SetTarget(CWnd* _pTargetWnd);
private:
int m_iBar; //此滚动条的风格: SB_HORZ = 水平横向滚动条, SB_VERT = 垂直纵向滚动条
int m_iSpace; //滚动条滑块通道两边的空白间隔
int m_iMinSlider; //滑块的最小宽度,不可能无限小吧,滑块过小了肉眼都难看到了
int m_iArrowFlags; //两边是否开启箭头
float m_fRatio; //比率: 如: 滚动条滚动 1pixl, 目标控件该移动多少 pix? 或者反过来,目标控件移动了10pix , 那么滚动条应该移动多少pix?
CRect m_rcLTUP; //滚动条(左边/顶部)的箭头范围 3个范围组成完整的滚动条
CRect m_rcScroll; //滚动条通道的范围 3个范围组成完整的滚动条
CRect m_rcRTDN; //滚动条(右边/底部)的箭头范围 3个范围组成完整的滚动条
SCROLLINFO m_infoScroll; //滚动条的信息 这2个info是相互关联依赖
SCROLLBARINFO m_infoScrollBar; //滚动条的滑块信息 这2个info是相互关联依赖
//以下解释2个info的关联性
/*
typedef struct tagSCROLLINFO
{
UINT cbSize; //size of(SCROLLINFO)
UINT fMask; //SIF_ALL 即nMin, nMax, nPage, nPos, nTrackPos 5个变量值都得到更改
int nMin; //一般置 0
int nMax; //目标控件的最大可见内容, 比如我的目标控件宽度: 666 pix, 控件内有一行文字内容,此行文字占:977 pix, 那么此行内容就有: 977-666 pix内容是看不到了,那么最大可见内容nMax=977
UINT nPage; //目标控件的宽度: 666 pix, 一般一行文字的最前面和最后面都有一个占1pix的光标,正如我们打字那个光标一样, 666-2pix = 664, 那么nPag = 664
int nPos; //这里我预设为: 29, 最下面会给出这个29的计算方法
int nTrackPos; //这个值是以前旧nPos, 等更新完成后,也会变成29
} SCROLLINFO, FAR* LPSCROLLINFO;
*/
/*
typedef struct tagSCROLLBARINFO
{
DWORD cbSize; //size of(SCROLLBARINFO)
RECT rcScrollBar; //滚动条的全部范围: 我预设此滚动条是: 666*17 pix, 水平横向滚动条,两头带17 pix的箭头按钮
int dxyLineButton; //滚动条2头的箭头宽度: 17pix
int xyThumbTop; //滑块左边位于滚动条的位置, 这里是36 pix 这个36 pix和上面的nPos 29pix是有关联性的
int xyThumbBottom; //滑块右边位于滚动条的位置, 这里是465 pix, 那么这个滚动条中间滑块的宽度就是: 465-36
int reserved; //windows预留位置,不用管
DWORD rgstate[CCHILDREN_SCROLLBAR + 1]; //当滚动可见,这里全部为0, 可视状态开关
} SCROLLBARINFO, * PSCROLLBARINFO, * LPSCROLLBARINFO;
*/
/*
* xyThumbTop = 36 计算出 nPos = 29 :
* xyThumbTop-17 = 36-17 = 19pix, 先去除左边箭头的宽度得到真实: 当前滚动条已经滚动的距离
* 计算比率: 滚动条的长度/目标控件的最大可见内容 = (666-(17*2))/977 = 0.6468781 (17*2)是去掉2个箭头距离
* xyThumbTop / 比率 = 19/0.6468781 = 29.37183991 = (int)29 = nPos = 29
* nPos逆向计算xyThumbTop = 29*0.6468781 = 19
* 总结: SCROLLBARINFO信息控制滚动条内部信息,SCROLLINFO控制外部目标控件的信息
*/
CRect m_rcSlider; //滚动条内滑块的范围
COLORREF m_clrSlider; //滑块的状态颜色
COLORREF m_clrSlider_Def; //滑块无操作状态下的颜色
COLORREF m_clrSlider_Hover; //鼠标悬停在滑块上面的颜色
COLORREF m_clrSlider_Click; //鼠标左键按下滑块的颜色
BOOL m_bIsTrack; //鼠标触发WM_MOUSEMOVE一次就触发一次鼠标悬停?明显太浪费了,所以这个bIsTrack控制,确保已经触发完成鼠标悬停事件再重置bIsTrack
BOOL m_bIsCaptrue; //监视这个控件是不是正在 捕获焦点 中
CPoint m_ptMouseLastPoint; //鼠标移动: 从A点移动到B点, m_ptMouseLastPoint代表A点(存储), B点在WM_MOUSEMOVE事件能得到
int m_iLTUPBtn; //(左边/顶部)箭头的(宽度/长度)
int m_iRTDNBtn; //(右边/底部)箭头的(宽度/长度)
CRgn m_rgnLTUPBtn; //(左边/顶部)三角形箭头的绘画路径
CRgn m_rgnRTDNBtn; //(右边/底部)三角形箭头的绘画路径
COLORREF m_clrArrow; //箭头的状态颜色
COLORREF m_clrArrow_Def; //箭头无操作状态颜色
COLORREF m_clrArrow_Hover; //鼠标悬停在箭头上的颜色
COLORREF m_clrArrow_Click; //鼠标左键按下箭头的颜色
BOOL m_bIsEnd; //当前滚动条已经达到尽头
CWnd* m_pTargetWnd; //关联的目标控件
float m_fCorrectionLT; //补正(左箭头/顶部箭头)移动距离
float m_fCorrectionRD; //补正(右箭头/底部箭头)移动距离
public:
DECLARE_MESSAGE_MAP()
afx_msg void OnPaint();
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnMouseHover(UINT nFlags, CPoint point);
afx_msg void OnMouseLeave();
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
virtual void OnMouseLeave(const CPoint& _point);
virtual void OnDragSlider(const CPoint& _point);
virtual void OnClickLTUPBtn();//点击了(左箭头/顶部箭头)
virtual void OnClickRTDNBtn();//点击了(右箭头/底部箭头)
};
#endif#include "ScrollBar.h"
ScrollBar::ScrollBar():
m_iBar(0),
m_iSpace(0),
m_iMinSlider(0),
m_iArrowFlags(ESB_ENABLE_BOTH),
m_fRatio(0.0f),
m_bIsTrack(FALSE),
m_bIsCaptrue(FALSE),
m_iLTUPBtn(0),
m_iRTDNBtn(0),
m_bIsEnd(FALSE),
m_pTargetWnd(NULL),
m_fCorrectionLT(0.0f),
m_fCorrectionRD(0.0f)
{
//初始化一些基本结构
m_rcLTUP = { 0, 0, 0, 0 };
m_rcScroll = {0, 0, 0, 0};
m_rcRTDN = {0, 0, 0, 0};
memset(&m_infoScroll, 0, sizeof m_infoScroll);
m_infoScroll.cbSize = sizeof m_infoScroll;
memset(&m_infoScrollBar, 0, sizeof m_infoScrollBar);
m_infoScrollBar.cbSize = sizeof m_infoScrollBar;
m_rcSlider = { 0,0,0,0 };
m_clrSlider_Def = RGB(104, 104, 104);
m_clrSlider_Hover = RGB(158, 158, 158);
m_clrSlider_Click = RGB(239, 235, 239);
m_clrSlider = m_clrSlider_Def;
m_ptMouseLastPoint = { 0,0 };
m_clrArrow_Def = RGB(104, 104, 104);
m_clrArrow_Hover = RGB(158, 158, 158);
m_clrArrow_Click = RGB(239, 235, 239);
m_clrArrow = m_clrArrow_Def;
}
ScrollBar::~ScrollBar()
{
//箭头路径销毁: windwos要求释放资源
m_rgnLTUPBtn.DeleteObject();
m_rgnRTDNBtn.DeleteObject();
}
BOOL ScrollBar::Create(const int _iBar, const CRect& _Rect, CWnd* _pParent, const int _iArrowFlags, const int _iSpace, const int _iMinSlider)
{
//准备滚动条数据
m_iBar = _iBar; //存储滚动条风格: SB_HORZ=水平横向滚动条; SB_VERT=垂直纵向滚动条
m_iSpace = _iSpace; //存储滚动条滑块两边空白间隔
m_iArrowFlags = _iArrowFlags; //存储滚动条的箭头开关值: 0=两个都开启; 1=(左边/顶部)的箭头关闭; 2=(右边/底部)的箭头关闭; 3=两个都关闭
m_iMinSlider = _iMinSlider; //存储滚动条滑块的最小(宽度/长度),不可能无限短,太短肉眼都看不见了
CRect recWnd = { 0, 0, 0,0 };
if (CBaseWnd::Create("scroll bar", _Rect, _pParent, NULL, WS_CHILD | WS_VISIBLE))//创建控件
{
//控件创建成功
m_infoScrollBar.rcScrollBar = {0, 0, _Rect.Width(), _Rect.Height()};//设置滚动条所占此控件的范围,一般是全部
recWnd = m_infoScrollBar.rcScrollBar;
switch (_iBar)
{
case SB_HORZ:
m_infoScrollBar.dxyLineButton = _Rect.Height();//如果是水平滚动条, 箭头按钮长度就是控件的宽度
break;
case SB_VERT:
m_infoScrollBar.dxyLineButton = _Rect.Width();//如果是垂直滚动条, 箭头按钮的宽度就是控件的长度
break;
}
switch (_iArrowFlags)
{
case ESB_ENABLE_BOTH://2个箭头都开启
m_iLTUPBtn = m_infoScrollBar.dxyLineButton;
m_iRTDNBtn = m_infoScrollBar.dxyLineButton;
break;
case ESB_DISABLE_LTUP://关闭(左边/顶部)的箭头
m_iLTUPBtn = 0;
m_iRTDNBtn = m_infoScrollBar.dxyLineButton;
break;
case ESB_DISABLE_RTDN://关闭(右边/底部)的箭头
m_iLTUPBtn = m_infoScrollBar.dxyLineButton;
m_iRTDNBtn = 0;
break;
case ESB_DISABLE_BOTH://2个箭头都关闭
m_iLTUPBtn = 0;
m_iRTDNBtn = 0;
break;
}
switch (_iBar)
{
case SB_HORZ:
{
m_infoScroll.nMax = _Rect.Width();//初始化目标控件里面的内容最大可视范围
m_infoScroll.nPage = _Rect.Width();//初始化目标控件的最大范围
m_rcSlider = { recWnd.left + m_iLTUPBtn, recWnd.top + m_iSpace, recWnd.right - m_iRTDNBtn, recWnd.bottom - m_iSpace };//初始化滑块坐标范围
m_infoScrollBar.xyThumbTop = m_rcSlider.left;//滑块的左边坐标
m_infoScrollBar.xyThumbBottom = m_rcSlider.right;//滑块的右边坐标
CPoint vecPoint[3];
//绘画左边箭头三角形
vecPoint[0] = { m_iSpace+1, m_infoScrollBar.dxyLineButton / 2 };
vecPoint[1] = { m_infoScrollBar.dxyLineButton - m_iSpace*2, m_infoScrollBar.dxyLineButton - m_iSpace };
vecPoint[2] = { m_infoScrollBar.dxyLineButton - m_iSpace*2 , m_iSpace-1 };
m_rgnLTUPBtn.CreatePolygonRgn(vecPoint, 3, WINDING);
//绘画右边箭头三角形
vecPoint[0] = {recWnd.Width()-(m_iSpace + 1), m_infoScrollBar.dxyLineButton / 2 };
vecPoint[1] = {recWnd.Width()- (m_infoScrollBar.dxyLineButton - m_iSpace * 2), m_infoScrollBar.dxyLineButton - m_iSpace };
vecPoint[2] = {recWnd.Width() - (m_infoScrollBar.dxyLineButton - m_iSpace * 2), m_iSpace-1 };
m_rgnRTDNBtn.CreatePolygonRgn(vecPoint, 3, WINDING);
//分配范围
m_rcLTUP = { recWnd.left, recWnd.top, recWnd.left + m_iLTUPBtn, recWnd.bottom };//左箭头范围
m_rcRTDN = { recWnd.right - m_iRTDNBtn, recWnd.top, recWnd.right, recWnd.bottom };//右箭头范围
m_rcScroll = {recWnd.left+m_iLTUPBtn, recWnd.top, recWnd.right-m_iRTDNBtn, recWnd.bottom};//中间滚动条通道
m_rcSlider = {m_rcScroll.left, m_rcScroll.top+m_iSpace, m_rcScroll.right, m_rcScroll.bottom-m_iSpace};
break;
}
case SB_VERT:
{
m_infoScroll.nMax = recWnd.Height();//初始化目标控件里面的内容最大可视范围
m_infoScroll.nPage = recWnd.Height();//初始化目标控件的最大范围
m_rcSlider = {recWnd.left+m_iSpace, recWnd.top+m_iLTUPBtn, recWnd.right-m_iSpace, recWnd.bottom-m_iRTDNBtn};//初始化滑块坐标范围
m_infoScrollBar.xyThumbTop = m_rcSlider.top;//滑块顶部的坐标
m_infoScrollBar.xyThumbBottom = m_rcSlider.bottom;//滑块底部的坐标
CPoint vecPoint[3];
//绘画顶部箭头的三角形
vecPoint[0] = { m_infoScrollBar.dxyLineButton / 2 , m_iSpace };
vecPoint[1] = { m_iSpace-1, m_infoScrollBar.dxyLineButton - m_iSpace * 2 };
vecPoint[2] = { m_infoScrollBar.dxyLineButton - m_iSpace, m_infoScrollBar.dxyLineButton - m_iSpace*2 };
m_rgnLTUPBtn.CreatePolygonRgn(vecPoint, 3, WINDING);
//绘画底部箭头的三角形
vecPoint[0] = { m_infoScrollBar.dxyLineButton / 2 , recWnd.Height() - m_iSpace - 1 };
vecPoint[1] = { m_iSpace , recWnd.Height() - m_infoScrollBar.dxyLineButton + m_iSpace * 2 };
vecPoint[2] = { m_infoScrollBar.dxyLineButton - m_iSpace, recWnd.Height() - m_infoScrollBar.dxyLineButton + m_iSpace * 2 };
m_rgnRTDNBtn.CreatePolygonRgn(vecPoint, 3, WINDING);
//分配范围
m_rcLTUP = { recWnd.left, recWnd.top, recWnd.right, recWnd.top+m_iLTUPBtn };//上箭头范围
m_rcRTDN = { recWnd.left, recWnd.bottom - m_iRTDNBtn, recWnd.right, recWnd.bottom };//下箭头范围
m_rcScroll = { recWnd.left, recWnd.top + m_iLTUPBtn, recWnd.right, recWnd.bottom - m_iRTDNBtn };//中间滚动条通道
m_rcSlider = {m_rcScroll.left+m_iSpace, m_rcScroll.top, m_rcScroll.right-m_iSpace, m_rcScroll.bottom};
break;
}
}
return TRUE;
}
return FALSE;
}
BEGIN_MESSAGE_MAP(ScrollBar, MFC::CBaseWnd)
ON_WM_PAINT()
ON_WM_MOUSEMOVE()
ON_WM_MOUSEHOVER()
ON_WM_MOUSELEAVE()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_HSCROLL()
ON_WM_VSCROLL()
END_MESSAGE_MAP()
void ScrollBar::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: 在此处添加消息处理程序代码
// 不为绘图消息调用 CBaseWnd::OnPaint()
CBrush brush;
brush.CreateSolidBrush(m_clrArrow);
m_MemDC.FillSolidRect(m_rcWnd, RGB(62, 62, 66));//背景色
m_MemDC.FillSolidRect(m_rcSlider, m_clrSlider);//滑块色
if(m_iLTUPBtn) m_MemDC.FillRgn(&m_rgnLTUPBtn, &brush);
if(m_iRTDNBtn) m_MemDC.FillRgn(&m_rgnRTDNBtn, &brush);
brush.DeleteObject();
dc.BitBlt(0, 0, m_rcWnd.Width(), m_rcWnd.Height(), &m_MemDC, 0, 0, SRCCOPY);
//更新scroll bar info数据
switch (m_iBar)
{
case SB_HORZ:
m_infoScrollBar.xyThumbTop = m_rcSlider.left;
m_infoScrollBar.xyThumbBottom = m_rcSlider.right;
break;
case SB_VERT:
m_infoScrollBar.xyThumbTop = m_rcSlider.top;
m_infoScrollBar.xyThumbBottom = m_rcSlider.bottom;
break;
}
}
void ScrollBar::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if (!m_bIsTrack && !m_bIsCaptrue)
{
m_bIsTrack = TRUE;
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(tme);
tme.hwndTrack = m_hWnd;
tme.dwFlags = TME_LEAVE | TME_HOVER;
tme.dwHoverTime = 1;// 鼠标在按钮上停留超过 1ms ,才认为成功
_TrackMouseEvent(&tme);
}
if (m_bIsCaptrue)
{
OnDragSlider(point);
m_ptMouseLastPoint = point;
}
CBaseWnd::OnMouseMove(nFlags, point);
}
void ScrollBar::OnMouseHover(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
OnMouseLeave(m_ptMouseLastPoint);
if (PtInRect(m_rcLTUP, point))
{
//鼠标悬停在(左边/顶部)箭头上面
m_clrArrow = m_clrArrow_Hover;
InvalidateRect(m_rcLTUP);
}
else if (PtInRect(m_rcRTDN, point))
{
//鼠标悬停在(右边/底部)箭头上面
m_clrArrow = m_clrArrow_Hover;
InvalidateRect(m_rcRTDN);
}
else if (PtInRect(m_rcSlider, point))
{
//鼠标悬停在滑块上面
m_clrSlider = m_clrSlider_Hover;
InvalidateRect(m_rcSlider);
}
m_ptMouseLastPoint = point;
m_bIsTrack = FALSE;
CBaseWnd::OnMouseHover(nFlags, point);
}
void ScrollBar::OnMouseLeave(const CPoint& _point)
{
if (PtInRect(m_rcLTUP, _point))
{
//鼠标离开(左边/顶部)箭头
m_clrArrow = m_clrArrow_Def;
InvalidateRect(m_rcLTUP);
}
else if (PtInRect(m_rcRTDN, _point))
{
//鼠标离开(右边/底部)箭头
m_clrArrow = m_clrArrow_Def;
InvalidateRect(m_rcRTDN);
}
else if (PtInRect(m_rcSlider, _point))
{
//鼠标离开滑块
m_clrSlider = m_clrSlider_Def;
InvalidateRect(m_rcSlider);
}
}
void ScrollBar::OnMouseLeave()
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
//鼠标离开控件
m_bIsTrack = FALSE;
m_clrSlider = m_clrSlider_Def;
m_clrArrow = m_clrArrow_Def;
Invalidate();
if (m_bIsCaptrue)
{
ReleaseCapture();
m_bIsCaptrue = FALSE;
}
CBaseWnd::OnMouseLeave();
}
void ScrollBar::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if (PtInRect(m_rcLTUP, point))
{
//鼠标左键点击(左边/顶部)箭头
m_clrArrow = m_clrArrow_Click;
InvalidateRect(m_rcLTUP);
OnClickLTUPBtn();
}
else if (PtInRect(m_rcRTDN, point))
{
//鼠标左键点击(右边/底部)箭头
m_clrArrow = m_clrArrow_Click;
InvalidateRect(m_rcRTDN);
OnClickRTDNBtn();
}
else if (PtInRect(m_rcSlider, point))
{
//鼠标左键点击滑块
m_clrSlider = m_clrSlider_Click;
InvalidateRect(m_rcSlider);
SetCapture();
m_bIsCaptrue = TRUE;
}
CBaseWnd::OnLButtonDown(nFlags, point);
}
void ScrollBar::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if (PtInRect(m_rcLTUP, point))
{
//鼠标左键点击(左边/顶部)箭头 后 鼠标弹起
m_clrArrow = m_clrArrow_Hover;
InvalidateRect(m_rcLTUP);
}
else if (PtInRect(m_rcRTDN, point))
{
//鼠标左键点击(右边/底部)箭头 后 鼠标弹起
m_clrArrow = m_clrArrow_Hover;
InvalidateRect(m_rcRTDN);
}
else if (PtInRect(m_rcSlider, point))
{
//鼠标左键点击滑块 后 鼠标弹起
m_clrSlider = m_clrSlider_Hover;
InvalidateRect(m_rcSlider);
ReleaseCapture();
m_bIsCaptrue = FALSE;
if (m_infoScroll.nTrackPos != m_infoScroll.nPos)//用nPos和nTrackPos比较是否滑块滑动过,nTrackPos在拖滑块中会更新
{
switch (m_iBar)
{
case SB_HORZ:
SendMessage(WM_HSCROLL, MAKEWPARAM(SB_THUMBPOSITION, m_rcSlider.left), NULL);
break;
case SB_VERT:
SendMessage(WM_VSCROLL, MAKEWPARAM(SB_THUMBPOSITION, m_rcSlider.top), NULL);
break;
}
}
}
if (m_bIsCaptrue)
{
ReleaseCapture();
m_bIsCaptrue = FALSE;
if (m_infoScroll.nTrackPos != m_infoScroll.nPos)//用nPos和nTrackPos比较是否滑块滑动过,nTrackPos在拖滑块中会更新
{
switch (m_iBar)
{
case SB_HORZ:
SendMessage(WM_HSCROLL, MAKEWPARAM(SB_THUMBPOSITION, m_rcSlider.left), NULL);
break;
case SB_VERT:
SendMessage(WM_VSCROLL, MAKEWPARAM(SB_THUMBPOSITION, m_rcSlider.top), NULL);
break;
}
}
}
m_bIsEnd = FALSE;
CBaseWnd::OnLButtonUp(nFlags, point);
}
void ScrollBar::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if (pScrollBar == NULL)//NULL说明内部发送过来信息
{
switch (nSBCode)
{
case SB_ENDSCROLL:
{
//结束滚动
//_output_debug_("水平滚动结束x:%d", nPos);
if (m_pTargetWnd)
{
m_pTargetWnd->SendMessage(WM_HSCROLL, MAKEWPARAM(SB_ENDSCROLL, m_infoScroll.nPos), (LPARAM)this);
}
break;
}
case SB_THUMBTRACK:
{
//正在拖动滑块
m_infoScroll.nTrackPos = int((float)nPos / m_fRatio);//使用滑块的距离逆向比率得到目标的xPos
if (m_pTargetWnd)
{
m_pTargetWnd->SendMessage(WM_HSCROLL, MAKEWPARAM(SB_THUMBTRACK, m_infoScroll.nTrackPos), (LPARAM)this);
}
break;
}
case SB_THUMBPOSITION:
{
//用户已拖动滚动框(拇指)并松开鼠标按钮
//_output_debug_("水平滚动条鼠标松开滑动,目标xPos:%d", m_infoScroll.nTrackPos);
m_infoScroll.nPos = m_infoScroll.nTrackPos;//最终更新用户数据
SendMessage(WM_HSCROLL, MAKEWPARAM(SB_ENDSCROLL, m_rcSlider.left-m_iLTUPBtn), NULL);
break;
}
case SB_LINELEFT:
{
//向左滚动一个单位 点击左箭头
//_output_debug_("水平滚动向左滚动一个单位:%d", nPos);
m_fCorrectionLT += (float)nPos / m_fRatio;//计算目标控件的滚动距离
int pos = (int)m_fCorrectionLT;
m_fCorrectionLT -= (float)pos;
m_infoScroll.nPos -= pos;
m_infoScroll.nPos = m_infoScroll.nPos < m_infoScroll.nMin ? m_infoScroll.nMin : m_infoScroll.nPos;
m_infoScroll.nTrackPos = m_infoScroll.nPos;
if (m_pTargetWnd)
{
m_pTargetWnd->SendMessage(WM_HSCROLL, MAKEWPARAM(SB_THUMBPOSITION, m_infoScroll.nPos), (LPARAM)this);
}
break;
}
case SB_LINERIGHT:
{
//向右滚动一个单位 点击右箭头
//_output_debug_("水平滚动向右滚动一个单位:%d", nPos);
m_fCorrectionRD += (float)nPos / m_fRatio;
int pos = (int)m_fCorrectionRD;
m_fCorrectionRD -= (float)pos;//把小数位单独取出来留给后面的补正
m_infoScroll.nPos += pos;//使用滑块的距离逆向比率得到目标的xPos
m_infoScroll.nPos = m_infoScroll.nPos > m_infoScroll.nMax ? m_infoScroll.nMax : m_infoScroll.nPos;
m_infoScroll.nTrackPos = m_infoScroll.nPos;
if (m_pTargetWnd)
{
m_pTargetWnd->SendMessage(WM_HSCROLL, MAKEWPARAM(SB_THUMBPOSITION, m_infoScroll.nPos), (LPARAM)this);
}
break;
}
case SB_LEFT:
{
//滚动到左边末尾
//_output_debug_("已经滚动到左边尽头.");
//if (m_pTargetWnd) m_pTargetWnd->SendMessage(WM_HSCROLL, MAKEWPARAM(SB_LEFT, 0), (LPARAM)this);
break;
}
case SB_RIGHT:
{
//滚动到右边末尾
//_output_debug_("已经滚动到右边尽头.");
//if (m_pTargetWnd) m_pTargetWnd->SendMessage(WM_HSCROLL, MAKEWPARAM(SB_RIGHT, m_infoScroll.nMax), (LPARAM)this);
break;
}
}
}
CBaseWnd::OnHScroll(nSBCode, nPos, pScrollBar);
}
void ScrollBar::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if (pScrollBar == NULL)//NULL说明内部发送过来信息
{
switch (nSBCode)
{
case SB_ENDSCROLL:
{
//结束滚动
//_output_debug_("垂直滚动结束y:%d", nPos);
if (m_pTargetWnd)
{
m_pTargetWnd->SendMessage(WM_VSCROLL, MAKEWPARAM(SB_ENDSCROLL, m_infoScroll.nPos), (LPARAM)this);
}
break;
}
case SB_THUMBTRACK:
{
//正在拖动滑块
m_infoScroll.nTrackPos = int((float)nPos / m_fRatio);//使用滑块的距离逆向比率得到目标的xPos
if (m_pTargetWnd)
{
m_pTargetWnd->SendMessage(WM_VSCROLL, MAKEWPARAM(SB_THUMBTRACK, m_infoScroll.nTrackPos), (LPARAM)this);
}
break;
}
case SB_THUMBPOSITION:
{
//用户已拖动滚动框(拇指)并松开鼠标按钮
//_output_debug_("垂直滚动条鼠标松开滑动,目标yPos: %d", m_infoScroll.nTrackPos);
m_infoScroll.nPos = m_infoScroll.nTrackPos;//最终更新用户数据
SendMessage(WM_VSCROLL, MAKEWPARAM(SB_ENDSCROLL, m_rcSlider.top-m_iLTUPBtn), NULL);
break;
}
case SB_LINEUP:
{
//向上滚动了一个单位 点击顶箭头
m_fCorrectionLT += int((float)nPos / m_fRatio);//使用滑块的距离逆向比率得到目标的xPos
int pos = (int)m_fCorrectionLT;
m_fCorrectionLT -= (float)pos;
m_infoScroll.nPos -= pos;
m_infoScroll.nPos = m_infoScroll.nPos < m_infoScroll.nMin ? m_infoScroll.nMin : m_infoScroll.nPos;
m_infoScroll.nTrackPos = m_infoScroll.nPos;
if (m_pTargetWnd)
{
m_pTargetWnd->SendMessage(WM_VSCROLL, MAKEWPARAM(SB_THUMBPOSITION, m_infoScroll.nPos), (LPARAM)this);
}
break;
}
case SB_LINEDOWN:
{
//向下滚动了一个单位 点击底箭头
m_fCorrectionRD += (float)nPos / m_fRatio;
int pos = (int)m_fCorrectionRD;
m_fCorrectionRD -= (float)pos;//把小数位单独取出来留给后面的补正
m_infoScroll.nPos += pos;//使用滑块的距离逆向比率得到目标的xPos
m_infoScroll.nPos = m_infoScroll.nPos > m_infoScroll.nMax ? m_infoScroll.nMax : m_infoScroll.nPos;
m_infoScroll.nTrackPos = m_infoScroll.nPos;
if (m_pTargetWnd)
{
m_pTargetWnd->SendMessage(WM_VSCROLL, MAKEWPARAM(SB_THUMBPOSITION, m_infoScroll.nPos), (LPARAM)this);
}
break;
}
case SB_TOP:
{
//滚动到顶部
//_output_debug_("已经滚动到顶部尽头.");
//if (m_pTargetWnd) m_pTargetWnd->SendMessage(WM_VSCROLL, MAKEWPARAM(SB_TOP, 0), (LPARAM)this);
break;
}
case SB_BOTTOM:
{
//滚动到底部
//_output_debug_("已经滚动到底部尽头.");
//if (m_pTargetWnd) m_pTargetWnd->SendMessage(WM_VSCROLL, MAKEWPARAM(SB_BOTTOM, 0), (LPARAM)this);
break;
}
}
}
CBaseWnd::OnVScroll(nSBCode, nPos, pScrollBar);
}
void ScrollBar::OnDragSlider(const CPoint& _point)
{
CPoint point = {0, 0};
CRect rect = m_rcSlider;
switch (m_iBar)
{
case SB_HORZ:
{
point.x = _point.x - m_ptMouseLastPoint.x;
rect.OffsetRect(point);
if (rect.left >= m_rcScroll.left && rect.right <= m_rcScroll.right)
{
//合法拖动滑块
m_rcSlider.left = rect.left;//更新滑块左边坐标
m_rcSlider.right = rect.right;//更新滑块右边坐标
InvalidateRect(m_rcScroll);//刷新通道
SendMessage(WM_HSCROLL, MAKEWPARAM(SB_THUMBTRACK, m_rcSlider.left-m_iLTUPBtn), NULL);
m_bIsEnd = FALSE;
}
else
{
//拖动滑块不合法: 超出左边 或者 右边
if (!m_bIsEnd && rect.left < m_rcScroll.left)
{
//超出左边
m_bIsEnd = TRUE;
SendMessage(WM_HSCROLL, MAKEWPARAM(SB_LEFT, m_rcScroll.left), NULL);
}
else if(!m_bIsEnd && rect.right > m_rcScroll.right)
{
//超出右边
m_bIsEnd = TRUE;
SendMessage(WM_HSCROLL, MAKEWPARAM(SB_RIGHT, m_rcScroll.right), NULL);
}
}
break;
}
case SB_VERT:
{
point.y = _point.y - m_ptMouseLastPoint.y;
rect.OffsetRect(point);
if (rect.top >= m_rcScroll.top && rect.bottom <= m_rcScroll.bottom)
{
//合法拖动滑块
m_rcSlider.top = rect.top;//更新滑块顶部坐标
m_rcSlider.bottom = rect.bottom;//更新滑块底部坐标
InvalidateRect(m_rcScroll);//刷新通道
SendMessage(WM_VSCROLL, MAKEWPARAM(SB_THUMBTRACK, m_rcSlider.top-m_iLTUPBtn), NULL);
m_bIsEnd = FALSE;
}
else
{
//拖动滑块不合法: 超出顶部 或者 底部
if (!m_bIsEnd && rect.top < m_rcScroll.top)
{
//超出顶部
m_bIsEnd = TRUE;
SendMessage(WM_VSCROLL, MAKEWPARAM(SB_TOP, m_rcScroll.top), NULL);
}
else if(!m_bIsEnd && rect.bottom > m_rcScroll.bottom)
{
//超出底部
m_bIsEnd = TRUE;
SendMessage(WM_VSCROLL, MAKEWPARAM(SB_BOTTOM, m_rcScroll.bottom), NULL);
}
}
break;
}
}
}
void ScrollBar::OnClickLTUPBtn()//点击了(左箭头/顶部箭头)
{
int iSpace = 0;
int iPage = 0;
switch (m_iBar)
{
case SB_HORZ:
{
//_output_debug_("点击了左箭头");
iSpace = m_rcScroll.Width() - m_rcSlider.Width();//计算出滑块能滑动的最大距离
iPage = int((float)iSpace / ((float)iSpace / 10.0f));//点击一次箭头 滑块移动的距离
if (m_rcSlider.left - iPage >= m_rcScroll.left)
{
//合法滑动
}
else
{
//滑动可能超出左边
iPage = m_rcSlider.left-m_rcScroll.left;//重置到尽头
}
if (iPage)
{
m_rcSlider.OffsetRect(-iPage, 0);
InvalidateRect(m_rcScroll);
SendMessage(WM_HSCROLL, MAKEWPARAM(SB_LINELEFT, iPage), NULL);
}
break;
}
case SB_VERT:
{
//_output_debug_("点击了顶部箭头");
iSpace = m_rcScroll.Height() - m_rcSlider.Height();//计算出滑块能滑动的最大距离
iPage = int((float)iSpace / ((float)iSpace / 10.0f));//点击一次箭头 滑块移动的距离
if (m_rcSlider.top - iPage >= m_rcScroll.top)
{
//合法滑动
}
else
{
//滑动可能超出顶部
iPage = m_rcSlider.top - m_rcScroll.top;//重置到尽头
}
if (iPage)
{
m_rcSlider.OffsetRect(0, -iPage);
InvalidateRect(m_rcScroll);
SendMessage(WM_VSCROLL, MAKEWPARAM(SB_LINEUP, iPage), NULL);
}
break;
}
}
}
void ScrollBar::OnClickRTDNBtn()//点击了(右箭头/底部箭头)
{
int iSpace = 0;
int iPage = 0;
switch (m_iBar)
{
case SB_HORZ:
{
//_output_debug_("点击了右箭头");
iSpace = m_rcScroll.Width() - m_rcSlider.Width();//计算出滑块能滑动的最大距离
iPage = int((float)iSpace / ((float)iSpace / 10.0f));//点击一次箭头 滑块移动的距离
if (m_rcSlider.right + iPage <= m_rcScroll.right)
{
//合法滑动
}
else
{
//滑动可能超出右边
iPage = m_rcScroll.right - m_rcSlider.right;//重置到尽头
}
if (iPage)
{
m_rcSlider.OffsetRect(iPage, 0);
InvalidateRect(m_rcScroll);
SendMessage(WM_HSCROLL, MAKEWPARAM(SB_LINERIGHT, iPage), NULL);
}
break;
}
case SB_VERT:
{
//_output_debug_("点击了底部箭头");
iSpace = m_rcScroll.Height() - m_rcSlider.Height();//计算出滑块能滑动的最大距离
iPage = int((float)iSpace / ((float)iSpace / 10.0f));//点击一次箭头 滑块移动的距离
if (m_rcSlider.bottom + iPage <= m_rcScroll.bottom)
{
//合法滑动
}
else
{
//滑动可能超出底部
iPage = m_rcScroll.bottom - m_rcSlider.bottom;//重置到尽头
}
if (iPage)
{
m_rcSlider.OffsetRect(0, iPage);
InvalidateRect(m_rcScroll);
SendMessage(WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, iPage), NULL);
}
break;
}
}
}
void ScrollBar::SetTarget(CWnd* _pTargetWnd)
{
m_pTargetWnd = _pTargetWnd;
}
BOOL ScrollBar::SetScrollInfo(SCROLLINFO& _refScrollInfo, BOOL _bRedraw)
{
if (_refScrollInfo.nMax > (int)_refScrollInfo.nPage)
{
CRect rect = m_infoScrollBar.rcScrollBar;//取出滚动条的范围
float fRatio = 0.0f;//比率
int iSliderLen = 0;//滑块(长度/宽度)
int iPos = 0;
int iLTUP = 0;
int iRTDN = 0;
bool bIsValid = false;
if ((_refScrollInfo.fMask & (SIF_RANGE | SIF_PAGE | SIF_POS)) == false)//提供的fMask权限不足
{
return false;
}
switch (m_iBar)
{
case SB_HORZ:
{
fRatio = (float)(rect.Width() - (m_iLTUPBtn + m_iRTDNBtn)) / (float)_refScrollInfo.nMax;//计算比率: (总长度-2个箭头) / 目标控件内容最大可视范围
iSliderLen = (int)((float)rect.Width() * fRatio);//计算滑块的(长度) 水平滚动条
if (iSliderLen < m_iMinSlider)//如果滑块太短了
{
iSliderLen = m_iMinSlider;//使用最小滑块长度
fRatio = (float)(rect.Width() - (m_iLTUPBtn + m_iRTDNBtn + m_iMinSlider)) / (float)_refScrollInfo.nMax;//重新计算比率: (总长度-2个箭头-最小滑块长度) / 目标控件内容最大可视范围
}
iPos = (int)((float)_refScrollInfo.nPos * fRatio);//根据提供的nPos算出应该滚动的距离
iLTUP = iPos + m_iLTUPBtn;//算出滑块左边的坐标值
iRTDN = iLTUP + iSliderLen;//算出滑块右边的坐标值
if (iLTUP >= m_rcScroll.left && iRTDN <= m_rcScroll.right)//如果滑块左右两边都位于滚动条通道内,提供的nPos合法
{
m_rcSlider.left = iLTUP;
m_rcSlider.right = iRTDN;
if (_bRedraw) InvalidateRect(m_rcScroll);//更新通道内容
bIsValid = true;//操作合法
m_fRatio = fRatio;//更新比率
}
else
{
//提供的nPos不合法
return FALSE;
}
break;
}
case SB_VERT:
{
fRatio = (float)(rect.Height() - (m_iLTUPBtn + m_iRTDNBtn)) / (float)_refScrollInfo.nMax;//计算比率: (总宽度-2个箭头) / 目标控件内容最大可视范围
iSliderLen = (int)((float)rect.Height() * fRatio);//计算滑块的(宽度) 垂直滚动条
if (iSliderLen < m_iMinSlider)//如果滑块太短了
{
iSliderLen = m_iMinSlider;//使用最小滑块宽度
fRatio = (float)(rect.Height() - (m_iLTUPBtn + m_iRTDNBtn + m_iMinSlider)) / (float)_refScrollInfo.nMax;//重新计算比率: (总宽度-2个箭头-最小滑块宽度) / 目标控件内容最大可视范围
}
iPos = (int)((float)_refScrollInfo.nPos * fRatio);//根据提供的nPos算出应该滚动的距离
iLTUP = iPos + m_iLTUPBtn;//算出滑块顶部的坐标值
iRTDN = iLTUP + iSliderLen;//算出滑块底部的坐标值
if (iLTUP >= m_rcScroll.top && iRTDN <= m_rcScroll.bottom)//如果滑块上下两边都位于滚动条通道内,提供的nPos合法
{
m_rcSlider.top = iLTUP;
m_rcSlider.bottom = iRTDN;
if (_bRedraw) InvalidateRect(m_rcScroll);//更新通道内容
bIsValid = true;//操作合法
m_fRatio = fRatio;//更新比率
}
else
{
//提供的nPos不合法
return FALSE;
}
break;
}
}
//更新scroll info 数据
if (bIsValid && _refScrollInfo.fMask & SIF_ALL)
{
m_infoScroll = _refScrollInfo;//全部数据复制给内部info
m_infoScroll.cbSize = sizeof SCROLLINFO;//系统性修正数据-避免提供的数据有问题
m_infoScroll.fMask = SIF_ALL;//系统性修正数据-避免提供的数据有问题
return TRUE;
}
else if (bIsValid)
{
if (_refScrollInfo.fMask & SIF_RANGE)
{
m_infoScroll.nMin = _refScrollInfo.nMin;
m_infoScroll.nMax = _refScrollInfo.nMax;
}
if (_refScrollInfo.fMask & SIF_PAGE)
{
m_infoScroll.nPage = _refScrollInfo.nPage;
}
if (_refScrollInfo.fMask & SIF_POS)
{
m_infoScroll.nPos = _refScrollInfo.nPos;
}
//SIF_DISABLENOSCROLL //不处理
//SIF_TRACKPOS //系统内部处理
return TRUE;
}
}
return FALSE;
}
BOOL ScrollBar::SetScrollPos(int _iPos, BOOL _bRedraw)
{
int iPos = (int)roundf((float)_iPos * m_fRatio);//根据提供的nPos算出应该滚动的距离
CRect rect;
if (_iPos > -1)
{
switch (m_iBar)
{
case SB_HORZ:
{
iPos += m_iLTUPBtn;
rect = m_rcSlider;
rect.OffsetRect(iPos, 0);
if (rect.right <= m_rcScroll.right)
{
m_rcSlider = rect;
if(_bRedraw)InvalidateRect(m_rcScroll);
m_infoScroll.nPos = _iPos;
m_infoScroll.nTrackPos = _iPos;
return TRUE;
}
else
{
m_rcSlider.right = m_rcScroll.right;
m_rcSlider.left = m_rcSlider.right - rect.Width();
if (_bRedraw)InvalidateRect(m_rcScroll);
m_infoScroll.nPos = _iPos;
m_infoScroll.nTrackPos = _iPos;
}
break;
}
case SB_VERT:
{
iPos += m_iLTUPBtn;
rect = m_rcSlider;
rect.OffsetRect(0, iPos);
if (rect.bottom <= m_rcScroll.bottom)
{
m_rcSlider = rect;
if (_bRedraw)InvalidateRect(m_rcScroll);
m_infoScroll.nPos = _iPos;
m_infoScroll.nTrackPos = _iPos;
return TRUE;
}
else
{
m_rcSlider.bottom = m_rcScroll.bottom;
m_rcSlider.top = m_rcScroll.bottom - rect.Height();
if (_bRedraw)InvalidateRect(m_rcScroll);
m_infoScroll.nPos = _iPos;
m_infoScroll.nTrackPos = _iPos;
}
break;
}
}
}
return FALSE;
}