创建可滚动对话框
Posted
技术标签:
【中文标题】创建可滚动对话框【英文标题】:Creating a scrollable dialog 【发布时间】:2021-06-20 07:52:01 【问题描述】:我想知道在 Windows API 中是否有公认的策略,可能使用 WTL 可滚动类(CScrollImpl
、CScrollWindowImpl
、CScrollContainer
等)。
我的一个想法是在外部父窗口中放置一个内部子对话框(其中包含实际控件),然后简单地移动子对话框窗口以响应滚动条消息。这样,至少在我看来,当滚动发生时,您不必移动每个单独的控件。您只需移动内部窗口,这将反过来移动控件。
但也许我在想这一切都错了。以前有人处理过这个问题吗?感谢您的帮助。
【问题讨论】:
这基本上就是 MFC 的 CFormView 所做的。不过,你的问题是什么? 谢谢,虽然我不在 MFC 项目中工作。我想我的问题是的典型方法是什么。一起移动所有控件?使用子对话方式? 系统不提供实现,因此您将不得不使用自定义解决方案。无论是否是典型的方法,遵循 MFC 肯定会确保您在数十年的过程中经过成百上千的应用程序的尝试和测试。 相信我,我不想重新发明***。我只是希望避免对 MFC 的依赖而坚持使用 ATL/WTL。 我并不是建议您使用 MFC。您是在询问特定的实施策略以及它是否合理。这个问题的答案是,是的,这是一种可能的方法,并且一个通用库成功地使用了它。如何将其转换为您选择的特定窗口库是您必须解决的问题。 【参考方案1】:这是修改后的版本,可以在对话框中使用:
template <class T>
class CScroller
public:
enum uSCROLL_FLAGS = SW_INVALIDATE ;
POINT m_ptOffset;
SIZE m_sizeAll;
SIZE m_sizeLine;
SIZE m_sizePage;
SIZE m_sizeClient;
int m_zDelta; // current wheel value
int m_nWheelLines; // number of lines to scroll on wheel
int m_zHDelta; // current horizontal wheel value
int m_nHWheelChars; // number of chars to scroll on horizontal wheel
UINT m_uScrollFlags;
DWORD m_dwExtendedStyle; // scroll specific extended styles
// Constructor
CScroller() : m_zDelta(0), m_nWheelLines(3),
m_zHDelta(0), m_nHWheelChars(3),
m_uScrollFlags(0U), m_dwExtendedStyle(0)
m_ptOffset.x = 0;
m_ptOffset.y = 0;
m_sizeAll.cx = 0;
m_sizeAll.cy = 0;
m_sizePage.cx = 0;
m_sizePage.cy = 0;
m_sizeLine.cx = 0;
m_sizeLine.cy = 0;
m_sizeClient.cx = 0;
m_sizeClient.cy = 0;
SetScrollExtendedStyle(SCRL_SCROLLCHILDREN | SCRL_ERASEBACKGROUND);
// Attributes & Operations
DWORD GetScrollExtendedStyle() const
return m_dwExtendedStyle;
DWORD SetScrollExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
DWORD dwPrevStyle = m_dwExtendedStyle;
if (dwMask == 0)
m_dwExtendedStyle = dwExtendedStyle;
else
m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
// cache scroll flags
T* pT = static_cast<T*>(this);
(void)pT; // avoid level 4 warning
m_uScrollFlags = pT->uSCROLL_FLAGS | (IsScrollingChildren() ? SW_SCROLLCHILDREN : 0) | (IsErasingBackground() ? SW_ERASE : 0);
m_uScrollFlags |= (IsSmoothScroll() ? SW_SMOOTHSCROLL : 0);
return dwPrevStyle;
// offset operations
void SetScrollOffset(int x, int y, BOOL bRedraw = TRUE)
T* pT = static_cast<T*>(this);
ATLASSERT(::IsWindow(pT->m_hWnd));
pT->AdjustScrollOffset(x, y);
int dx = m_ptOffset.x - x;
int dy = m_ptOffset.y - y;
m_ptOffset.x = x;
m_ptOffset.y = y;
// block: set horizontal scroll bar
SCROLLINFO si = sizeof(SCROLLINFO) ;
si.fMask = SIF_POS;
if ((m_dwExtendedStyle & SCRL_DISABLENOSCROLLH) != 0)
si.fMask |= SIF_DISABLENOSCROLL;
si.nPos = m_ptOffset.x;
pT->SetScrollInfo(SB_HORZ, &si, bRedraw);
// block: set vertical scroll bar
SCROLLINFO si = sizeof(SCROLLINFO) ;
si.fMask = SIF_POS;
if ((m_dwExtendedStyle & SCRL_DISABLENOSCROLLV) != 0)
si.fMask |= SIF_DISABLENOSCROLL;
si.nPos = m_ptOffset.y;
pT->SetScrollInfo(SB_VERT, &si, bRedraw);
// Move all children if needed
if (IsScrollingChildren() && ((dx != 0) || (dy != 0)))
for (HWND hWndChild = ::GetWindow(pT->m_hWnd, GW_CHILD); hWndChild != NULL; hWndChild = ::GetWindow(hWndChild, GW_HWNDNEXT))
RECT rect = ;
::GetWindowRect(hWndChild, &rect);
::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rect, 1);
::SetWindowPos(hWndChild, NULL, rect.left + dx, rect.top + dy, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
if (bRedraw)
pT->Invalidate();
void SetScrollOffset(POINT ptOffset, BOOL bRedraw = TRUE)
SetScrollOffset(ptOffset.x, ptOffset.y, bRedraw);
void GetScrollOffset(POINT& ptOffset) const
ptOffset = m_ptOffset;
// size operations
void SetScrollSize(int cx, int cy, BOOL bRedraw = TRUE, bool bResetOffset = true)
T* pT = static_cast<T*>(this);
ATLASSERT(::IsWindow(pT->m_hWnd));
m_sizeAll.cx = cx;
m_sizeAll.cy = cy;
int x = 0;
int y = 0;
if (!bResetOffset)
x = m_ptOffset.x;
y = m_ptOffset.y;
pT->AdjustScrollOffset(x, y);
int dx = m_ptOffset.x - x;
int dy = m_ptOffset.y - y;
m_ptOffset.x = x;
m_ptOffset.y = y;
// block: set horizontal scroll bar
SCROLLINFO si = sizeof(SCROLLINFO) ;
si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
if ((m_dwExtendedStyle & SCRL_DISABLENOSCROLLH) != 0)
si.fMask |= SIF_DISABLENOSCROLL;
si.nMin = 0;
si.nMax = m_sizeAll.cx - 1;
si.nPage = m_sizeClient.cx;
si.nPos = m_ptOffset.x;
pT->SetScrollInfo(SB_HORZ, &si, bRedraw);
// block: set vertical scroll bar
SCROLLINFO si = sizeof(SCROLLINFO) ;
si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
if ((m_dwExtendedStyle & SCRL_DISABLENOSCROLLV) != 0)
si.fMask |= SIF_DISABLENOSCROLL;
si.nMin = 0;
si.nMax = m_sizeAll.cy - 1;
si.nPage = m_sizeClient.cy;
si.nPos = m_ptOffset.y;
pT->SetScrollInfo(SB_VERT, &si, bRedraw);
// Move all children if needed
if (IsScrollingChildren() && ((dx != 0) || (dy != 0)))
for (HWND hWndChild = ::GetWindow(pT->m_hWnd, GW_CHILD); hWndChild != NULL; hWndChild = ::GetWindow(hWndChild, GW_HWNDNEXT))
RECT rect = ;
::GetWindowRect(hWndChild, &rect);
::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rect, 1);
::SetWindowPos(hWndChild, NULL, rect.left + dx, rect.top + dy, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
SetScrollLine(0, 0);
SetScrollPage(0, 0);
if (bRedraw)
pT->Invalidate();
void SetScrollSize(SIZE size, BOOL bRedraw = TRUE, bool bResetOffset = true)
SetScrollSize(size.cx, size.cy, bRedraw, bResetOffset);
void GetScrollSize(SIZE& sizeWnd) const
sizeWnd = m_sizeAll;
// line operations
void SetScrollLine(int cxLine, int cyLine)
ATLASSERT((cxLine >= 0) && (cyLine >= 0));
ATLASSERT((m_sizeAll.cx != 0) && (m_sizeAll.cy != 0));
m_sizeLine.cx = T::CalcLineOrPage(cxLine, m_sizeAll.cx, 100);
m_sizeLine.cy = T::CalcLineOrPage(cyLine, m_sizeAll.cy, 100);
void SetScrollLine(SIZE sizeLine)
SetScrollLine(sizeLine.cx, sizeLine.cy);
void GetScrollLine(SIZE& sizeLine) const
sizeLine = m_sizeLine;
// page operations
void SetScrollPage(int cxPage, int cyPage)
ATLASSERT((cxPage >= 0) && (cyPage >= 0));
ATLASSERT((m_sizeAll.cx != 0) && (m_sizeAll.cy != 0));
m_sizePage.cx = T::CalcLineOrPage(cxPage, m_sizeAll.cx, 10);
m_sizePage.cy = T::CalcLineOrPage(cyPage, m_sizeAll.cy, 10);
void SetScrollPage(SIZE sizePage)
SetScrollPage(sizePage.cx, sizePage.cy);
void GetScrollPage(SIZE& sizePage) const
sizePage = m_sizePage;
// commands
void ScrollLineDown()
T* pT = static_cast<T*>(this);
ATLASSERT(::IsWindow(pT->m_hWnd));
pT->DoScroll(SB_VERT, SB_LINEDOWN, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
void ScrollLineUp()
T* pT = static_cast<T*>(this);
ATLASSERT(::IsWindow(pT->m_hWnd));
pT->DoScroll(SB_VERT, SB_LINEUP, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
void ScrollPageDown()
T* pT = static_cast<T*>(this);
ATLASSERT(::IsWindow(pT->m_hWnd));
pT->DoScroll(SB_VERT, SB_PAGEDOWN, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
void ScrollPageUp()
T* pT = static_cast<T*>(this);
ATLASSERT(::IsWindow(pT->m_hWnd));
pT->DoScroll(SB_VERT, SB_PAGEUP, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
void ScrollTop()
T* pT = static_cast<T*>(this);
ATLASSERT(::IsWindow(pT->m_hWnd));
pT->DoScroll(SB_VERT, SB_TOP, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
void ScrollBottom()
T* pT = static_cast<T*>(this);
ATLASSERT(::IsWindow(pT->m_hWnd));
pT->DoScroll(SB_VERT, SB_BOTTOM, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
void ScrollLineRight()
T* pT = static_cast<T*>(this);
ATLASSERT(::IsWindow(pT->m_hWnd));
pT->DoScroll(SB_HORZ, SB_LINEDOWN, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
void ScrollLineLeft()
T* pT = static_cast<T*>(this);
ATLASSERT(::IsWindow(pT->m_hWnd));
pT->DoScroll(SB_HORZ, SB_LINEUP, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
void ScrollPageRight()
T* pT = static_cast<T*>(this);
ATLASSERT(::IsWindow(pT->m_hWnd));
pT->DoScroll(SB_HORZ, SB_PAGEDOWN, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
void ScrollPageLeft()
T* pT = static_cast<T*>(this);
ATLASSERT(::IsWindow(pT->m_hWnd));
pT->DoScroll(SB_HORZ, SB_PAGEUP, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
void ScrollAllLeft()
T* pT = static_cast<T*>(this);
ATLASSERT(::IsWindow(pT->m_hWnd));
pT->DoScroll(SB_HORZ, SB_TOP, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
void ScrollAllRight()
T* pT = static_cast<T*>(this);
ATLASSERT(::IsWindow(pT->m_hWnd));
pT->DoScroll(SB_HORZ, SB_BOTTOM, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
// scroll to make point/view/window visible
void ScrollToView(POINT pt)
T* pT = static_cast<T*>(this);
ATLASSERT(::IsWindow(pT->m_hWnd));
RECT rect = pt.x, pt.y, pt.x, pt.y ;
pT->ScrollToView(rect);
void ScrollToView(RECT& rect)
T* pT = static_cast<T*>(this);
ATLASSERT(::IsWindow(pT->m_hWnd));
RECT rcClient = ;
pT->GetClientRect(&rcClient);
int x = m_ptOffset.x;
if (rect.left < m_ptOffset.x)
x = rect.left;
else if (rect.right > (m_ptOffset.x + rcClient.right))
x = rect.right - rcClient.right;
int y = m_ptOffset.y;
if (rect.top < m_ptOffset.y)
y = rect.top;
else if (rect.bottom > (m_ptOffset.y + rcClient.bottom))
y = rect.bottom - rcClient.bottom;
SetScrollOffset(x, y);
void ScrollToView(HWND hWnd)
T* pT = static_cast<T*>(this);
ATLASSERT(::IsWindow(pT->m_hWnd));
RECT rect = ;
::GetWindowRect(hWnd, &rect);
::OffsetRect(&rect, m_ptOffset.x, m_ptOffset.y);
::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rect, 2);
ScrollToView(rect);
BEGIN_MSG_MAP(CScroller)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_VSCROLL, OnVScroll)
MESSAGE_HANDLER(WM_HSCROLL, OnHScroll)
MESSAGE_HANDLER(WM_MOUSEWHEEL, OnMouseWheel)
MESSAGE_HANDLER(WM_MOUSEHWHEEL, OnMouseHWheel)
MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange)
MESSAGE_HANDLER(WM_SIZE, OnSize)
// standard scroll commands
ALT_MSG_MAP(1)
COMMAND_ID_HANDLER(ID_SCROLL_UP, OnScrollUp)
COMMAND_ID_HANDLER(ID_SCROLL_DOWN, OnScrollDown)
COMMAND_ID_HANDLER(ID_SCROLL_PAGE_UP, OnScrollPageUp)
COMMAND_ID_HANDLER(ID_SCROLL_PAGE_DOWN, OnScrollPageDown)
COMMAND_ID_HANDLER(ID_SCROLL_TOP, OnScrollTop)
COMMAND_ID_HANDLER(ID_SCROLL_BOTTOM, OnScrollBottom)
COMMAND_ID_HANDLER(ID_SCROLL_LEFT, OnScrollLeft)
COMMAND_ID_HANDLER(ID_SCROLL_RIGHT, OnScrollRight)
COMMAND_ID_HANDLER(ID_SCROLL_PAGE_LEFT, OnScrollPageLeft)
COMMAND_ID_HANDLER(ID_SCROLL_PAGE_RIGHT, OnScrollPageRight)
COMMAND_ID_HANDLER(ID_SCROLL_ALL_LEFT, OnScrollAllLeft)
COMMAND_ID_HANDLER(ID_SCROLL_ALL_RIGHT, OnScrollAllRight)
END_MSG_MAP()
LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
T* pT = static_cast<T*>(this);
pT->GetSystemSettings();
bHandled = FALSE;
return 1;
LRESULT OnVScroll(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
T* pT = static_cast<T*>(this);
ATLASSERT(::IsWindow(pT->m_hWnd));
pT->DoScroll(SB_VERT, (int)(short)LOWORD(wParam), (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
return 0;
LRESULT OnHScroll(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
T* pT = static_cast<T*>(this);
ATLASSERT(::IsWindow(pT->m_hWnd));
pT->DoScroll(SB_HORZ, (int)(short)LOWORD(wParam), (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
return 0;
LRESULT OnMouseWheel(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
T* pT = static_cast<T*>(this);
ATLASSERT(::IsWindow(pT->m_hWnd));
int zDelta = (int)GET_WHEEL_DELTA_WPARAM(wParam);
int nScrollCode = (m_nWheelLines == WHEEL_PAGESCROLL) ? ((zDelta > 0) ? SB_PAGEUP : SB_PAGEDOWN) : ((zDelta > 0) ? SB_LINEUP : SB_LINEDOWN);
m_zDelta += zDelta; // cumulative
int zTotal = (m_nWheelLines == WHEEL_PAGESCROLL) ? abs(m_zDelta) : abs(m_zDelta) * m_nWheelLines;
if (m_sizeAll.cy > m_sizeClient.cy)
for (int i = 0; i < zTotal; i += WHEEL_DELTA)
pT->DoScroll(SB_VERT, nScrollCode, (int&)m_ptOffset.y, m_sizeAll.cy, m_sizePage.cy, m_sizeLine.cy);
pT->UpdateWindow();
else if (m_sizeAll.cx > m_sizeClient.cx) // can't scroll vertically, scroll horizontally
for (int i = 0; i < zTotal; i += WHEEL_DELTA)
pT->DoScroll(SB_HORZ, nScrollCode, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
pT->UpdateWindow();
m_zDelta %= WHEEL_DELTA;
return 0;
LRESULT OnMouseHWheel(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
T* pT = static_cast<T*>(this);
ATLASSERT(::IsWindow(pT->m_hWnd));
int zDelta = (int)GET_WHEEL_DELTA_WPARAM(wParam);
int nScrollCode = (m_nHWheelChars == WHEEL_PAGESCROLL) ? ((zDelta > 0) ? SB_PAGERIGHT : SB_PAGELEFT) : ((zDelta > 0) ? SB_LINERIGHT : SB_LINELEFT);
m_zHDelta += zDelta; // cumulative
int zTotal = (m_nHWheelChars == WHEEL_PAGESCROLL) ? abs(m_zHDelta) : abs(m_zHDelta) * m_nHWheelChars;
if (m_sizeAll.cx > m_sizeClient.cx)
for (int i = 0; i < zTotal; i += WHEEL_DELTA)
pT->DoScroll(SB_HORZ, nScrollCode, (int&)m_ptOffset.x, m_sizeAll.cx, m_sizePage.cx, m_sizeLine.cx);
pT->UpdateWindow();
m_zHDelta %= WHEEL_DELTA;
return 0;
LRESULT OnSettingChange(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
GetSystemSettings();
return 0;
LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
T* pT = static_cast<T*>(this);
ATLASSERT(::IsWindow(pT->m_hWnd));
pT->DoSize(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
bHandled = FALSE;
return 1;
// scrolling handlers
LRESULT OnScrollUp(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
ScrollLineUp();
return 0;
LRESULT OnScrollDown(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
ScrollLineDown();
return 0;
LRESULT OnScrollPageUp(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
ScrollPageUp();
return 0;
LRESULT OnScrollPageDown(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
ScrollPageDown();
return 0;
LRESULT OnScrollTop(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
ScrollTop();
return 0;
LRESULT OnScrollBottom(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
ScrollBottom();
return 0;
LRESULT OnScrollLeft(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
ScrollLineLeft();
return 0;
LRESULT OnScrollRight(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
ScrollLineRight();
return 0;
LRESULT OnScrollPageLeft(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
ScrollPageLeft();
return 0;
LRESULT OnScrollPageRight(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
ScrollPageRight();
return 0;
LRESULT OnScrollAllLeft(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
ScrollAllLeft();
return 0;
LRESULT OnScrollAllRight(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
ScrollAllRight();
return 0;
// Overrideables
void DoPaint(CDCHandle /*dc*/)
// must be implemented in a derived class
ATLASSERT(FALSE);
// Implementation
void DoSize(int cx, int cy)
m_sizeClient.cx = cx;
m_sizeClient.cy = cy;
T* pT = static_cast<T*>(this);
// block: set horizontal scroll bar
SCROLLINFO si = sizeof(SCROLLINFO) ;
si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
si.nMin = 0;
si.nMax = m_sizeAll.cx - 1;
if ((m_dwExtendedStyle & SCRL_DISABLENOSCROLLH) != 0)
si.fMask |= SIF_DISABLENOSCROLL;
si.nPage = m_sizeClient.cx;
si.nPos = m_ptOffset.x;
pT->SetScrollInfo(SB_HORZ, &si, TRUE);
// block: set vertical scroll bar
SCROLLINFO si = sizeof(SCROLLINFO) ;
si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
si.nMin = 0;
si.nMax = m_sizeAll.cy - 1;
if ((m_dwExtendedStyle & SCRL_DISABLENOSCROLLV) != 0)
si.fMask |= SIF_DISABLENOSCROLL;
si.nPage = m_sizeClient.cy;
si.nPos = m_ptOffset.y;
pT->SetScrollInfo(SB_VERT, &si, TRUE);
int x = m_ptOffset.x;
int y = m_ptOffset.y;
if (pT->AdjustScrollOffset(x, y))
// Children will be moved in SetScrollOffset, if needed
pT->ScrollWindowEx(m_ptOffset.x - x, m_ptOffset.y - y, (m_uScrollFlags & ~SCRL_SCROLLCHILDREN));
SetScrollOffset(x, y, FALSE);
void DoScroll(int nType, int nScrollCode, int& cxyOffset, int cxySizeAll, int cxySizePage, int cxySizeLine)
T* pT = static_cast<T*>(this);
RECT rect = ;
pT->GetClientRect(&rect);
int cxyClient = (nType == SB_VERT) ? rect.bottom : rect.right;
int cxyMax = cxySizeAll - cxyClient;
if (cxyMax < 0) // can't scroll, client area is bigger
return;
bool bUpdate = true;
int cxyScroll = 0;
switch (nScrollCode)
case SB_TOP: // top or all left
cxyScroll = cxyOffset;
cxyOffset = 0;
break;
case SB_BOTTOM: // bottom or all right
cxyScroll = cxyOffset - cxyMax;
cxyOffset = cxyMax;
break;
case SB_LINEUP: // line up or line left
if (cxyOffset >= cxySizeLine)
cxyScroll = cxySizeLine;
cxyOffset -= cxySizeLine;
else
cxyScroll = cxyOffset;
cxyOffset = 0;
break;
case SB_LINEDOWN: // line down or line right
if (cxyOffset < cxyMax - cxySizeLine)
cxyScroll = -cxySizeLine;
cxyOffset += cxySizeLine;
else
cxyScroll = cxyOffset - cxyMax;
cxyOffset = cxyMax;
break;
case SB_PAGEUP: // page up or page left
if (cxyOffset >= cxySizePage)
cxyScroll = cxySizePage;
cxyOffset -= cxySizePage;
else
cxyScroll = cxyOffset;
cxyOffset = 0;
break;
case SB_PAGEDOWN: // page down or page right
if (cxyOffset < cxyMax - cxySizePage)
cxyScroll = -cxySizePage;
cxyOffset += cxySizePage;
else
cxyScroll = cxyOffset - cxyMax;
cxyOffset = cxyMax;
break;
case SB_THUMBTRACK:
if (IsNoThumbTracking())
break;
// else fall through
case SB_THUMBPOSITION:
SCROLLINFO si = sizeof(SCROLLINFO), SIF_TRACKPOS ;
if (pT->GetScrollInfo(nType, &si))
cxyScroll = cxyOffset - si.nTrackPos;
cxyOffset = si.nTrackPos;
break;
case SB_ENDSCROLL:
default:
bUpdate = false;
break;
if (bUpdate && (cxyScroll != 0))
pT->SetScrollPos(nType, cxyOffset, TRUE);
if (nType == SB_VERT)
pT->ScrollWindowEx(0, cxyScroll, m_uScrollFlags);
else
pT->ScrollWindowEx(cxyScroll, 0, m_uScrollFlags);
static int CalcLineOrPage(int nVal, int nMax, int nDiv)
if (nVal == 0)
nVal = nMax / nDiv;
if (nVal < 1)
nVal = 1;
else if (nVal > nMax)
nVal = nMax;
return nVal;
bool AdjustScrollOffset(int& x, int& y)
int xOld = x;
int yOld = y;
int cxMax = m_sizeAll.cx - m_sizeClient.cx;
if (x > cxMax)
x = (cxMax >= 0) ? cxMax : 0;
else if (x < 0)
x = 0;
int cyMax = m_sizeAll.cy - m_sizeClient.cy;
if (y > cyMax)
y = (cyMax >= 0) ? cyMax : 0;
else if (y < 0)
y = 0;
return ((x != xOld) || (y != yOld));
void GetSystemSettings()
::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &m_nWheelLines, 0);
#ifndef SPI_GETWHEELSCROLLCHARS
const UINT SPI_GETWHEELSCROLLCHARS = 0x006C;
#endif
::SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &m_nHWheelChars, 0);
bool IsScrollingChildren() const
return (m_dwExtendedStyle & SCRL_SCROLLCHILDREN) != 0;
bool IsErasingBackground() const
return (m_dwExtendedStyle & SCRL_ERASEBACKGROUND) != 0;
bool IsNoThumbTracking() const
return (m_dwExtendedStyle & SCRL_NOTHUMBTRACKING) != 0;
bool IsSmoothScroll() const
return (m_dwExtendedStyle & SCRL_SMOOTHSCROLL) != 0;
;
template <class T, class TBase = ATL::CWindow>
class ATL_NO_VTABLE CScrollDialogImpl : public ATL::CDialogImpl<T, TBase>, public CScroller< T >
public:
void InitializeScroll(HWND hWnd)
T* pT = static_cast<T*>(this);
pT->GetSystemSettings();
RECT rect = ;
this->GetClientRect(&rect);
pT->DoSize(rect.right, rect.bottom);
BEGIN_MSG_MAP(CScrollDialogImpl)
MESSAGE_HANDLER(WM_VSCROLL, CScroller< T >::OnVScroll)
MESSAGE_HANDLER(WM_HSCROLL, CScroller< T >::OnHScroll)
MESSAGE_HANDLER(WM_MOUSEWHEEL, CScroller< T >::OnMouseWheel)
MESSAGE_HANDLER(WM_MOUSEHWHEEL, CScroller< T >::OnMouseHWheel)
MESSAGE_HANDLER(WM_SETTINGCHANGE, CScroller< T >::OnSettingChange)
MESSAGE_HANDLER(WM_SIZE, CScroller< T >::OnSize)
ALT_MSG_MAP(1)
COMMAND_ID_HANDLER(ID_SCROLL_UP, CScroller< T >::OnScrollUp)
COMMAND_ID_HANDLER(ID_SCROLL_DOWN, CScroller< T >::OnScrollDown)
COMMAND_ID_HANDLER(ID_SCROLL_PAGE_UP, CScroller< T >::OnScrollPageUp)
COMMAND_ID_HANDLER(ID_SCROLL_PAGE_DOWN, CScroller< T >::OnScrollPageDown)
COMMAND_ID_HANDLER(ID_SCROLL_TOP, CScroller< T >::OnScrollTop)
COMMAND_ID_HANDLER(ID_SCROLL_BOTTOM, CScroller< T >::OnScrollBottom)
COMMAND_ID_HANDLER(ID_SCROLL_LEFT, CScroller< T >::OnScrollLeft)
COMMAND_ID_HANDLER(ID_SCROLL_RIGHT, CScroller< T >::OnScrollRight)
COMMAND_ID_HANDLER(ID_SCROLL_PAGE_LEFT, CScroller< T >::OnScrollPageLeft)
COMMAND_ID_HANDLER(ID_SCROLL_PAGE_RIGHT, CScroller< T >::OnScrollPageRight)
COMMAND_ID_HANDLER(ID_SCROLL_ALL_LEFT, CScroller< T >::OnScrollAllLeft)
COMMAND_ID_HANDLER(ID_SCROLL_ALL_RIGHT, CScroller< T >::OnScrollAllRight)
END_MSG_MAP()
;
然后您可以如下使用它(启用垂直和水平滚动条的无模式对话框)
class CMainDlg : public CScrollDialogImpl<CMainDlg>, public CUpdateUI<CMainDlg>,
public CMessageFilter, public CIdleHandler
public:
enum IDD = IDD_MAINDLG ;
virtual BOOL PreTranslateMessage(MSG* pMsg)
return CWindow::IsDialogMessage(pMsg);
virtual BOOL OnIdle()
UIUpdateChildWindows();
return FALSE;
BEGIN_UPDATE_UI_MAP(CMainDlg)
END_UPDATE_UI_MAP()
BEGIN_MSG_MAP(CMainDlg)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
COMMAND_ID_HANDLER(IDOK, OnOK)
COMMAND_ID_HANDLER(IDCANCEL, OnCancel)
CHAIN_MSG_MAP(CScrollDialogImpl<CMainDlg>);
END_MSG_MAP()
LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
this->InitializeScroll(m_hWnd);
RECT rect = ;
this->GetClientRect(&rect);
SetScrollSize(rect.right + 200, rect.bottom + 200);
// center the dialog on the screen
CenterWindow();
// set icons
HICON hIcon = AtlLoadIconImage(IDR_MAINFRAME, LR_DEFAULTCOLOR,
::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON));
SetIcon(hIcon, TRUE);
HICON hIconSmall = AtlLoadIconImage(IDR_MAINFRAME, LR_DEFAULTCOLOR,
::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON));
SetIcon(hIconSmall, FALSE);
// register object for message filtering and idle updates
CMessageLoop* pLoop = _Module.GetMessageLoop();
ATLASSERT(pLoop != NULL);
pLoop->AddMessageFilter(this);
pLoop->AddIdleHandler(this);
UIAddChildWindowContainer(m_hWnd);
return TRUE;
LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
// unregister message filtering and idle updates
CMessageLoop* pLoop = _Module.GetMessageLoop();
ATLASSERT(pLoop != NULL);
pLoop->RemoveMessageFilter(this);
pLoop->RemoveIdleHandler(this);
return 0;
LRESULT OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
CAboutDlg dlg;
dlg.DoModal();
return 0;
LRESULT OnOK(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
// TODO: Add validation code
CloseDialog(wID);
return 0;
LRESULT OnCancel(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
CloseDialog(wID);
return 0;
void CloseDialog(int nVal)
DestroyWindow();
::PostQuitMessage(nVal);
;
【讨论】:
以上是关于创建可滚动对话框的主要内容,如果未能解决你的问题,请参考以下文章
Creating Dialogbased Win32 Application / 创建基于对话框的Win32应用程序Edit Control的应用Unicode转ANSI自动滚动 /