窗体设计窗口面面观
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
int SetWindowRgn( HWND hWnd, HRGN hRgn, BOOL bRedraw);
显然,在 SDK 函数中,仅仅增加了一个窗口句柄 hWnd 用于指定该“区域”将适用的 窗口。 为了使设计出自己所希望的窗口形状,在 CRgn 类中,提供了为数甚众的函数用于实现 对“区域”的操作: “区域”创建:
第三节:简单实现技术:多边形(Polygon)
在本章中, 利用在上一节中介绍的多边形 “区域” 构造技术来实现一个形状奇特的窗口。
程序设计目标如图 2-5 所示。为了增加程序的观赏性,在程序中也同时使用了圆形的 “区域” 。
图 2-5 多边形窗口
这个窗口究竟与普通窗口有什么不一样呢?将鼠标移到窗口“客户区”中的四个“洞” 中再按下鼠标键你就会明白了:程序已经失去了焦点( Focus) !也就是说, “客户区”中的 四个“洞”现在不属于该程序! 下面,逐步实现它。 1. 利用 MFC AppWizard(exe)创建一基于对话框的程序 Polygon。 Application:类 CPolygonApp 在 Polygon.h 和 Polygon.cpp 中; Dialog:类 CPolygonDlg 在 PolygonDlg.h 和 PolygonDlg.cpp 中。 去掉程序对 ActiveX 的支持; 确定对话框 IDD_POLYGON_DIALOG 具有 Popup 属性。
class CPolygonDlg : public CDialog { // Construction public: CPolygonDlg(CWnd* pParent = NULL); …… // Implementation protected: HICON m_hIcon; // standard constructor
第二节:窗体设计基础知识:区域(Region)
程序窗体的设计的核心函数其实只有一个:SetWindowRgn() 。该函数的定义如下:
CWnd::SetWindowRgn() int SetWindowRgn( HRGN hRgn, BOOL bReHale Waihona Puke Baiduraw );
SetWindowRgn()仅仅带两个参数:一个区域( Region)句柄(Handle)hRgn 和一个 布尔(Boolern)变量 bRedraw。 hRgn 决定将来绘画及鼠标消息的受限区域( Limited Area) ,该区域的形状决定了将来 程序窗口在通常情形下能对鼠标及绘画消息作出响应的区域; bRedraw 的不同值则决定了在该窗口被设置区域后操作系统应该采取的行动:如 果 bRedraw 为 TRUE , 操 作 系 统 发 送 WM_WINDOWPOSCHANGING 及 WM_WINDOWPOSCHANGED 消息,使得窗口可以即时重绘;否则,不采取任何行动反映 这一变化。在窗口可见时,将 bRedraw 设为 TRUE 几乎总是不错的选择。 函数成功执行时返回一非零值;否则,返回零。 调用该函数时需要注意的是, 该区域的坐标是以整个窗口区域左上角为参照而非以窗口 客户区(Client Area) 左上角为参照点! 另外,由于该区域句柄是由应用程序自己维护的(操 作系统在应用程序成功设置区域后仅仅根据 bRedraw 值决定是否发送重绘消息,之后就不 再对该区域作操作) ,在窗口被设置区域后,该句柄应一直存在;而且,除非必要,不要对 该句柄作任何进一步的操作。 既然“区域”在窗口设计中具有这么重要的作用,它究竟代表着什么呢? 所谓一个“区域” ,就是一可用于鼠标击键测试的矩形(Rectangle) 、多边形(Polygon) 、 椭圆(Ellipse)或前述三者的结合体,一个区域可被填充( Filled) 、绘制( Painted) 、反转 (Inverted)等。 另外,还应该提一下 SetWindowRegion()函数的 SDK 形式,在本章的最后一个程序 中,将通过该函数而不是其 CWnd 类的成员函数来实现窗口“区域”的设置。
// Generated message map functions //{{AFX_MSG(CPolygonDlg) virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); afx_msg UINT OnNcHitTest(CPoint point); afx_msg BOOL OnEraseBkgnd(CDC* pDC); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: CBitmap m_bmp; CRgn m_rgnWnd; };
CreateRectRgn() :用于实现最简单的矩形“区域” ; CreatePolygonRgn () :用于实现多边形“区域” ; CreatePolyPolygonRgn () :用于实现更复杂的多边形“区域” (如多边形“区域”之间 相互交叠等) ; CreateRectRgnIndirect () :用于间接实现矩形“区域” ; CreateRoundRectRgn () :用于实现圆角矩形“区域” ; CreateEllipticRgn () :用于实现椭圆“区域” ; CreateEllipticRgnIndirect() :用于间接实现椭圆“区域” ; ExtCreateRegion () :用于从现有“区域”及过渡数据创建一新的“区域” ; “区域”操作: FillRgn () :用于实现对“区域”的特定填充; FrameRgn() :以特定刷子(Brush)给“区域”描边; OffsetRgn() :对“区域”作一定偏移; PaintRgn() :绘制该“区域” ; SetPolyFillMode() :设置多边形“区域”填充模式; SetRectRgn() :将“区域”以指定坐标转化为一矩形“区域” ; CombineRgn() :将两个“区域”以特定逻辑如“与” 、 “和”等进行合并,在后面的例 程中,将见到该函数强大的功能; InvertRgn() :将“区域”反转; 获取“区域”信息: GetPolyFillMode() :获取多边形“区域”填充模式; GetRegionData() :获取有关构成该“区域”的边界信息并将其写入指定的缓冲区中; GetRgnBox() :获取“区域”的包络矩形; “区域”测试: EqualRgn() :测试两个“区域”是否相同; PtInRegion() :测试一个点是否位于指定“区域”中; RectInRegion() :测试一矩形是否位于指定“区域”中; 另外,在“区域”创建中常用的另一种手法是使用“路径” (Path) 。 所谓“路径” ,是由函数 BeginPath()及 EndPath()之间有效的绘图操作所定义的区 域。它可以通过函数 PathToRegion()转化为在前面所介绍的“区域” 。 函数 CDC: :BeginPath()及 CDC: :EndPath()均不带传入参数,仅仅通过返回值 指出“路径”创建的成功与否。 函数 PathToRegion()原型为:
图 2-6 设置对话框风格
删除该对话框中的其他所有控件。最终结果如图 2-6 所示。
向项目中引入一位图,将其 ID 设为 IDB_BACKGROUND。该位图如图 2-7 所示。该 位图可以在 C:\Windows\安装程序.bmp 中找到。
图 2-7 引入位图
2.
向类 CPolygonDlg 中加入两个成员变量用于位图及“区域”数据的保存。
图 2-1 Kjofol
图 2-2 WinAmp
图 2-3 JetAudio
图 2-4 RealPlayer
如果在未对这些程序作全面考察时,你会选择哪个程序作自己的播放器呢?RealPlayer 具有标准的 Windows 程序界面,使用时会很容易上手,RealPlayer 在网络传输速度受限制时 能保证你的曲目较连续地播放; JetAudio 众多的控制面板则意味着它可能能播放不同格式的 文件,的确,JetAudio 几乎能播放所有格式如 WAV 、MIDI、MP3、 MPEG、AVI 等,其极 富个性的遥控器设计及仿实际播放机面板的设计更是让人赏心悦目,总之,JetAudio 几乎是 上佳的选择;WinAmp 则于中规中举的外观下暗含不俗表现:简洁典雅的界面,方便实用的 按钮,千变万化的外壳(Skin) ,一切都于平凡中见神奇,软件迅速的升级速度和对众多插 件(Plaug-ins)的支持更是使得 WinAmp 的开发性极强;然而,笔者却对 Kjofol 一见倾心, 不为别的, 别具一格的程序外观足以影响个人的选择: 洒脱不羁的界面中实现的功能却一个 也不少:一样支持众多的文件格式,一样富于想象力但仍极简洁的按钮,一样支持外壳的自 定制,一样支持第三方插件。 仅凭漂亮的外观,Kjofol 的开发者就足以自傲。 如果说漂亮的界面有利而无弊,怎么实现它?
HRGN PathToRegion( HDC hdc);
该函数接受一设备上下文句柄 (显然, 需要转化的 “路径” 应该已被选入该设备环境中) , 函数成功执行时,则返回一“区域”句柄。 最后,也可以通过对数据进行分析自己进行“区域”使用的各种操作,在本章的最后一 个例程中将通过对一位图数据的扫描来生成一形状奇特的“区域” 。
窗体设计——窗口面面观
第一节:漂亮窗体魅力所在
设计的程序对用户有吸引力吗?人们总能寄希望于程序精彩的内涵吗?“酒好不怕巷 子深” 能否在任何情况下适用?在一个软硬件技术不断发展的时代, 对大多数并非计算机专 家的用户来说,在面临多种选择时,优秀的程序怎样才能从众多的产品中脱颖而出? 这是一个对大多数程序,尤其是商用程序来说相当重要的问题。 显然,人们只能通过程序最直观的表现:程序界面,来作出判断。一个富于特色界面程 序的作者,才有可能写出更好的程序。这个结论有为数甚众的实例支持。 另一方面,对一个程序员来说,这还有别的含义。由于希望所有基于 Windows 操作系 统的程序具有相同的、 至少是相似的使用方法, Microsoft 公司尽量使得用 Visual C++开发的 Win32 应用程序具有相同的外观。从 SDK (Software Development Kit)到 AFX (这是一个 胎死腹中的方案,它被自己的高度抽象性扼杀了)到 MFC(Microsoft Fundamental Class) , 随着开发工具的强大,程序员所要作的工作越来越少。但是,这样做的一个必然结果是系统 的许多细节被隐藏起来, 能供程序员自由发挥的空间越来越小, 如果希望编写出有个性的代 码,不得不付出比原来更多的精力。窗口的设计是一个典型的例子。 在众多的播放器程序中,Kjofol,WinAmp,JetAudio,RealPlayer 可以说相当不错。图 2-1,2-2,2-3,2-4 依次为上述播放器的界面。
3.
在类 CPolygonDlg 的构造函数中对 m_bmp 进行初始化, 将在前面加入的位图载入。
CPolygonDlg::CPolygonDlg(CWnd* pParent /*=NULL*/) : CDialog(CPolygonDlg::IDD, pParent) { //{{AFX_DATA_INIT(CPolygonDlg) // NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT // Note that LoadIcon does not require a subsequent DestroyIcon in Win32 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); VERIFY(m_bmp.LoadBitmap(IDB_BACKGROUND)); }
显然,在 SDK 函数中,仅仅增加了一个窗口句柄 hWnd 用于指定该“区域”将适用的 窗口。 为了使设计出自己所希望的窗口形状,在 CRgn 类中,提供了为数甚众的函数用于实现 对“区域”的操作: “区域”创建:
第三节:简单实现技术:多边形(Polygon)
在本章中, 利用在上一节中介绍的多边形 “区域” 构造技术来实现一个形状奇特的窗口。
程序设计目标如图 2-5 所示。为了增加程序的观赏性,在程序中也同时使用了圆形的 “区域” 。
图 2-5 多边形窗口
这个窗口究竟与普通窗口有什么不一样呢?将鼠标移到窗口“客户区”中的四个“洞” 中再按下鼠标键你就会明白了:程序已经失去了焦点( Focus) !也就是说, “客户区”中的 四个“洞”现在不属于该程序! 下面,逐步实现它。 1. 利用 MFC AppWizard(exe)创建一基于对话框的程序 Polygon。 Application:类 CPolygonApp 在 Polygon.h 和 Polygon.cpp 中; Dialog:类 CPolygonDlg 在 PolygonDlg.h 和 PolygonDlg.cpp 中。 去掉程序对 ActiveX 的支持; 确定对话框 IDD_POLYGON_DIALOG 具有 Popup 属性。
class CPolygonDlg : public CDialog { // Construction public: CPolygonDlg(CWnd* pParent = NULL); …… // Implementation protected: HICON m_hIcon; // standard constructor
第二节:窗体设计基础知识:区域(Region)
程序窗体的设计的核心函数其实只有一个:SetWindowRgn() 。该函数的定义如下:
CWnd::SetWindowRgn() int SetWindowRgn( HRGN hRgn, BOOL bReHale Waihona Puke Baiduraw );
SetWindowRgn()仅仅带两个参数:一个区域( Region)句柄(Handle)hRgn 和一个 布尔(Boolern)变量 bRedraw。 hRgn 决定将来绘画及鼠标消息的受限区域( Limited Area) ,该区域的形状决定了将来 程序窗口在通常情形下能对鼠标及绘画消息作出响应的区域; bRedraw 的不同值则决定了在该窗口被设置区域后操作系统应该采取的行动:如 果 bRedraw 为 TRUE , 操 作 系 统 发 送 WM_WINDOWPOSCHANGING 及 WM_WINDOWPOSCHANGED 消息,使得窗口可以即时重绘;否则,不采取任何行动反映 这一变化。在窗口可见时,将 bRedraw 设为 TRUE 几乎总是不错的选择。 函数成功执行时返回一非零值;否则,返回零。 调用该函数时需要注意的是, 该区域的坐标是以整个窗口区域左上角为参照而非以窗口 客户区(Client Area) 左上角为参照点! 另外,由于该区域句柄是由应用程序自己维护的(操 作系统在应用程序成功设置区域后仅仅根据 bRedraw 值决定是否发送重绘消息,之后就不 再对该区域作操作) ,在窗口被设置区域后,该句柄应一直存在;而且,除非必要,不要对 该句柄作任何进一步的操作。 既然“区域”在窗口设计中具有这么重要的作用,它究竟代表着什么呢? 所谓一个“区域” ,就是一可用于鼠标击键测试的矩形(Rectangle) 、多边形(Polygon) 、 椭圆(Ellipse)或前述三者的结合体,一个区域可被填充( Filled) 、绘制( Painted) 、反转 (Inverted)等。 另外,还应该提一下 SetWindowRegion()函数的 SDK 形式,在本章的最后一个程序 中,将通过该函数而不是其 CWnd 类的成员函数来实现窗口“区域”的设置。
// Generated message map functions //{{AFX_MSG(CPolygonDlg) virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); afx_msg UINT OnNcHitTest(CPoint point); afx_msg BOOL OnEraseBkgnd(CDC* pDC); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: CBitmap m_bmp; CRgn m_rgnWnd; };
CreateRectRgn() :用于实现最简单的矩形“区域” ; CreatePolygonRgn () :用于实现多边形“区域” ; CreatePolyPolygonRgn () :用于实现更复杂的多边形“区域” (如多边形“区域”之间 相互交叠等) ; CreateRectRgnIndirect () :用于间接实现矩形“区域” ; CreateRoundRectRgn () :用于实现圆角矩形“区域” ; CreateEllipticRgn () :用于实现椭圆“区域” ; CreateEllipticRgnIndirect() :用于间接实现椭圆“区域” ; ExtCreateRegion () :用于从现有“区域”及过渡数据创建一新的“区域” ; “区域”操作: FillRgn () :用于实现对“区域”的特定填充; FrameRgn() :以特定刷子(Brush)给“区域”描边; OffsetRgn() :对“区域”作一定偏移; PaintRgn() :绘制该“区域” ; SetPolyFillMode() :设置多边形“区域”填充模式; SetRectRgn() :将“区域”以指定坐标转化为一矩形“区域” ; CombineRgn() :将两个“区域”以特定逻辑如“与” 、 “和”等进行合并,在后面的例 程中,将见到该函数强大的功能; InvertRgn() :将“区域”反转; 获取“区域”信息: GetPolyFillMode() :获取多边形“区域”填充模式; GetRegionData() :获取有关构成该“区域”的边界信息并将其写入指定的缓冲区中; GetRgnBox() :获取“区域”的包络矩形; “区域”测试: EqualRgn() :测试两个“区域”是否相同; PtInRegion() :测试一个点是否位于指定“区域”中; RectInRegion() :测试一矩形是否位于指定“区域”中; 另外,在“区域”创建中常用的另一种手法是使用“路径” (Path) 。 所谓“路径” ,是由函数 BeginPath()及 EndPath()之间有效的绘图操作所定义的区 域。它可以通过函数 PathToRegion()转化为在前面所介绍的“区域” 。 函数 CDC: :BeginPath()及 CDC: :EndPath()均不带传入参数,仅仅通过返回值 指出“路径”创建的成功与否。 函数 PathToRegion()原型为:
图 2-6 设置对话框风格
删除该对话框中的其他所有控件。最终结果如图 2-6 所示。
向项目中引入一位图,将其 ID 设为 IDB_BACKGROUND。该位图如图 2-7 所示。该 位图可以在 C:\Windows\安装程序.bmp 中找到。
图 2-7 引入位图
2.
向类 CPolygonDlg 中加入两个成员变量用于位图及“区域”数据的保存。
图 2-1 Kjofol
图 2-2 WinAmp
图 2-3 JetAudio
图 2-4 RealPlayer
如果在未对这些程序作全面考察时,你会选择哪个程序作自己的播放器呢?RealPlayer 具有标准的 Windows 程序界面,使用时会很容易上手,RealPlayer 在网络传输速度受限制时 能保证你的曲目较连续地播放; JetAudio 众多的控制面板则意味着它可能能播放不同格式的 文件,的确,JetAudio 几乎能播放所有格式如 WAV 、MIDI、MP3、 MPEG、AVI 等,其极 富个性的遥控器设计及仿实际播放机面板的设计更是让人赏心悦目,总之,JetAudio 几乎是 上佳的选择;WinAmp 则于中规中举的外观下暗含不俗表现:简洁典雅的界面,方便实用的 按钮,千变万化的外壳(Skin) ,一切都于平凡中见神奇,软件迅速的升级速度和对众多插 件(Plaug-ins)的支持更是使得 WinAmp 的开发性极强;然而,笔者却对 Kjofol 一见倾心, 不为别的, 别具一格的程序外观足以影响个人的选择: 洒脱不羁的界面中实现的功能却一个 也不少:一样支持众多的文件格式,一样富于想象力但仍极简洁的按钮,一样支持外壳的自 定制,一样支持第三方插件。 仅凭漂亮的外观,Kjofol 的开发者就足以自傲。 如果说漂亮的界面有利而无弊,怎么实现它?
HRGN PathToRegion( HDC hdc);
该函数接受一设备上下文句柄 (显然, 需要转化的 “路径” 应该已被选入该设备环境中) , 函数成功执行时,则返回一“区域”句柄。 最后,也可以通过对数据进行分析自己进行“区域”使用的各种操作,在本章的最后一 个例程中将通过对一位图数据的扫描来生成一形状奇特的“区域” 。
窗体设计——窗口面面观
第一节:漂亮窗体魅力所在
设计的程序对用户有吸引力吗?人们总能寄希望于程序精彩的内涵吗?“酒好不怕巷 子深” 能否在任何情况下适用?在一个软硬件技术不断发展的时代, 对大多数并非计算机专 家的用户来说,在面临多种选择时,优秀的程序怎样才能从众多的产品中脱颖而出? 这是一个对大多数程序,尤其是商用程序来说相当重要的问题。 显然,人们只能通过程序最直观的表现:程序界面,来作出判断。一个富于特色界面程 序的作者,才有可能写出更好的程序。这个结论有为数甚众的实例支持。 另一方面,对一个程序员来说,这还有别的含义。由于希望所有基于 Windows 操作系 统的程序具有相同的、 至少是相似的使用方法, Microsoft 公司尽量使得用 Visual C++开发的 Win32 应用程序具有相同的外观。从 SDK (Software Development Kit)到 AFX (这是一个 胎死腹中的方案,它被自己的高度抽象性扼杀了)到 MFC(Microsoft Fundamental Class) , 随着开发工具的强大,程序员所要作的工作越来越少。但是,这样做的一个必然结果是系统 的许多细节被隐藏起来, 能供程序员自由发挥的空间越来越小, 如果希望编写出有个性的代 码,不得不付出比原来更多的精力。窗口的设计是一个典型的例子。 在众多的播放器程序中,Kjofol,WinAmp,JetAudio,RealPlayer 可以说相当不错。图 2-1,2-2,2-3,2-4 依次为上述播放器的界面。
3.
在类 CPolygonDlg 的构造函数中对 m_bmp 进行初始化, 将在前面加入的位图载入。
CPolygonDlg::CPolygonDlg(CWnd* pParent /*=NULL*/) : CDialog(CPolygonDlg::IDD, pParent) { //{{AFX_DATA_INIT(CPolygonDlg) // NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT // Note that LoadIcon does not require a subsequent DestroyIcon in Win32 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); VERIFY(m_bmp.LoadBitmap(IDB_BACKGROUND)); }