这个问题说起来简单,做起来难。
虽然说是WTL,但我想MFC应该没有太大差别。
关键点:ScrollWindow,MoveWindow。
首先看ScrollWnd。
class ScrollWnd : public CWindowImpl //MFC中直接继承CWnd,WTL的精髓就是模板
{
private:
const int LINE; //定义移动的距离
public:
BEGIN_MSG_MAP(ScrollWnd)
MSG_WM_VSCROLL(OnVScroll)
MSG_WM_HSCROLL(OnHScroll)
END_MSG_MAP()
public:
ScrollWnd() : LINE(12) { }
void OnVScroll(UINT nSBCode,UINT,CScrollBar)
{
SCROLLINFO sif = { sizeof(SCROLLINFO),SIF_ALL };
GetScrollInfo(SB_VERT,&sif);
int oldPos = sif.nPos;
switch(nSBCode)
{
case SB_TOP: sif.nPos = sif.nMin; break;
case SB_BOTTOM: sif.nPos = sif.nMax; break;
case SB_LINEUP: sif.nPos -= LINE; break;
case SB_LINEDOWN: sif.nPos += LINE; break;
case SB_PAGEUP: sif.nPos -= sif.nPage; break;
case SB_PAGEDOWN: sif.nPos += sif.nPage; break;
case SB_THUMBTRACK: sif.nPos = sif.nTrackPos; break;
default: break;
}
if(sif.nPos > sif.nMax)
sif.nPos = sif.nMax;
else if(sif.nPos < sif.nMin)
sif.nPos = sif.nMin;
sif.fMask = SIF_POS;
SetScrollInfo(SB_VERT,&sif);
GetScrollInfo(SB_VERT,&sif);
if(sif.nPos != oldPos)
ScrollWindow(0,oldPos - sif.nPos); //这里是重点
}
void OnHScroll(UINT nSBCode,UINT,CScrollBar)
{
SCROLLINFO sif = { sizeof(SCROLLINFO),SIF_ALL };
GetScrollInfo(SB_HORZ,&sif);
int oldPos = sif.nPos;
switch(nSBCode)
{
case SB_LEFT: sif.nPos = sif.nMin; break;
case SB_RIGHT: sif.nPos = sif.nMax; break;
case SB_LINELEFT: sif.nPos -= LINE; break;
case SB_LINERIGHT: sif.nPos += LINE; break;
case SB_PAGELEFT: sif.nPos -= sif.nPage; break;
case SB_PAGERIGHT: sif.nPos += sif.nPage; break;
case SB_THUMBTRACK: sif.nPos = sif.nTrackPos; break;
default: break;
}
if(sif.nPos > sif.nMax)
sif.nPos = sif.nMax;
else if(sif.nPos < sif.nMin)
sif.nPos = sif.nMin;
sif.fMask = SIF_POS;
SetScrollInfo(SB_HORZ,&sif);
GetScrollInfo(SB_HORZ,&sif);
if(sif.nPos != oldPos)
ScrollWindow(oldPos - sif.nPos,0);
}
void ResetThumbHeight(HWND wndChild)
{
SCROLLINFO sif = { sizeof(SCROLLINFO),SIF_ALL }, sif2 = sif;
GetScrollInfo(SB_VERT,&sif); GetScrollInfo(SB_HORZ,&sif2);
CRect rc_client,rc_child;
::GetWindowRect(wndChild,&rc_child);
GetClientRect(&rc_client);
if(rc_client.Height() >= rc_child.Height()) //不需要垂直滚动条的情况
{
ShowScrollBar(SB_VERT,FALSE);
::SetWindowPos(wndChild,NULL,0,0,rc_client.Width(),rc_child.Height(),SWP_NOMOVE);
return;
}
if(rc_client.Width() >= rc_child.Width()) //不需要水平滚动条的情况
{
ShowScrollBar(SB_HORZ,FALSE);
::SetWindowPos(wndChild,NULL,0,0,rc_client.Width(),rc_child.Height(),SWP_NOMOVE);
return;
}
sif.nPage = rc_client.Height(); sif2.nPage = rc_client.Width();
sif.nMin = 0; sif2.nMin = 0;
sif.nMax = rc_child.Height(); sif2.nMax = rc_child.Width();
sif.nPos = 0; sif2.nPos = 0;
SetScrollInfo(SB_VERT,&sif); SetScrollInfo(SB_HORZ,&sif2);
}
};
不知道为什么,添加MOUSEWHEEL消息却一直不响应,所以此处就略去了。
下面是ImageCtrl:
采用gdi+绘制图片。记得ERASEBKGND返回TRUE
class ImageCtrl : public CWindowImpl
{
private:
CString m_strImagePath;
public:
BEGIN_MSG_MAP(ImageCtrl)
MSG_WM_PAINT(OnPaint)
END_MSG_MAP()
public:
ImageCtrl()
{
m_strImagePath = L"";
}
void DrawImage(CString str_image_path)
{
m_strImagePath = str_image_path;
RedrawWindow();
}
void OnPaint(CDCHandle)
{
CPaintDC dc(m_hWnd);
if(m_strImagePath.IsEmpty())
return;
CRect rc; GetClientRect(&rc);
Graphics g(dc);
Image img(m_strImagePath)
g.DrawImage(&img,0,0,rc.Width(),rc.Height());
}
};
使用方法:
ScrollWnd scrWnd;
scrWnd.Create(m_hWnd,CRect(26,72,494,319),NULL,WS_CHILD|WS_VISIBLE|WS_VSCROLL|WS_HSCROLL);
Image img(L"res/3qqq.png");
ImageCtrl icc;
icc.Create(scrWnd.m_hWnd,CRect(0,0,img.GetWidth(),img.GetHeight()),NULL,WS_CHILD|WS_VISIBLE);
icc.DrawImage(L"res/3qqq.png"););
scrWnd.ResetThumbHeight(icc.m_hWnd);
总结:其实,可以这么来思考。我们有一块布ImageCtrl,有很多幅画,任意选择一幅,然后调整布的大小和画一样,把画放到布上去。ScrollWnd就是一个相框,我们把布塞到相框里面,通过上下左右设定布显示区域。其实布的移动是最困扰我们的,我最初以为要自己移动,结果发现ScrollWnd的ScrollWindow已经帮我们做好了,因为ImageCtrl是他的子窗口。