介绍 使用MFC为自定义CWnd或cdialog派生的类实现标准滚动行为是相当重要的。至少有3或4个消息处理程序需要编写,以及每个滚动条的许多滚动参数,每当您的窗口或对话框调整大小时,都需要对这些滚动条进行调整。在本文中,我介绍了一个c++助手类,它可以用于向任何CWnd或CDialog类添加水平滚动和/或垂直滚动。与您所见过的其他实现不同,没有需要派生的可滚动对话框基类。使用助手类(CScrollHelper)不需要更改继承层次结构。 背景 在详细讨论如何使用helper类之前,我想引用一些引用,它们很好地解释了实现滚动的基础知识。第一个引用是MSDN文章262954,“如何在Visual c++中创建具有滚动条的可调整大小的对话框”。本文给出了一个如何向CDialog类添加垂直滚动条支持的示例。第二个参考来自Jeff Prosise(微软出版社)的《用MFC编程Windows,第二版》一书。本书的第2章解释了在cwnd派生类中实现滚动的细节,并给出了一个支持水平和垂直滚动的简单电子表格应用程序的示例代码。 对于cwnd派生的类,实现滚动的第一步是确保用窗口样式创建窗口,WS_HSCROLL(如果您想要水平滚动条)和/或WS_VSCROLL(如果您想要垂直的、附加在右边的滚动条)。隐藏,复制Code
Create(NULL, "CScrollWnd", WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL, CRect(0,0,0,0), parentWnd, 0, NULL);
对于对话框,窗口样式通常是通过资源编辑器设置的。在VS 2003中,我通常确保为任何我想要滚动的对话框设置以下属性: 边框=“调整大小”,如果你有一个弹出的对话框。如果您的对话框是嵌入在父容器中的子窗口,您可以选择另一种样式,如“None”(演示项目有一个这样的示例)。 Clip Children = "True"。此设置可帮助最小化在调整对话框大小时显示的闪烁。 水平滚动条= "True"。这相当于添加窗口样式WS_HSCROLL。 如果您的对话框是嵌入在父容器中的子窗口,则样式= "Child"。 垂直滚动条= "True"。这相当于添加窗口样式WS_VSCROLL。 可见= " True "。Visual Studio在某些情况下默认为“False”,所以您需要检查此设置。 使用的代码 CScrollHelper类在两个源文件中实现:ScrollHelper.h和ScrollHelper.cpp。类的公共接口如下所示:收缩,复制Code
class CScrollHelper { public: CScrollHelper(); ~CScrollHelper(); // Attach/detach a CWnd or CDialog. void AttachWnd(CWnd* pWnd); void DetachWnd(); // Set/get the virtual display size. // When the dialog or window // size is smaller than the display // size, then that is when // scrollbars will appear. Set either // the display width or display // height to zero if you don't want to // enable the scrollbar in the // corresponding direction. void SetDisplaySize(int displayWidth, int displayHeight); const CSize& GetDisplaySize() const; // Get current scroll position. // This is needed if you are scrolling // a custom CWnd which implements its // own drawing in OnPaint(). const CSize& GetScrollPos() const; // Get current page size. Useful // for debugging purposes. const CSize& GetPageSize() const; // Scroll back to top, left, or // top-left corner of the window. void ScrollToOrigin(bool scrollLeft, bool scrollTop); // Message handling. void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt); void OnSize(UINT nType, int cx, int cy); };
为了向cdialog派生的类(例如CScrollDlg)添加滚动支持,我们首先向对话框的类定义(包括文件)添加一个私有成员:复制Code
class CScrollHelper; // Forward class declaration. class CScrollDlg : public CDialog { ... private: CScrollHelper* m_scrollHelper; };
接下来,我们向与滚动相关的类定义(包括文件)添加四个消息处理程序:复制Code
// Generated message map functions. //{{AFX_MSG(CScrollDlg) ... afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt); afx_msg void OnSize(UINT nType, int cx, int cy); //}}AFX_MSG DECLARE_MESSAGE_MAP()
在对话框源文件中,包括ScrollHelper.h文件,并为四个消息处理程序添加消息映射项:复制Code
#include "ScrollHelper.h" ... BEGIN_MESSAGE_MAP(CScrollDlg, CDialog) //{{AFX_MSG_MAP(CScrollDlg) ... ON_WM_HSCROLL() ON_WM_VSCROLL() ON_WM_MOUSEWHEEL() ON_WM_SIZE() //}}AFX_MSG_MAP END_MESSAGE_MAP()
然后在dialog构造函数中创建helper类的实例,并将该对话框附加到实例中:复制Code
CScrollDlg::CScrollDlg(CWnd* pParent) : CDialog(IDD_SCROLL_DLG, pParent) { // Create the scroll helper // and attach it to this dialog. m_scrollHelper = new CScrollHelper; m_scrollHelper->AttachWnd(this); }
记住在对话框析构函数中删除助手类实例:复制Code
CScrollDlg::~CScrollDlg() { delete m_scrollHelper; }
接下来,通过简单地委托给helper类来实现这四个消息处理程序,该类具有与消息处理程序完全相同的签名的方法:Hide收缩,复制Code
void CScrollDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { m_scrollHelper->OnHScroll(nSBCode, nPos, pScrollBar); } void CScrollDlg::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { m_scrollHelper->OnVScroll(nSBCode, nPos, pScrollBar); } BOOL CScrollDlg::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) { BOOL wasScrolled = m_scrollHelper->OnMouseWheel(nFlags, zDelta, pt); return wasScrolled; } void CScrollDlg::OnSize(UINT nType, int cx, int cy) { CDialog::OnSize(nType, cx, cy); m_scrollHelper->OnSize(nType, cx, cy); }
完成滚动支持的最后一步是在helper类中设置“显示大小”。它表示对话框的虚拟显示区域,是一个可以在代码本身中预先确定和修复的值,或者在运行时动态计算的值。如果您熟悉c#中的Windows窗体编程,那么显示大小的概念类似于可滚动控件中的DisplayRectangle属性。显示大小表示滚动条出现或消失的精确阈值或点。例如,如果用户将对话框的大小调整到小于显示的大小,则会出现滚动条,允许您滚动对话框来访问整个虚拟显示界面。如果用户将对话框的大小调整到大于显示的大小,那么滚动条就会消失。显示大小通常是一个固定的值,在你第一次设置它之后:Hide 复制Code
CScrollDlg::CScrollDlg(CWnd* pParent) : CDialog(IDD_SCROLL_DLG, pParent) { // Create the scroll helper and // attach it to this dialog. m_scrollHelper = new CScrollHelper; m_scrollHelper->AttachWnd(this); // We also set the display size // equal to the dialog size. // This is the size of the dialog // in pixels as laid out // in the resource editor. m_scrollHelper->SetDisplaySize(701, 375); // Create the dialog. Create(IDD_SCROLL_DLG, pParent); }
在CScrollHelper类的接口中,您可能已经注意到了GetPageSize()方法。页面大小是另一个重要的滚动参数,由helper类在内部管理。基本上,页面大小与对话框大小相同(或者更准确地说,与对话框的客户区大小相同)。这类似于Win表单中的ClientRectangle属性。ratio页面大小的显示器大小使用Windows确定多大的“拇指”部分滚动条。拇指的大小给你表明你正在查看多少虚拟显示表面(和拇指的位置告诉你哪一部分的虚拟显示表面你看)。例如,假设你正在查看很长在Microsoft Word文档(100页)。您将看到的拇指垂直滚动条很小。另一方面,如果你正在编辑的文档比单个页面只有一段时间,你会发现滚动条上的拇指是接近最大大小。 TestScroll应用程序 演示项目(TestScroll)演示了使用助手类。这是一个我从头开始创建MDI应用程序使用Visual Studio。生成项目,我违约VS向导选项除了文档/视图支持复选框,我不。然后我写了两个新类,CScrollDlg CScrollWnd。CScrollDlg是一个对话框类,使用辅助类来实现滚动(如上述代码部分所示)。它只显示四个按钮,一个在每个角落的虚拟显示表面,以及CListBox显示当前滚动参数对话框被调整大小。CScrollWnd是一个自定义CWnd-derived类,向你展示了如何添加滚动支持non-dialog类。有趣的是这里的实现是这个类描绘一个矩形代表固定的显示尺寸。所以你改变窗口的大小,你可以看到正是当一个滚动条会出现或消失。 生成的MDI应用程序提供了一个类称为CChildView,这是包含在MDI子框架窗口。CChildView是集成的起点和我上面两个新类。CChildView提供它自己的内容,而是我修改或创建一个CScrollDlg覆盖整个客户区CScrollWnd实例。隐藏,复制Code
int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct) { if ( CWnd::OnCreate(lpCreateStruct) == -1 ) return -1; // We either create a CScrollWnd or a CScrollDlg. // We alternate using a counter. static int counter = 0; if ( counter % 2 == 0 ) m_scrollWin = new CScrollWnd(this); else m_scrollWin = new CScrollDlg(this); ++counter; return 0; }
测试演示应用程序,只需使用文件|新菜单项打开MDI子窗口。第一次你会得到一个CScrollWnd实例。第二次,CScrollDlg将被创建。每一次你选择“新”,它将替代之间的两种类型的例子。 下面的快照显示了滚动CWnd-derived类。MDI子窗口显示滚动条顶部自窗口大小小于显示大小。底部MDI子没有滚动条,你可以清楚地看到,窗口大小大于固定显示尺寸(由蓝色矩形)。在底部MDI的孩子,请注意页面大小是报告为0 x 0。这就是助手类能够隐藏滚动条——通过设置内部滚动参数如滚动位置和页面大小为零值。 下面的快照显示了CDialog滚动的一个例子。注意底部MDI子窗口的标题栏显示当前滚动位置,并且滚动对话框的右下角。滚动到最大位置时,注意到一个有趣的一点是滚动的位置(222、230)添加到页面大小(479、145)=显示尺寸(701、375)。 总而言之,CScrollHelper类很容易添加滚动支持CWnd或CDialog类,因为它负责实施所有必要的消息处理程序。使用助手类的关键是能够正确地设置显示大小。CWnd类,您可能还需要进一步看GDI映射模式和逻辑,设备坐标之间的转换。 历史 2005年7月5日, 最初的版本。 2005年7月6日, 添加Get32BitScrollPos()函数根据MSDN文章ID 152252,“如何获得32位滚动位置在滚动消息”。 处理SB_THUMBPOSITION OnHScroll / OnVScroll。 添加了vc++ 6.0演示项目。(由于PJ阿伦兹建议修复。) 2005年9月8日, 添加GetClientRectSB () helper函数。 使用内存DC图演示项目(CScrollWnd类),以避免闪烁的调整。 本文转载于:http://www.diyabc.com/frontweb/news5239.html