精华内容
下载资源
问答
  • windows的特殊对话框

    2009-07-31 23:02:00
    1、 打开保存对话框:(1)使用GetOpenFileName(LPOPENFILENAME Ipofn)创建打开对话框;Lpofn包含了初始化对话框的信息,当此函数返回时这个结构包含文件选择的信息;ltypedef struct tagOFN { DWORD lStructSize; ...

    1、          打开和保存对话框:

    (1)使用GetOpenFileName(LPOPENFILENAME Ipofn)创建打开对话框;

    Lpofn包含了初始化对话框的信息,当此函数返回时这个结构包含文件选择的信息;l

    typedef struct tagOFN {

      DWORD         lStructSize; //结构大小

      HWND          hwndOwner; //拥有这个对话框的窗口

      HINSTANCE     hInstance; //拥有此对话框的模块实例句柄

      LPCTSTR       lpstrFilter; //包含过滤信息每个过滤信息含两个字符串用/0结束,例Text Files/0*.TXT”

    后的过滤信息要/0/0结束,例: "All Files/0*.*/0/0"

      LPTSTR        lpstrCustomFilter; //默认的过滤信息

      DWORD         nMaxCustFilter; ///默认的过滤信息大小,当lpstrCustomFilter不为空时,这个至少要大于40

      DWORD         nFilterIndex; //当前选择的过滤信息索引,当为0时使用lpstrCustomFilter

      LPTSTR        lpstrFile; //包含用户选定文件的全路径和文件名、扩展名

      DWORD         nMaxFile; //lpstrFile的大小

      LPTSTR        lpstrFileTitle; //包含用户选定文件的文件名、扩展名

      DWORD         nMaxFileTitle; //lpstrFileTitle的大小

      LPCTSTR       lpstrInitialDir; //默认的显示路径

      LPCTSTR       lpstrTitle; //标题

      DWORD         Flags; //各种标志

      WORD          nFileOffset; //指定lpstrFile中文件名的偏移量,如lpstrFile"c:/dir1/dir2/file.ext",

    nFileOffset的值为12;

      WORD          nFileExtension; //指定lpstrFile中扩展名的偏移量如lpstrFile"c:/dir1/dir2/file.ext",nFileExtension的值为18

      LPCTSTR       lpstrDefExt; //默认扩展名

      LPARAM        lCustData; //保留吧

      LPOFNHOOKPROC lpfnHook; //可用来重新设定回调函数

      LPCTSTR       lpTemplateName; //以下可以都为NULL

    #if (_WIN32_WINNT >= 0x0500)

      void *        pvReserved;

      DWORD         dwReserved;

      DWORD         FlagsEx;

    #endif // (_WIN32_WINNT >= 0x0500)

    } OPENFILENAME, *LPOPENFILENAME;

    (2)使用GetSaveFileName创建保存对话框;

    2、浏览文件夹对话框:

    使用SHBrowseForFolder创建

    char DirPath[MAX_PATH];

     BROWSEINFO bi;

     ITEMIDLIST * pidl;

     

     bi.hwndOwner = NULL;

     bi.pidlRoot = NULL;

     bi.pszDisplayName = Dir;

     bi.lpszTitle = "请选择图片文件夹。";

     bi.ulFlags = BIF_RETURNONLYFSDIRS;

     bi.lpfn = NULL;

     bi.lParam = 0;

     bi.iImage = 0;

     

     pidl = SHBrowseForFolder(&bi);

     if(pidl == NULL)

      DirPath[0] = 0;

     if(!SHGetPathFromIDList(pidl,Dir))

                    DirPath[0] = 0;

     

     //释放内存

     LPMALLOC pMalloc;

     if (SUCCEEDED(SHGetMalloc(&pMalloc)))

     {

      pMalloc->Free(pidl);

      (void)pMalloc->Release();

     }

     

    3ChooseColor
    函数功能:该函数创建一个能使用户从中选择颜色的通用颜色对话框。
    函数原型:BOOL ChooseColorLPCHOOSECOLOR IpCC);

    参数:

    lpCC
    :指向一个包括初始化对话框信息的CHOOSECOLOR结构。当ChooseColor函数返回时,此结构含有有关用户颜色选择的信息。

    返回值:如果用户点击对话框中的OK按钮,返回值为非零值。CHOOSECOLOR结构中的rgbResult成员含有用户选择的颜色的RGB颜色值。如果用户取消或关闭Color对话框或错误出现,返回值为零。若想获得更多错误信息,请调用CommDlgExtondedError函数,此函数的返回值为下列中的一个:

       CDERR_FINDRESFAILURE
    CDERR_MEMLOCKFAILURECDERR_INITIALIZATION

       CDERR_NOHINSTANCE
    CDERR_LOCKRESFAILURE
    CDERR_NOHOOK
       CDERR_LOADRESFAILURE
    CDERR_NOTEMPLATECDERR_LOADSTRFAlLURE

       CDERR_STRUCTSIZE
    CDERR_MEMALLOCFAILURE
    备注Color对话框不支持彩色调色板,对话框提供的颜色的选择仅限于系统颜色和这些颜色的混合值,可以为对话框提供一个CCHOOKProc程序,此挂钩程序能处理发送给对话框的信息。通过建立CHOOSECOLOR结构中Flags成员的CC_ENABLEHOOK标志和指定IpfnHook成员中挂钩程序的地址,可使挂钩程序生效。

     

    4ChooseFont
    函数功能:该函数创建一个使用户选择逻辑字体属性的对话框,这些属性包括字体名称、字体风格(如粗体、斜体或正常体)、字号、效果(如强调线,下划线或字体颜色)和手写体(或字符集)。
    函数原型:BOOL ChooseFontLPCHOOSEFONT Ipcf);

    参数:

    Ipcf
    :指向一个含有初始化对话框信息的CHOOSEFONT结构。当返回ChooseFont函数时,此结构含有用户对字体选择的信息。

    返回值:如果用户点击对话框的OK按钮,返回值为非零值,CHOOSEFONT结构中的成员表明用户的选择。如果用户取消或关闭Font对话框或出现错误信息,返回值为零。若想获得更多错误信息。请调用CommDlgExtendedError函数,其返回值如下:

       CDERR_FINDRESFAILURE
    CDERR_NOHINSTANCECDERR_INITIALIZATION
    CDERR_NOHOOK
       CDERR_LOCKRESFAILURE
    CDERR_NOTEMPLATECDERR_LOADRESFAILURE

       CDERR_STRUCTSIZE
    CDERR_LOADSTRFAILURE
    CDERR_MAXLESSTHANMIN
       CDERR_MEMALLOCFAILURE
    CDERR_NOFONTS
    CDERR_MEMLOCKFAILURE
    备注:可以为Font对话框提供一个CFHOOKProc挂钩程序。此挂钩程序能够处理发送给对话框的信息。

    通过建立CHOOSEFONT结构中Flags成员的CE ENABLEHOOK标志和指定IPfn Hook成员中挂钩程序的地址可以使挂钩程序有效。

    挂钩程序可以把信息WM_CHOOSEFONT_GETLOGLONTWM_CHOOSEFONT_SETFLAGS

    WM_CHOOSEFONT_SETLOGFONT
    消息发送给对话框以便得到和创建当前值和对话框的图标。

    5FindText
    函数功能:该函数创建一个系统定义的无模式Find对话框,为使用户指定一个串来查找文本内的文字。
    函数原型:HWND FindTextLPFINDREPLACE lpfr);

    参数:

    Ipfr
    :指向一个FINDEPLACE结构,此结构包含用来初始对话框的信息。对话框用此结构把用户输入的信息传送到应用程序。有关更多的信息,请参见下面说明部分。

    返回值:如果函数调用成功,返回值是对话框的窗口句柄。可以使用窗口句柄与对话框联系或关闭它;如果函数调用失败,返回值为NULL。若想获得更多的错误信息,请调用CommDlgExtendedError函数。其返回值如下:

       CDERR_FINDRESFAILURE
    CDERR_MEMLOCKFAILURECDERR_INITIALIZATION
       CDERR_NOHINSTANCE
    CDERR_LOCKRESFAILURE
    CDERR_NOHOOK
       CDERR_LOADRESFAILURE
    CDERR_NOTEMPLATE
    CDERR_LOADSTRFAILURE
       CDERR_STRUCTSIZE
    CDERR_MEMALLOCFAILURE
    FRERR_BUFFERLENGTHZERO
    备注:FindText函数不执行查找操作,相反,对话框把FINDMSGSTRING己登记的信息传送到对话框窗口的窗口函数。当创建对话框时FINDReplace结构中的hwndCwner成员标识窗口。

        
    调用Find Text函数前,必须调用RegisterWindowMessage函数以得到FINDMSGSTRING信息的标识符,对话框函数在用户点击FindNext按钮或对话框被关闭时利用此标识符传送信息。FINDMSGSTRING信息的IParam参数包含一个指向FINDREPLACE结构的指针,此结构的Flags成员显示开诚信息的事件。

    其他成员显示用户的输入。

        
    若想创建对话框,必须利用应用程序的主信息链中的IsDialogMessage函数来保证对话框正确处理键盘输入,例如TabEsc键。IsDialogMessage返回值显示Find对话框是否处理信息。

        
    可以为Find对话框提供一个挂钩函数FRHookProc。挂钩函数可处理发送到对话框中的信息。为使挂钩函数生效,可设置HNDREPLACE结构的Flags成员的FR_ENABLEHOOK标志,且指定IpfnHook成员中挂钩函数的地址。

    6PageSetupDlg
    函数功能:该函数创建一个PageSetup对话框,此对话框能使用户指定打印页的属性。这些属性包括纸张大小和来源,送纸方向和页边距。
    函数原型:BOOL PageSetupDlgLPPAGESETUPDLGI ppsd);

    参数:

    Ippsd
    :指向一个包含初始化对话框信息的PAGESETUPDLG结构。当函数返回时,该结构存放有关用户选择的信息。

    返回值:如果用户点击OK钮,返回值为非零值,Ippsp参数指向的PAGESETUPDLG结构中的成员显示用户的选择。如果用户取消或关闭PageSetup对话框或错误出现,返回值为零。若想获得更多的错误信息,请调用CommDlgExtendedError函数

    7PrintDlg
    函数功能:该函数显示打印对话框或打印设置对话框。打印对话框使用户指定特殊的打印工作的特点。
    打印设置对话框不能应用在新应用程序中,它已经被PageSetupDlg函数创建的打印设置公共对话框所替代。

    函数原型:BOOL PrintDIgLPPRINTDLG Ippd);

    参数:

    Ippd
    :指向一个含有初始化对话框信息的PRINTDLG结构。当PRINTDLG函数返回时,此结构含有关用户选择的信息。

    返回值:如果用户点击OK按钮,返回值为非零值。由lppd参数指向的PRINTDLG结构中的成员显示用户的选择。如果用户取消或关闭PrintPrinterSetup对话框或错误出现,返回值为零。若想获得更多的错误信息,请调用CommDlgError函数。如果用户取消或关闭对话框,函数返回零值:否则,返回值如下:

       CDERR_FINDRESFAILURE PDERR_CRETELCFAILUPE
       COERR_INITIALIZATION PDERR_DEFAULTDIFFERENT
       CDERR_LOADRESFAILURE PDERR_DNDMMISMATCH
       CDERR_LOADSTRFAILURE PDERR_GETDEVMODEFAIL
       CKERR_LOCKRESFAILURE PDERR_INITFAILURE
       CDERR_MEMALLOCFAILURE PDERR LOADDRVFAILURE
       CDERR_MEMLOCKFAILURE PDERR_NODEFAULTPRN
       CDERR_NOHINSTANCE PDERR_NODEVICES
       CDFRR_NOHOOK PDERR_PARSEFAILURE
       CDERR_NOTEMPLATE PDERR_PRINTERNOTFOUND
       CDERR_STRUCTSIZE PDERR_RETDEFFAILURE
    备注:如果挂钩函数(由PRINTDLG结构中的IpfnrintHOOk成员或IpfnSetupHOOk成员指向的)处理WM_CTLCOLORDLG信息,挂钩函数必须返回一个刷子句柄,此刷了用来刷控制背景。

    展开全文
  • 对话框

    千次阅读 2011-05-03 15:51:00
    对话框是一种用户界面,它的主要功能是输出信息接收用户的输入。对话框与控件是密不可分的,在每个对话框内一般都有一些控件,对话框依靠这些控件与用户进行交互.一个典型的对话框例子是选择了File-Open命令后弹...

     对话框是一种用户界面,它的主要功能是输出信息和接收用户的输入。对话框与控件是密不可分的,在每个对话框内一般都有一些控件,对话框依靠这些控件与用户进行交互.一个典型的对话框例子是选择了File-Open命令后弹出的文件对话框.

      对话框是一种复杂的用户界面,本章的讨论将围绕对话框和基本控件进行。

    5.1对话框和控件的基本概念

    5.1.1对话框的基本概念

    对话框(Dialog)实际上是一个窗口.在MFC中,对话框的功能被封装在了CDialog类中,CDialog类是CWnd类的派生类.

    对话框分为模态对话框和非模态对话框两种.大部分读者都会有这样的经历,当你通过File-Open命令打开一个文件对话框后,再用鼠标去选择菜单将只会发出嘟嘟声,这是因为文件对话框是一个模态对话框.模态对话框垄断了用户的输入,当一个模态对话框打开时,用户只能与该对话框进行交互,而其它用户界面对象收不到输入信息.我们平时所遇到的大部分对话框都是模态对话框。非模态对话框的典型例子是Windows95提供的写字板程序中的搜索对话框,搜索对话框不垄断用户的输入,打开搜索对话框后,仍可与其它用户界面对象进行交互,用户可以一边搜索,一边修改文章,这样就大大方便了使用.

    本节主要介绍模态对话框,在第四节将介绍非模态对话框.

    从MFC编程的角度来看,一个对话框由两部分组成:

    对话框模板资源.对话框模板用于指定对话框的控件及其分布,Windows根据对话框模板来创建并显示对话框.

    对话框类.对话框类用来实现对话框的功能,由于对话框行使的功能各不相同,因此一般需要从CDialog类派生一个新类,以完成特定的功能.

    相应地,对话框的设计包括对话框模板的设计和对话框类的设计两个主要方面.

         与对话框有关的消息主要包括WM_INITDIALOG消息和控件通知消息。在对话框创建时,会收到WM_INITDIALOG消息,对话框对该消息的处理函数是OnInitDialog 。

         OnInitDialog的主要用处是初始化对话框。对话框的控件会向对话框发送控件通知消息,以表明控件的状态发生了变化。

    5.1.2控件的基本概念



    图5.1对话框中的控件

    控件(Control)是独立的小部件,在对话框与用户的交互过程中,控件担任着主要角色.控件的种类较多,图5.1显示了对话框中的一些基本的控件.MFC的控件类封装了控件的功能,表5.1介绍了一些常用的控件及其对应的控件类.

    表5.1

    控件 功能 对应控件类 
    静态正文(Static Text) 显示正文,一般不能接受输入信息。 CStatic 
    图片(Picture) 显式位图、图标、方框和图元文件,一般不能接受输入信息. CStatic 
    编辑框(Edit Box) 输入并编辑正文,支持单行和多行编辑. CEdit 
    命令按钮(Pushbutton) 响应用户的输入,触发相应的事件. CButton 
    检查框(Check Box) 用作选择标记,可以有选中、不选中和不确定三种状态。 CButton 
    单选按钮(Radio Button) 用来从两个或多个选项中选中一项. CButton 
    组框(Group Box) 显示正文和方框,主要用来将相关的一些控件聚成一组. CButton 
    列表框(List Box) 显示一个列表,用户可以从该列表中选择一项或多项. CListBox 
    组合框(Combo Box) 是一个编辑框和一个列表框的组合.分为简易式、下拉式和下拉列表式. CComboBox 
    滚动条(Scroll Bar) 主要用来从一个预定义范围值中迅速而有效地选取一个整数值. CScrollBar 

    控件实际上都是窗口,所有的控件类都是CWnd类的派生类.控件通常是作为对话框的子窗口而创建的,控件也可以出现在视窗口,工具条和状态条中.
    5.2对话框模板的设计

    利用Developer Studio提供的可视化设计工具,用户可以方便地设计对话框模板. 

    请读者按前面章节介绍的方法利用AppWizard建立一个名为Register的MFC应用程序,并在进入MFC AppWizard对话框后按下面几步操作:  

    在第1步中选中Single document以建立一个单文档应用程序.  
    在第4步中使Docking toolbar项不选中,这样AppWizard就不会创建工具条.  
    在第6步中先选择CRegisterView,然后在Base class栏中选择CEditView,这样CRegisterView将是CEditView的继承类,从而使视图具有了编辑功能.  
    编译并运行Register,读者会发现Register居然是个编辑器,它可以打开、编辑和保存文本文件. 当然,Register的目的不仅仅是个编辑器。假设要对某一地区的就业情况进行调查,我们希望Register程序能够登录就业情况数据并将数据存储起来. 

    要登录数据,用对话框是再合适不过了。一个典型的就业情况登录对话框如图5.1所示,本节的任务就是设计如图5.1的中文对话框模板. 

    切换至资源视图,选择Insert-Resource命令,并在Insert Resource对话框中双击Dialog项。完成后在资源视图中会出现一个名为IDD_DIALOG1的新的对话框模板资源。双击IDD_DIALOG1,则会打开该对话框模板的编辑窗口,如图5.2所示。缺省的对话框模板有OK和Cancel两个按钮,在窗口的旁边有一个控件面板,在控件面板上用鼠标选择一个控件,然后在对话框中点击,则相应的控件就被放置到了对话框模板中。图5.3显示了控件面板上的按钮所代表的控件。读者不用记忆图5.3的内容,如果不能确定控件的类型,可将鼠标在某个控件按钮上停留片刻,则会显示一个工具提示,指出该按钮所代表控件的名称。 



    图5.2 缺省的对话框模板 



    图5.3 控件面板 

    提示:若读者看不到控件面板,请在Developer Studio的工具条的空白处单击鼠标右键,并在随之弹出的菜单中选中Controls。  

             

    读者可以在对话框模板中随意加几个控件试试看。当用鼠标选择对话框或控件时,会出现一个围绕它的虚框,拖动虚框的边界可以改变对话框或控件的大小,在Developer Studio的状态条中会显示出所选对象的坐标和尺寸。控件可以被拖动,也可以按箭头键来移动选中的控件。在拖动控件时若按住Ctrl键,则控件会被复制。 

    用户可以一次选择多个控件,选择的方法有两个:1。 在对话框的空白处拖动鼠标,则拖动出来的虚线框内的控件将被选中。2。在选择控件时按住Ctrl键,则可以多重选择。 

    选中控件或对话框后按回车键,则会弹出一个属性对话框,属性对话框用来设置控件或对话框的各种属性。属性对话框是标签式对话框,第一页是常规属性(General)。一个典型的控件属性对话框如图5.4所示.如果对属性对话框中的选项的意思不明白,可以按F1键获得帮助. 



    图5.4 控件属性对话框 

    在控件属性对话框的常规属性中,有一些控件共同的属性: 

    ID属性。用于指定控件的标识符,Windows依靠ID来区分不同的控件。 

    Caption(标题)属性。静态正文、组框、按钮、检查框、单选按钮等控件可以显示标题,用来对控件进行文字说明。控件标题中的字符&使紧跟其后的字符有下划线,按Alt+下划线将启动该控件。若控件是一个单选按钮,则Alt+下划线字符将选择该按钮;若是检查框,则相当于对该检查框按空格键;若是按钮,则将激活按钮命令;若控件是一个静态正文,则将激活按tab顺序紧随其后的下一个控件。 

    Visible属性。用来指定控件是否是可见的。 

    Disable属性。使控件允许或禁止,一个禁止的控件呈灰色显示,不能接收任何输入。  
    Tabstop属性。用户可以按Tab键移动到具有Tabstop属性的控件上。Tab移动的顺序可以由用户指定。按Ctrl+D则Tab顺序会显示出来,如图5.5,用户可以用鼠标来重新指定Tab顺序。缺省的Tab顺序是控件的创建次序。  
    Group属性。用来指定一组控件,用户可以用箭头键在该组控件内移动。在同一组内的单选按钮具有互斥的特性,即在这些单选按钮中只能有一个是选中的。如果一个控件具有Group属性,则这个控件以及按Tab顺序紧随其后的所有控件都属于一组的,直到遇到另一个有Group属性的控件为止。  
    现在就开始进行对话框模板的设计。首先,用鼠标选中对话框,按回车键,在弹出的属性对话框中将ID改为IDD_REGISTER并指定对话框的标题为“登录数据”。需要注意的是,由于要在对话框中显示汉字,因此必须设定正确的语种和字体。请读者在工作区资源视图的Dialog类型中单击鼠标选中IDD_REGISTER项,然后按Alt+Enter键,并在弹出的属性对话框中的Language栏中选择Chinese(P.R.C.)。接着,打开模板的属性对话框,单击Font...按钮,并选择“宋体”。 

    接着,请将对话框模板上的所有控件删除,删除的办法是选择控件后按Del键。为了容纳所有需要的控件,需将对话框的尺寸扩大到280×180。然后,请读者按图5.1和表5.2来设计对话框模板。 

    提示:对话框的尺寸单位不是象素,而是与字体的大小有关。X方向上一个单位等于字符平均宽度的1/4,Y方向上一个单位等于字符平均高度的1/8。这样,随着字体的改变,对话框单位也会改变,对话框本身的总体比例保持不变。  



    表5.2 

    控件类型   ID   标题(Caption)   其它属性  
    组框(个人情况)   缺省   个人情况   缺省  
    组框(单位情况)   缺省   单位情况   缺省  
    静态正文(姓名)   缺省   姓名   缺省  
    编辑框(姓名)   IDC_NAME       缺省  
    检查框(婚否)   IDC_MARRIED   婚否   缺省  
    静态正文(年龄)   缺省   年龄   缺省  
    编辑框(年龄)   IDC_AGE       缺省  
    组框(性别)   缺省   性别   缺省  
    单选按钮(男)   IDC_SEX   男   Group、Tabstop  
    单选按钮(女)   缺省   女   缺省  
    组框(就业状况)   缺省   就业状况   缺省  
    单选按钮(在职)   IDC_WORK   在职   Group、Tabstop  
    单选按钮(下岗)   IDC_WORK1   下岗   缺省  
    静态正文(工作单位)   缺省   工作单位   缺省  
    编辑框(工作单位)   IDC_UNIT       缺省  
    静态正文(单位性质)   缺省   单位性质   缺省  
    组合框(单位性质)   IDC_KIND       Drop List、不排序(不选中Sort风格)、初始化列表项(见下文说明)  
    静态正文(工资收入)   缺省   工资收入   缺省  
    列表框(工资收入)   IDC_INCOME       不排序(不选中Sort)  
    按钮(确定)   IDOK   确定(&Y)   缺省  
    按钮(取消)   IDCANCEL   取消(&C)   缺省  

    请注意组合框IDC_KIND的Drop List属性,Drop List属性是在属性对话框的Styles(风格)页的Type栏中选择的,这使得IDC_KIND成为一个下拉列表式组合框。组合框有简易式(Simple)、下拉式(Dropdown)和下拉列表式(Drop List)三种。简易式组合框包含一个编辑框和一个总是显示的列表框。下拉式组合框同简易式组合框的区别在于仅当单击下滚箭头时才出现列表框。下拉列表式组合框也有一个下拉的列表框,但它的编辑框是只读的,不能输入字符。组合框IDC_KIND不要自动排序,因此需在Styles页中使Sort项不被选中。 

    组合框的列表项可以在设计模板时初始化,而列表框的初始化只能在程序中进行。请读者在组合框IDC_KIND的属性对话框的General页中输入以下几个列表项,以作为单位性质的选项。输入时要注意,换行时不要按回车键,而应按Ctrl+回车键。 

    国有企事业 

    集体企业 

    私有企业 

    中外合资 

    外商独资 



    组合框控件的一个与众不同之处是它有两个尺寸,一个是下拉前的尺寸,一个是下拉后的尺寸。当用鼠标点击组合框上的箭头后,可设定下拉后的尺寸。 

    控件最好都放在对话框模板的蓝色虚框内,控件之间的距离不要太近,否则有可能造成不正确的显示。 

    安置好控件之后,下一步的任务是指定Tab顺序。按Ctrl+D键后,会显示当前的Tab顺序,通过用鼠标点击控件可以设定新的Tab顺序,如果想放弃本次修改,在对话框的空白处点击一下即可。请读者按图5.5安排Tab顺序。 



    图5.5 对话框的Tab顺序 

    最后,需要测试一下对话框。按Ctrl+T,则会弹出一个当前模板的测试对话框,这个对话框的外观和基本行为与程序中将要弹出的对话框一样。这样,读者不用编译运行程序,通过测试对话框就可以评估对话框是否合乎要求。如果发现了错误或不满意的地方,可按ESC键退出测试对话框并重新修改对话框模板。 

    至此,对话框模板的设计就完成了。 

    5.3 对话框类的设计

    完成对话框模板的设计后,就需要设计一个对话框类以实现对话框的功能。设计对话框类主要包括下面几步: 

    创建对话框类。该类应从CDialog类派生。 

    为对话框类加入与控件相对应的成员变量。 

    为对话框进行初始化工作。 

    增加对控件通知消息的处理 



    5.3.1对话框类的创建 

    利用ClassWizard,程序员可以十分方便的创建MFC窗口类的派生类,对话框类也不例外。请读者按以下几步操作: 

    打开IDD_REGISTER对话框模板,然后按Ctrl+W进入ClassWizard。 

    进入ClassWizard后,ClassWizard发现IDD_REGISTER是一个新的对话框模板,于是它会询问是否要为IDD_REGISTER创建一个对话框类。按OK键确认。 

    如图5.6在Create New Class对话框中,在Name栏中输入CRegisterDialog,在Base class栏中选择CDialog,在Dialog ID栏中选择IDD_REGISTER。按Create按钮后,对话框类CRegisterDialog即被创建。 



    图5.6 Create New Class对话框 

    ClassWizard自动使类CRegesterDialog与IDD_REGISTER模板联系起来。 

    提示:只要想创建的类是某一MFC窗口类的派生类,一般都可以利用ClassWizard来自动完成创建。创建的一般方法是:打开ClassWizard,选择Add Class->New,然后在Create New Class对话框中输入新类的类名,选择其MFC基类,如果是对话框类,则还要选择对话框的ID。  

    5.3.2为对话框类加入成员变量 



              对话框的主要功能是输出和输入数据,例子中的登录数据对话框的任务就是输入数据。对话框需要有一组成员变量来存储数据。在对话框中,控件用来表示或输入数据,因此,存储数据的成员变量应该与控件相对应。 

    与控件对应的成员变量即可以是一个数据,也可以是一个控件对象,这将由具体需要来确定。例如,可以为一个编辑框控件指定一个数据变量,这样就可以很方便地取得或设置编辑框控件所代表的数据,如果想对编辑框控件进行控制,则应该为编辑框指定一个CEdit对象,通过CEdit对象,程序员可以控制控件的行为。需要指出的是,不同类的控件对应的数据变量的类型往往是不一样的,而且一个控件对应的数据变量的类型也可能有多种。表5.3说明了控件的数据变量的类型。 

    表5.3 

    控件   数据变量的类型  
    编辑框   CString, int, UINT, long, DWORD, float, double, short, BOOL, COleDateTime, COleCurrency  
    普通检查框   BOOL(真表示被选中,假表示未选中)  
    三态检查框   int(0表示未选中,1表示选中,2表示不确定状态)  
    单选按钮(组中的第一个按钮)   int(0表示选择了组中第一个单选按钮,1表示选择了第二个...,-1表示没有一个被选中)  
    不排序的列表框   CString(为空则表示没有一个列表项被选中),   
    int(0表示选择了第一项,1表示选了第二项,-1表示没有一项被选中)  

    下拉式组合框   CString, int(含义同上)  
    其它列表框和组合框   CString(含义同上)  

    利用ClassWizard可以很方便地为对话框类CRegisterDialog加入成员变量。请读者按下列步骤操作。 

    按Ctrl+W进入ClassWizard。 

    选择ClassWizard上部的Member Variables标签,然后在Class name栏中选择CRegisterDialog。这时,在下面的变量列表中会出现对话框控件的ID,如图5.7所示。 



    图5.7 ClassWizard对话框 

    双击列表中的ID_AGE会弹出Add Member Variable对话框,如图5.8所示。在Member variable name栏中输入m_nAge,在Category栏中选择Value,在Variable type栏中选择UINT。按OK按钮后,数据变量m_nAge就会被加入到变量列表中。 



    图5.8 Add Member Variable对话框 

    仿照第3步和表5.4,为各个控件加入相应的成员变量。 

    将m_nAge的值限制在16到65之间。方法是先选择m_nAge,然后在ClassWizard对话框的左下角输入最大和最小值。m_nAge代表年龄,这里规定被调查的人的年龄应在16岁以上,64岁以下。有了这个限制后,对话框会对输入的年龄值进行有效性检查,若输入的值不在限制范围内,则对话框会提示用户输入有效的值。 

    表5.4 

    控件ID  
    变量类型  
    变量名  

    IDC_AGE  
    UINT  
    m_nAge  

    IDC_INCOME  
    CString  
    m_strIncome  

    IDC_INCOME  
    CListBox  
    m_ctrlIncome  

    IDC_KIND  
    CString  
    m_strKind  

    IDC_MARRIED  
    BOOL  
    m_bMarried  

    IDC_NAME  
    CString  
    m_strName  

    IDC_SEX  
    int  
    m_nSex  

    IDC_UNIT  
    CString  
    m_strUnit  

    IDC_WORK  
    int  
    m_nWork  


    读者会注意到控件IDC_INCOME居然有两个变量,一个是CString型的,一个是CListBox型的,这是完全合法的,不会引起任何冲突。之所以要加入CListBox型的变量,是因为列表框的初始化要通过CListBox对象进行。 

    提示:在ClassWizard中可分别为一个控件指定一个数据变量和一个控件对象,这样做的好处是即能方便地获得数据,又能方便地控制控件。  

    5.3.3对话框的初始化 

    对话框的初始化工作一般在构造函数和OnInitDialog函数中完成。在构造函数中的初始化主要是针对对话框的数据成员。读者可以找到CRegisterDialog的构造函数,如清单5.1所示。 



    清单5.1 CRegisterDialog的构造函数 

    CRegisterDialog::CRegisterDialog(CWnd* pParent /*=NULL*/) 

    : CDialog(CRegisterDialog::IDD, pParent) 

    { 

    //{{AFX_DATA_INIT(CRegisterDialog) 

    m_nAge = 0; 

    m_strIncome = _T(""); 

    m_strKind = _T(""); 

    m_bMarried = FALSE; 

    m_strName = _T(""); 

    m_nSex = -1; 

    m_strUnit = _T(""); 

    m_nWork = -1; 

    //}}AFX_DATA_INIT 

    } 





    可以看出,对数据成员的初始化是由ClassWizard自动完成的。若读者对初值的含义还不太清楚,请参看表5.3。 

    在对话框创建时,会收到WM_INITDIALOG消息,对话框对该消息的处理函数是OnInitDialog。调用OnInitDialog时,对话框已初步创建,对话框的窗口句柄也已有效,但对话框还未被显示出来。因此,可以在OnInitDialog中做一些影响对话框外观的初始化工作。OnInitDialog对对话框的作用与OnCreate对CMainFrame的作用类似。 

    提示:MFC窗口的初始化工作一般在OnCreate成员函数中进行,但对话框的初始化工作最好在OnInitDialog中进行。  

    OnInitDialog是WM_INITDIALOG消息的处理函数,所以要用ClassWizard为RegisteritDialog类增加一个WM_INITDIALOG消息的处理函数,增加的方法是进入ClassWizard后,先选中MessageMaps标签,然后在Class name中选择CRegisterDialog,在Object IDs栏中选择CRegisterDialog,在Messages栏中找到WM_INITDIALOG并双击之,最后按OK按钮退出ClassWizard。 

    请读者按清单5.2修改OnInitDialog函数。 



    清单5.2 OnInitDialog函数 

    BOOL CRegisterDialog::OnInitDialog()  

    { 

    CDialog::OnInitDialog(); 



    // TODO: Add extra initialization here 



    m_ctrlIncome.AddString("500元以下"); 

    m_ctrlIncome.AddString("500-1000元"); 

    m_ctrlIncome.AddString("1000-2000元"); 

    m_ctrlIncome.AddString("2000元以上"); 



    return TRUE; // return TRUE unless you set the focus to a control 

    // EXCEPTION: OCX Property Pages should return FALSE 

    } 

    CRegisterDialog::OnInitDialog()的主要任务是对工资收入列表框的列表项进行初始化。调用CListBox::AddString可将指定的字符串加入到列表框中。由于该列表是不自动排序的,因此AddString将表项加在列表框的末尾。 

    5.3.4对话框的数据交换机制 

    对话框的数据成员变量存储了与控件相对应的数据。数据变量需要和控件交换数据,以完成输入或输出功能。例如,一个编辑框即可以用来输入,也可以用来输出:用作输入时,用户在其中输入了字符后,对应的数据成员应该更新;用作输出时,应及时刷新编辑框的内容以反映相应数据成员的变化。对话框需要一种机制来实现这种数据交换功能,这对对话框来说是至关重要的。 

    MFC提供了类CDataExchange来实现对话框类与控件之间的数据交换(DDX),该类还提供了数据有效机制(DDV)。数据交换和数据有效机制适用于编辑框、检查框、单选按钮、列表框和组合框。 

    数据交换的工作由CDialog::DoDataExchange来完成。读者可以找到CRegisterDialog::DoDataExchange函数,如清单5.3所示。 

    清单5.3 DoDataExchange函数 

    void CRegisterDialog::DoDataExchange(CDataExchange* pDX) 

    { 

    CDialog::DoDataExchange(pDX); 

    //{{AFX_DATA_MAP(CRegisterDialog) 

    DDX_Control(pDX, IDC_INCOME, m_ctrlIncome); 

    DDX_LBString(pDX, IDC_INCOME, m_strIncome); 

    DDX_CBString(pDX, IDC_KIND, m_strKind); 

    DDX_Check(pDX, IDC_MARRIED, m_bMarried); 

    DDX_Text(pDX, IDC_NAME, m_strName); 

    DDX_Radio(pDX, IDC_SEX, m_nSex); 

    DDX_Text(pDX, IDC_UNIT, m_strUnit); 

    DDX_Radio(pDX, IDC_WORK, m_nWork); 

    DDX_Text(pDX, IDC_AGE, m_nAge); 

    DDV_MinMaxUInt(pDX, m_nAge, 16, 65); 

    //}}AFX_DATA_MAP 

    } 

    读者可以看出,该函数中的代码是由ClassWizard自动加入的。DoDataExchange只有一个参数,即一个CDataExchange对象的指针pDX。在该函数中调用了DDX函数来完成数据交换,调用DDV函数来进行数据有效检查。 

    当程序需要交换数据时,不要直接调用DoDataExchange函数,而应该调用CWnd::UpdateData。UpdataData函数内部调用了DoDataExchange。该函数只有一个布尔型参数,它决定了数据传送的方向。调用UpdateData(TRUE)将数据从对话框的控件中传送到对应的数据成员中,调用UpdateData(FALSE)则将数据从数据成员中传送给对应的控件。 

    在缺省的CDialog::OnInitDialog中调用了UpdateData(FALSE),这样,在对话框创建时,数据成员的初值就会反映到相应的控件上。若用户是按了OK(确定)按钮退出对话框,则对话框认为输入有效,就会调用UpdataData(TRUE)将控件中的数据传给数据成员。图5.9描绘了对话框的这种数据交换机制。 



    图5.9 对话框的数据交换 

    5.3.5对话框的运行机制 

    在程序中运行模态对话框有两个步骤: 

    在堆栈上以变量的形式构建一个对话框对象。 

    调用CDialog::DoModal ( )。 



    DoModal负责对模态话框的创建和撤销。在创建对话框时,DoModal的任务包括载入对话框模板资源、调用OnInitDialog初始化对话框和将对话框显示在屏幕上。完成对话框的创建后,DoModal启动一个消息循环,以响应用户的输入。由于该消息循环截获了几乎所有的输入消息,使主消息循环收不到对对话框的输入,致使用户只能与模态对话框进行交互,而其它用户界面对象收不到输入信息。 

    若用户在对话框内点击了ID为IDOK的按钮(通常该按钮的标题是“确定”或“OK”),或按了回车键,则CDialog::OnOK将被调用。OnOK首先调用UpdateData(TRUE)将数据从控件传给对话框成员变量,然后调用CDialog::EndDialog关闭对话框。关闭对话框后,DoModal会返回值IDOK。 

    若用户点击了ID为IDCANCEL的按钮(通常其标题为“取消”或“Cancel”),或按了ESC键,则会导致CDialog::OnCancel的调用。该函数只调用CDialog::EndDialog关闭对话框。关闭对话框后,DoModal会返回值IDCANCEL。 

    程序根据DoModal的返回值是IDOK还是IDCANCEL就可以判断出用户是确定还是取消了对对话框的操作。 

    在弄清了对话框的运行机制后,下面让我们来就可以实现Register程序登录数据的功能。 

    首先,将Register工程的工作区切换至资源视图。打开IDR_MAINFRAME菜单资源,在Edit菜单的底端加入一个名为“登录数据”的新菜单项,并令其ID为ID_EDIT_REGISTER(最好在该项之前加一条分隔线,以便和前面的菜单项分开)。注意不要忘了把菜单资源的语种设置成中文,否则菜单中将显示不出中文来。设置的方法是先在工作区资源视图中选择IDR_MAINFRAME菜单资源,然后按Alt+Enter键,并在弹出的属性对话框中的Language栏中选择Chinese(P.R.C.)。 

    接着,用ClassWizard为该菜单命令创建命令处理函数CRegisterView::OnEditRegister。注意,OnEditRegister是类CRegisterView的成员函数,这是因为CRegisterView要负责打开和关闭登录数据对话框,并将从对话框中输入的数据在视图中输出。 

    然后,请读者在RegisterView.cpp文件的开头加入下面一行 

    #include "RegisterDialog.h" 

    最后,按清单5.4修改程序。 

    清单5.4 OnEditRegister函数 

    void CRegisterView::OnEditRegister()  

    { 

    // TODO: Add your command handler code here 

    CRegisterDialog dlg; 

    if(dlg.DoModal()==IDOK) 

    { 

    CString str; 

    //获取编辑正文 

    GetWindowText(str); 

    //换行 

    str+="/r/n"; 

    str+="姓名:"; 

    str+=dlg.m_strName; 

    str+="/r/n"; 

    str+="性别:"; 

    str+=dlg.m_nSex?"女":"男"; 

    str+="/r/n"; 

    str+="年龄:"; 

    CString str1; 

    //将数据格式输出到字符串对象中 

    str1.Format("%d",dlg.m_nAge); 

    str+=str1; 

    str+="/r/n"; 

    str+="婚否:"; 

    str+=dlg.m_bMarried?"已婚":"未婚"; 

    str+="/r/n"; 

    str+="就业状况:"; 

    str+=dlg.m_nWork?"下岗":"在职"; 

    str+="/r/n"; 

    str+="工作单位:"; 

    str+=dlg.m_strUnit; 

    str+="/r/n"; 

    str+="单位性质:"; 

    str+=dlg.m_strKind; 

    str+="/r/n"; 

    str+="工资收入:"; 

    str+=dlg.m_strIncome; 

    str+="/r/n"; 

    //更新编辑视图中的正文 

    SetWindowText(str); 

    } 

    } 

    在OnEditRegister函数中,首先构建了一个CRegisterDialog对象,然后调用CDialog::DoModal来实现模态对话框。如果DoModal返回IDOK,则说明用户确认了登录数据的操作,程序需要将录入的数据在编辑视图中输出。程序用一个CString对象来作为编辑正文的缓冲区,CString是一个功能强大的字符串类,它的最大特点在于可以存储动态改变大小的字符串,这样,用户不必担心字符串的长度超过缓冲区的大小, 使用十分方便。 

    在输出数据时,程序首先调用CWnd::GetWindowText获得编辑正文,这是一个多行的编辑正文。CWnd::GetWindowText用来获取窗口的标题,若该窗口是一个控件,则获取的是控件内的正文。CRegisterView是CEditView的继承类,而CEditView实际上包含了一个编辑控件,因此在CRegisterView中调用GetWindowText获得的是编辑正文。 

    然后,程序在该编辑正文的末尾加入新的数据。在程序中大量使用了CString类的重载操作符“+=”,该操作符的功能是将操作符右侧字符串添加到操作符左侧的字符串的末尾。注意在多行编辑控件中每行末尾都有一对回车和换行符。在程序中还调用了CString::Format来将数据格式化输出到字符串中,Format的功能与sprintf类似。最后,调用CWnd::SetWindowText来更新编辑视图中的正文。 

    编译并运行Register,打开登录数据对话框,输入一些数据试试。现在,Register已经是一个简易的数据库应用程序了,它可以将与就业情况有关的数据输出到一个编辑视图中。用户可以编辑视图中的正文,并将结果保存在文本文件中。 

    5.3.6处理控件通知消息 

    虽然Register已经可以登录数据了,但读者会很快会发现该程序还有一些不完善的地方: 

    登录完一个人的数据后,对话框就关闭了,若用户有很多人的数据要输入,则必须频繁地打开对话框,很不方便。在登录数据时,应该使对话框一直处于打开状态。 

    登录数据对话框分个人情况和单位情况两组,若被调查人是下岗职工,则不必输入单位情况。程序应该能够对用户的输入及时地作出反应,即当用户选择了“下岗”单选按钮时,应使单位情况组中的控件禁止。一个禁止的控件呈灰色示,并且不能接收用户的输入。 

    要解决上述问题,就必须对控件通知消息进行处理。当控件的状态因为输入等原因而发生变化时,控件会向其父窗口发出控件通知消息。例如,如果用户在登录数据对话框中的某一按钮(包括普通按钮、检查框和单选按钮)上单击鼠标,则该按钮会向对话框发送BN_CLICKED消息。对话框根据按钮的ID激活相应的BN_CLICKED消息处理函数,以对单击按钮这一事件作出反应。通过对按钮的BN_CLICKED消息的处理,我们可以使登录数据对话框的功能达到上述要求。 

    首先,让我们来解决第一个问题。我们的设想是修改原来的“确定(Y)”按钮,使得当用户点击该按钮后,将数据输出到视图中,并且对话框不关闭,以便用户输入下一个数据。请读者按下面几步进行修改。 

    修改登录数据对话框的“确定(Y)”按钮,使该按钮的标题变为“添加(&A)”,ID变为IDC_ADD。这样,当用户点击该按钮后,对话框会收到BN_CLICKED消息。由于这个BN_CLICKED消息对应的按钮ID不是IDOK,不会触发OnOK消息处理函数,因此不会关闭对话框。 

    为按钮IDC_ADD的BN_CLICKED消息创建消息处理函数。创建的方法是进入ClassWizard后,选Message Maps页并在Class name栏中选择CRegisterDialog,然后在Object IDs栏中选择IDC_ADD,在Messages栏中双击BN_CLICKED。在确认使用缺省的消息处理函数名OnAdd后,按回车键退出ClassWizard。 

    OnAdd要向编辑视图输出正文,就必须获得一个指向CRegisterView对象的指针以访问该对象。为此,请在CRegisterDialog类的说明中加入下面一行
    Cwnd* m_pParent;
    注意不要加在AFX注释对中。 

    为实现IDC_ADD按钮的功能,请按清单5.5和清单5.6修改程序。主要的改动是把原来由CRegiserView::OnEditRegister完成的在视图中输出数据的任务交给CRegisterDialog::OnAdd来完成。 

    清单5.5 CRegisterView::OnEditRegister函数 

    void CRegisterView::OnEditRegister()  

    { 

    // TODO: Add your command handler code here 

    CRegisterDialog dlg(this); 

    dlg.DoModal(); 

    } 

    清单5.6 CRegisterDialog类的部分源代码 

    CRegisterDialog::CRegisterDialog(CWnd* pParent /*=NULL*/) 

    : CDialog(CRegisterDialog::IDD, pParent) 

    { 

    //{{AFX_DATA_INIT(CRegisterDialog) 

    . . . . . . 

    //}}AFX_DATA_INIT 

    m_pParent=pParent; 

    } 

    void CRegisterDialog::OnAdd()  

    { 

    // TODO: Add your control notification handler code here 

    //更新数据 

    UpdateData(TRUE); 

    //检查数据是否有效 

    if(m_strName=="" || m_nSex<0 || m_nWork<0 || m_strUnit==""  

    || m_strKind=="" || m_strIncome=="") 

    { 

    AfxMessageBox("请输入有效数据"); 

    return; 

    } 

    CString str; 

    //获取编辑正文 

    m_pParent->GetWindowText(str); 

    //换行 

    str+="/r/n"; 

    str+="姓名:"; 

    str+=m_strName; 

    str+="/r/n"; 

    str+="性别:"; 

    str+=m_nSex?"女":"男"; 

    str+="/r/n"; 

    str+="年龄:"; 

    CString str1; 

    //将数据格式输出到字符串对象中 

    str1.Format("%d",m_nAge); 

    str+=str1; 

    str+="/r/n"; 

    str+="婚否:"; 

    str+=m_bMarried?"已婚":"未婚"; 

    str+="/r/n"; 

    str+="就业状况:"; 

    str+=m_nWork?"下岗":"在职"; 

    str+="/r/n"; 

    str+="工作单位:"; 

    str+=m_strUnit; 

    str+="/r/n"; 

    str+="单位性质:"; 

    str+=m_strKind; 

    str+="/r/n"; 

    str+="工资收入:"; 

    str+=m_strIncome; 

    str+="/r/n"; 

    //更新编辑视图中的正文 

    m_pParent->SetWindowText(str); 

    } 

    CRegisterDialog的构造函数有一个参数pParent,该参数是一个指向CWnd对象的指针,用于指定对话框的父窗口或拥有者窗口。在CRegisterView:: OnEditRegister函数中,在构建CRegisterDialog对象时指定了this参数,this指针指向CRegisterView对象本身。这样在调用CRegisterDialog的构造函数时,this指针值被赋给了CRegisterDialog的成员m_pParent。OnAdd函数可利用m_pParent来访问对话框的拥有者即CRegisterView对象。 

    提示:术语父窗口(Parent)是相对于子窗口而言。若某一个窗口拥有一个子窗口(Child),则该窗口就被称为子窗口的父窗口。子窗口就是具有WS_CHILD风格的窗口,子窗口依赖于父窗口且完全被限制在父窗口内部。拥有者窗口(owner)相对于被拥有者窗口而言。若某一个窗口拥有一个非子窗口,则该窗口被称为拥有者窗口。被拥有窗口(owned)不具有WS_CHILD风格,可在屏幕上任意移动。  

    当用户用鼠标点击IDC_ADD按钮时,该按钮的BN_CLICKED消息处理函数CRegisterDialog::OnAdd将被调用。在OnAdd中,首先调用了UpdateData(TRUE)以把数据从控件传给对话框的数据成员变量。然后,程序要对数据的有效性进行检查,如果输入的数据不完全有效,则会显示一个消息对话框,提示用户输入有效的数据。接下来进行的工作是在视图中输出数据,这部分代码与清单5.4类似,读者应该比较熟悉了。 

    完成上述工作后,登录数据对话框就变得较为实用了。打开对话框后,用户可以方便地输入多人的数据,只有按了取消按钮后,对话框才会关闭。 

    接下来让我们来解决第二个问题。解决该问题的关键在于当用户点击“在职”或“下岗”单选按钮时,程序要对收到的BN_CLICKED消息作出响应。有些读者可能会想到为两个单选按钮分别创建BN_CLICKED消息处理函数,这在只有两个单选按钮的情况下是可以的,但如果一组内有多个单选按钮,则分别创建消息处理函数就比较麻烦了。利用MFC提供的消息映射宏ON_CONTROL_RANGE可以避免这种麻烦,该映射宏把多个ID连续的控件发出的消息映射到同一个处理函数上。这样,我们只要编写一个消息处理函数,就可以对“在职”和“下岗”两个单选按钮的BN_CLICKED消息作出响应。ClassWizard不支持ON_CONTROL_RANGE宏,所以我们必须手工创建单选按钮的消息映射和消息处理函数。 

    首先,在CRegisterDialog类的头文件中加入消息处理函数的声明,该函数名为OnWorkClicked,如清单5.7所示。 

    清单5.7 BN_CLICKED消息处理函数OnWorkClicked的声明 

    . . . . . .  

    protected: 

    void OnWorkClicked(UINT nCmdID); 

    // Generated message map functions 

    //{{AFX_MSG(CRegisterDialog) 

    virtual BOOL OnInitDialog(); 

    afx_msg void OnAdd(); 

    //}}AFX_MSG 

    . . . . . . 

    然后,在CRegisterDialog类的消息映射中加入ON_CONTROL_RANGE映射,如清单5.8所示。ON_CONTROL_RANGE映射的形式是ON_CONTROL_RANGE 

    清单5.8 在CRegisterDialog类的消息映射中加入ON_CONTROL_RANGE映射 

    BEGIN_MESSAGE_MAP(CRegisterDialog, CDialog) 

    //{{AFX_MSG_MAP(CRegisterDialog) 

    ON_BN_CLICKED(IDC_ADD, OnAdd) 

    //}}AFX_MSG_MAP 

    ON_CONTROL_RANGE(BN_CLICKED, IDC_WORK, IDC_WORK1, OnWorkClicked) 

    END_MESSAGE_MAP() 

    ON_CONTROL_RANGE消息映射宏的第一个参数是控件消息码,第二和第三个参数分别指明了一组连续的控件ID中的头一个和最后一个ID,最后一个参数是消息处理函数名。如果读者是按表5.2的顺序放置控件的则IDC_WORK和IDC_WORK1应该是连续的。这样,无论用户是在IDC_WORK还是在IDC_WORK1单选按钮上单击,都会调用OnWorkClicked消息处理函数。 

    提示:如果不能确定两个ID是否是连续的,请用File->Open命令打开resource.h文件,在该文件中有对控件ID值的定义。如果发现两个ID是不连续的,读者可以改变对ID的定义值使之连续,但要注意改动后的值不要与别的ID值发生冲突。 

    最后,在CRegisterDialog类所在CPP文件的最后插入消息处理函数CRegisterDialog::OnWorkClicked,如清单5.9所示。 

    清单5.9 CRegisterDialog::OnWorkClicked消息处理函数 

    void CRegisterDialog::OnWorkClicked(UINT nCmdID)  

    { 

    //判断“在职”单选按钮是否被选中 

    if(IsDlgButtonChecked(IDC_WORK)) 

    { 

    //使控件允许 

    GetDlgItem(IDC_UNIT)->EnableWindow(TRUE); 

    GetDlgItem(IDC_KIND)->EnableWindow(TRUE); 

    GetDlgItem(IDC_INCOME)->EnableWindow(TRUE); 

    } 

    else 

    { 

    //清除编辑框的内容并使之禁止 

    GetDlgItem(IDC_UNIT)->SetWindowText(""); 

    GetDlgItem(IDC_UNIT)->EnableWindow(FALSE); 

    //使组合框处于未选择状态并使之禁止 

    CComboBox *pComboBox=(CComboBox *)GetDlgItem(IDC_KIND); 

    pComboBox->SetCurSel(-1); 

    pComboBox->EnableWindow(FALSE); 

    //使列表框处于未选择状态并使之禁止 

    m_ctrlIncome.SetCurSel(-1); 

    m_ctrlIncome.EnableWindow(FALSE); 

    } 

    } 

    OnWorkClicked函数判断“在职”单选按钮是否被选中。若该按钮被选中,则使单位情况组中的控件允许,若该按钮未被选中,则说明“下岗”按钮被选中,这时应使控件禁止,清除编辑框中的正文, 并且使组合框和列表框处于未选中状态。 

    在OnWorkClicked函数中主要调用了下列函数: 

    CWnd::IsDlgButtonChecked函数,用来判断单选按钮或检查框是否被选择,该函数的声明为
    UINT IsDlgButtonChecked(int nIDButton) const;
    参数nIDButton为按钮的ID。若按钮被选择,则函数返回1,否则返回0,若按钮处于不确定状态,则返回值为2。 

    CWnd::GetDlgItem函数,用来获得指向某一控件的指针,该函数的声明为
    CWnd* GetDlgItem(int nID) const;
    参数nID为控件的ID。该函数返回一个指定控件的CWnd对象指针,通过该指针,程序可以对控件进行控制。 

    CWnd::EnableWindow函数,该函数使窗口允许或禁止,禁止的窗口呈灰色显示,不能接收键盘和鼠标的输入。该函数的声明是
    BOOL EnableWindow( BOOL bEnable = TRUE );
    若参数bEnable的值为TRUE,则窗口被允许,若bEnable的值为FALSE,则窗口被禁止。 

    CListBox::SetCurSel和CComboBox::SetCurSel函数功能类似,用来使列表中的某一项被选中,选中的项呈高亮度显示。函数的声明是
    int SetCurSel(int nSelect);
    参数nSelect指定了新选项的索引,第一项的索引值为0,若nSelect的值为-1,那么函数将清除以前的选择,使列表处于未选择状态。 

    有时,需要将GetDlgItem返回的CWnd指针强制转换成控件对象的指针,以便调用控件对象专有的成员函数对控件进行控制。例如,在程序中GetDlgItem(IDC_KIND)返回的指针被强制转换成CComboBox类型,只有这样,才能调用CComboBox::SetCurSel成员函数。 

    为了对控件进行查询和控制,在程序中采用了两种访问控件的方法。一种方法是直接利用ClassWizard提供的控件对象,例如m_ctrlIncome列表框对象。另一种方法是利用CWnd类提供的一组管理对话框控件的成员函数,例如程序中用到的GetDlgItem和IsDlgButtonChecked。这两种方法是在对话框内访问控件的常用方法,读者都应该掌握。表5.5列出了管理对话框控件的Cwnd成员函数。 



    表5.5 用来管理对话框控件的CWnd成员函数 

    函数名  
    功能  

    CheckDlgButton  
    选中或不选中按钮控件。  

    CheckRadioButton  
    选择一个指定的单选按钮并使同组内的其它单选按钮不被选择。  

    DlgDirList  
    往一个列表框中添加文件、目录或驱动器的列表。  

    DlgDirListComboBox  
    往一个组合框中的列表框内添加文件、目录或驱动器的列表。  

    DlgDirSelect  
    从一个列表框中获得当前选择的文件、目录或驱动器。  

    DlgDirSelectBomboBox  
    从一个组合框中获得当前选择的文件、目录或驱动器。  

    GetCheckedRadioButton  
    返回指定的单选按钮组中被选择的单选按钮的ID。  

    GetDlgItem  
    返回一个指向一给定的控件的临时对象的指针。  

    GetDlgItemInt  
    返回在一个指定的控件中由正文表示的数字值。  

    GetDlgItemText  
    获得在一个控件内显示的正文。  

    GetNextDlgGroupItem  
    返回一个指向一组控件内的下一个或上一个控件的临时对象的指针。  

    GetNextDlgTabItem  
    返回下一个tab顺序的控件的临时对象的指针。  

    IsDlgButtonChecked  
    返回一个按钮控件的状态。  

    SendDlgItemMessage  
    把一个消息传送给一个控件。  

    SetDlgItemInt  
    将一个整数转换为正文,并将此正文赋给控件。  

    SetDlgItemText  
    设置一个控件显示的正文。  


    编译并运行Register看看,现在的登录数据对话框已经比较令人满意了。 

    5.4 非模态对话框

    5.4.1 非模态对话框的特点 

    与模态对话框不同,非模态对话框不垄断用户的输入,用户打开非模态对话框后,仍然可以与其它界面进行交互。 

    非模态对话框的设计与模态对话框基本类似,也包括设计对话框模板和设计CDialog类的派生类两部分。但是,在对话框的创建和删除过程中,非模态对话框与模态对话框相比有下列不同之处: 

    非模态对话框的模板必须具有Visible风格,否则对话框将不可见,而模态对话框则无需设置该项风格。更保险的办法是调用CWnd::ShowWindow(SW_SHOW)来显示对话框,而不管对话框是否具有Visible风格。 

    非模态对话框对象是用new操作符在堆中动态创建的,而不是以成员变量的形式嵌入到别的对象中或以局部变量的形式构建在堆栈上。通常应在对话框的拥有者窗口类内声明一个指向对话框类的指针成员变量,通过该指针可访问对话框对象。 

    通过调用CDialog::Create函数来启动对话框,而不是CDialog::DoModal,这是模态对话框的关键所在。由于Create函数不会启动新的消息循环,对话框与应用程序共用同一个消息循环,这样对话框就不会垄断用户的输入。Create在显示了对话框后就立即返回,而DoModal是在对话框被关闭后才返回的。众所周知,在MFC程序中,窗口对象的生存期应长于对应的窗口,也就是说,不能在未关闭屏幕上窗口的情况下先把对应的窗口对象删除掉。由于在Create返回后,不能确定对话框是否已关闭,这样也就无法确定对话框对象的生存期,因此只好在堆中构建对话框对象,而不能以局部变量的形式来构建之。 

    必须调用CWnd::DestroyWindow而不是CDialog::EndDialog来关闭非模态对话框。调用CWnd::DestroyWindow是直接删除窗口的一般方法。由于缺省的CDialog::OnOK和CDialog::OnCancel函数均调用EndDialog,故程序员必须编写自己的OnOK和OnCancel函数并且在函数中调用DestroyWindow来关闭对话框。 

    因为是用new操作符构建非模态对话框对象,因此必须在对话框关闭后,用delete操作符删除对话框对象。在屏幕上一个窗口被删除后,框架会调用CWnd::PostNcDestroy,这是一个虚拟函数,程序可以在该函数中完成删除窗口对象的工作,具体代码如下
    void CModelessDialog::PostNcDestroy
    {
    delete this; //删除对象本身
    }
    这样,在删除屏幕上的对话框后,对话框对象将被自动删除。拥有者对象就不必显式的调用delete来删除对话框对象了。 

    必须有一个标志表明非模态对话框是否是打开的。这样做的原因是用户有可能在打开一个模态对话框的情况下,又一次选择打开命令。程序根据标志来决定是打开一个新的对话框,还是仅仅把原来打开的对话框激活。通常可以用拥有者窗口中的指向对话框对象的指针作为这种标志,当对话框关闭时,给该指针赋NULL值,以表明对话框对象已不存在了。 

    提示:在C++编程中,判断一个位于堆中的对象是否存在的常用方法是判断指向该对象的指针是否为空。这种机制要求程序员将指向该对象的指针初始化为NULL值,在创建对象时将返回的地址赋给该指针,而在删除对象时将该指针置成NULL值。  

    根据上面的分析,我们很容易把Register程序中的登录数据对话框改成非模态对话框。这样做的好处在于如果用户在输入数据时发现编辑视图中有错误的数据,那么不必关闭对话框,就可以在编辑视图中进行修改。 

    请读者按下面几步操作: 

    在登录数据对话框模板的属性对话框的More Styles页中选择Visible项。 

    在RegisterView.h头文件的CRegisterView类的定义中加入
    public:
    CRegisterDialog* m_pRegisterDlg; 

    在RegisterView.h头文件的头部加入对CRegisterDialog类的声明
    class CRegisterDialog;
    加入该行的原因是在CRegisterView类中有一个CRegisterDialog类型的指针,因此必须保证CRegisterDialog类的声明出现在CRegisterView之前,否则编译时将会出错。解决这个问题有两种办法,一种办法是保证在#include “RegisterView.h”语句之前有#include “RegisterDialog.h”语句,这种办法造成了一种依赖关系,增加了编译负担,不是很好;另一种办法是在CRegisterView类的声明之前加上一个对CRegisterDialog的声明来暂时“蒙蔽”编译器,这样在有#include “RegisterView.h”语句的模块中,除非要用到CRegisterDialog类,否则不用加入#include “RegisterDialog.h”语句。 

    在RegisterDialog.cpp文件的头部的#include语句区的末尾添加下面两行
    #include "RegisterDoc.h"
    #include "RegisterView.h" 

    利用ClassWizard为CRegisterDialog类加入OnCancel和PostNcDestroy成员函数。加入的方法是进入ClassWizard后选择Message Maps页,并在Class name栏中选择CRegisterDialog。然后,在Object IDs栏中选择IDCANCEL后,在Messages栏中双击BN_CLICKED,这就创建了OnCancel。要创建PostNcDestroy,先在Object IDs栏中选择CRegisterDialog,再在Messages栏中双击PostNcDestroy即可。 

    分别按清单5.10和5.11,对CRegisterView类和CRegisterDialog类进行修改。 

    清单5.10 CRegisterView类的部分代码 

    CRegisterView::CRegisterView() 

    { 

    // TODO: add construction code here 



    m_pRegisterDlg=NULL; //指针初始化为NULL 

    } 



    void CRegisterView::OnEditRegister()  

    { 

    // TODO: Add your command handler code here 





    if(m_pRegisterDlg) 

    m_pRegisterDlg->SetActiveWindow(); //激活对话框 

    else 

    { 

    //创建非模态对话框 

    m_pRegisterDlg=new CRegisterDialog(this); 

    m_pRegisterDlg->Create(IDD_REGISTER,this); 

    } 

    }
    清单5.11 CRegisterDialog的部分代码 

    void CRegisterDialog::PostNcDestroy()  

    { 

    // TODO: Add your specialized code here and/or call the base class 

    delete this; //删除对话框对象 

    } 

    void CRegisterDialog::OnCancel()  

    { 

    // TODO: Add extra cleanup here 

    ((CRegisterView*)m_pParent)->m_pRegisterDlg=NULL; 

    DestroyWindow(); //删除对话框  

    } 

    CRegisterView::OnEditRegister函数判断登录数据对话框是否已打开,若是,就激活对话框,否则,就创建该对话框。该函数中主要调用了下列函数: 

    调用CWnd::SetActiveWindow激活对话框,该函数的声明为
    CWnd* SetActiveWindow( );
    该函数使本窗口成为活动窗口,并返回原来活动的窗口。 

    调用CDialog::Create来显示对话框,该函数的声明为
    BOOL Create( UINT nIDTemplate, CWnd* pParentWnd = NULL );
    参数nIDTemplate是对话框模板的ID。pParentWnd指定了对话框的父窗口或拥有者。 

    当用户在登录数据对话框中点击“取消”按钮后,CRegisterDialog::OnCancel将被调用,在该函数中调用CWnd::DestroyWindow来关闭对话框,并且将CRegisterView的成员m_pRegisterDlg置为NULL以表明对话框被关闭了。调用DestroyWindow导致了对CRegisterDialog::PostNcDestroy的调用,在该函数中用delete操作符删除了CRegisterDialog对象本身。 

    编译并运行Register,现在登录数据对话框已经变成一个非模态对话框了。 

    5.4.2 窗口对象的自动清除 

    一个MFC窗口对象包括两方面的内容:一是窗口对象封装的窗口,即存放在m_hWnd成员中的HWND(窗口句柄),二是窗口对象本身是一个C++对象。要删除一个MFC窗口对象,应该先删除窗口对象封装的窗口,然后删除窗口对象本身。 

    删除窗口最直接方法是调用CWnd::DestroyWindow或::DestroyWindow,前者封装了后者的功能。前者不仅会调用后者,而且会使成员m_hWnd保存的HWND无效(NULL)。如果DestroyWindow删除的是一个父窗口或拥有者窗口,则该函数会先自动删除所有的子窗口或被拥有者,然后再删除父窗口或拥有者。在一般情况下,在程序中不必直接调用DestroyWindow来删除窗口,因为MFC会自动调用DestroyWindow来删除窗口。例如,当用户退出应用程序时,会产生WM_CLOSE消息,该消息会导致MFC自动调用CWnd::DestroyWindow来删除主框架窗口,当用户在对话框内按了OK或Cancel按钮时,MFC会自动调用CWnd::DestroyWindow来删除对话框及其控件。 

    窗口对象本身的删除则根据对象创建方式的不同,分为两种情况。在MFC编程中,会使用大量的窗口对象,有些窗口对象以变量的形式嵌入在别的对象内或以局部变量的形式创建在堆栈上,有些则用new操作符创建在堆中。对于一个以变量形式创建的窗口对象,程序员不必关心它的删除问题,因为该对象的生命期总是有限的,若该对象是某个对象的成员变量,它会随着父对象的消失而消失,若该对象是一个局部变量,那么它会在函数返回时被清除。 

    对于一个在堆中动态创建的窗口对象,其生命期却是任意长的。初学者在学习C++编程时,对new操作符的使用往往不太踏实,因为用new在堆中创建对象,就不能忘记用delete删除对象。读者在学习MFC的例程时,可能会产生这样的疑问,为什么有些程序用new创建了一个窗口对象,却未显式的用delete来删除它呢?问题的答案就是有些MFC窗口对象具有自动清除的功能。 

    如前面讲述非模态对话框时所提到的,当调用CWnd::DestroyWindow或::DestroyWindow删除一个窗口时,被删除窗口的PostNcDestroy成员函数会被调用。缺省的PostNcDestroy什么也不干,但有些MFC窗口类会覆盖该函数并在新版本的PostNcDestroy中调用delete this来删除对象,从而具有了自动清除的功能。此类窗口对象通常是用new操作符创建在堆中的,但程序员不必操心用delete操作符去删除它们,因为一旦调用DestroyWindow删除窗口,对应的窗口对象也会紧接着被删除。 

    不具有自动清除功能的窗口类如下所示。这些窗口对象通常是以变量的形式创建的,无需自动清除功能。 

    所有标准的Windows控件类。 

    从CWnd类直接派生出来的子窗口对象(如用户定制的控件)。 

    切分窗口类CSplitterWnd。 

    缺省的控制条类(包括工具条、状态条和对话条)。 

    模态对话框类。 

    具有自动清除功能的窗口类如下所示,这些窗口对象通常是在堆中创建的。 

    主框架窗口类(直接或间接从CFrameWnd类派生)。 

    视图类(直接或间接从CView类派生)。 



    读者在设计自己的派生窗口类时,可根据窗口对象的创建方法来决定是否将窗口类设计成可以自动清除的。例如,对于一个非模态对话框来说,其对象是创建在堆中的,因此应该具有自动清除功能。 

    综上所述,对于MFC窗口类及其派生类来说,在程序中一般不必显式删除窗口对象。也就是说,既不必调用DestroyWindow来删除窗口对象封装的窗口,也不必显式地用delete操作符来删除窗口对象本身。只要保证非自动清除的窗口对象是以变量的形式创建的,自动清除的窗口对象是在堆中创建的,MFC的运行机制就可以保证窗口对象的彻底删除。 

    如果需要手工删除窗口对象,则应该先调用相应的函数(如CWnd::DestroyWindow)删除窗口,然后再删除窗口对象.对于以变量形式创建的窗口对象,窗口对象的删除是框架自动完成的.对于在堆中动态创建了的非自动清除的窗口对象,必须在窗口被删除后,显式地调用delete来删除对象(一般在拥有者或父窗口的析构函数中进行).对于具有自动清除功能的窗口对象,只需调用CWnd::DestroyWindow即可删除窗口和窗口对象。注意,对于在堆中创建的窗口对象,不要在窗口还未关闭的情况下就用delete操作符来删除窗口对象. 

    提示:在非模态对话框的OnCancel函数中可以不调用CWnd::DestroyWindow,取而代之的是调用CWnd::ShowWindow(SW_HIDE)来隐藏对话框.在下次打开对话框时就不必调用Create了,只需调用CWnd::ShowWindow(SW_SHOW)来显示对话框.这样做的好处在于对话框中的数据可以保存下来,供以后使用.由于拥有者窗口在被关闭时会调用DestroyWindow删除每一个所属窗口,故只要非模态对话框是自动清除的,程序员就不必担心对话框对象的删除问题.  

    5.5 标签式对话框

    在设计较为复杂的对话框时,常常会遇到这种情况:对某一事物的设置或选项需要用到大量的控件,以至于一个对话框放不下,而这些控件描述的是类似的属性,不能分开。用普通的对话框技术,这一问题很难解决。 

    MFC提供了对标签式对话框的支持,可以很好的解决上述问题。标签式对话框实际上是一个包含了多个子对话框的对话框,这些子对话框通常被称为页(Page)。每次只有一个页是可见的,在对话框的顶端有一行标签,用户通过单击这些标签可切换到不同的页。显然,标签式对话框可以容纳大量的控件。在象Word和Developer Studio这样复杂的软件中,用户会接触到较多的标签式对话框,一个典型的标签式对话框如图5.10所示。 



    图5.10 典型的标签式对话框 

    5.5.1 标签式对话框的创建 

    为了支持标签式对话框,MFC提供了CPropertySheet类和CPropertyPage类。前者代表对话框的框架,后者代表对话框中的某一页。CPropertyPage是CDialog类的派生类,而CPropertySheet是CWnd类的派生类。虽然CPropertySheet不是CDialog类的派生类,但使用CPropertySheet对象的方法与使用CDialog对象是类似的。标签式对话框是一种特殊的对话框,因此,和普通对话框相比,它的设计与实现既有许多相似之处,又有一些不同的特点。 

    创建一个标签式对话框一般包括以下几个步骤: 

    分别为各个页创建对话框模板,去掉缺省的OK和Cancel按钮。每页的模板最好具有相同的尺寸,如果尺寸不统一,则框架将根据最大的页来确定标签对话框的大小。在创建模板时,需要在模板属性对话框中指定下列属性: 

    指定标题(Caption)的内容。标题的内容将显示在该页对应的标签中。 

    选择TitleBar、Child、ThinBorder和Disable属性。 

    根据各个页的模板,用ClassWizard分别为每个页创建CPropertyPage类的派生类。这一过程与创建普通对话框类的过程类似,不同的是在创建新类对话框中应在Base class一栏中选择CPropertyPage而不是CDialog。 

    用ClassWizard为每页加入与控件对应的成员变量,这个过程与为普通对话框类加入成员变量类似。 

    程序员可直接使用CPropertySheet类,也可以从该类派生一个新类。除非要创建一个非模态对话框,或要在框架对话框中加入控件,否则没有必要派生一个新类。如果直接使用CPropertySheet类,则一个典型的标签式对话框的创建代码如清单5.12所示,该段代码也演示了标签式对话框与外界的数据交换。这些代码通常是放在显示对话框的命令处理函数中。可以看出,对话框框架的创建过程及对话框与外界的数据交换机制与普通对话框是一样的,不同之处是还需将页对象加入到CPropertySheet对象中。如果要创建的是模态对话框,应调用CPropertySheet::DoModal,如果想创建非模态对话框,则应该调用CPropertySheet::Create。 

    若从CPropertySheet类派生了一个新类,则应该将所有的页对象以成员变量的形式嵌入到派生类中,并在派生类的构造函数中调用CPropertySheet::AddPage函数来把各个页添加到对话框中。这样,在创建标签式对话框时就不用做添加页的工作了。 

    清单5.12 典型的标签式对话框创建代码 

    void CMyView::DoModalPropertySheet() 

    { 

    CPropertySheet propsheet; 

    CMyFirstPage pageFirst; // derived from CPropertyPage 

    CMySecondPage pageSecond; // derived from CPropertyPage 



    // Move member data from the view (or from the currently 

    // selected object in the view, for example). 

    pageFirst.m_nMember1 = m_nMember1;  

    pageFirst.m_nMember2 = m_nMember2; 



    pageSecond.m_strMember3 = m_strMember3; 

    pageSecond.m_strMember4 = m_strMember4; 



    propsheet.AddPage(&pageFirst); 

    propsheet.AddPage(&pageSecond); 



    if (propsheet.DoModal() == IDOK) 

    { 

    m_nMember1 = pageFirst.m_nMember1; 

    m_nMember2 = pageFirst.m_nMember2; 

    m_strMember3 = pageSecond.m_strMember3; 

    m_strMember4 = pageSecond.m_strMember4;  

    . . .  

    } 

    } 

    .5.2 标签式对话框的运行机制 

    标签式对话框的初始化包括框架对话框的初始化和页的初始化。页的初始化工作可在OnInitDialog函数中进行,而框架对话框的初始化应该在OnCreate函数中完成。 

    根据CPropertySheet::DoModal返回的是IDOK还是IDCANCEL,程序可判断出关闭对话框时按的是OK还是Cancel按钮,这与普通对话框是一样的。 

    如果标签式对话框是模态对话框,在其底部会有三个按钮,依次为OK、Cancel和Apply(应用)按钮,如果对话框是非模态的,则没有这些按钮。OK和Cancel按钮的意义与普通对话框没什么两样,Apply按钮则是标签对话框所特有的。普通的模态对话框只有在用户按下了OK按钮返回后,对话框的设置才能生效,而设计Apply按钮的意图是让用户能在不关闭对话框的情况下使对话框中的设置生效。由此可见,Apply的作用与前面例子中登录数据的“添加”按钮类似,用户不必退出对话框,就可以反复进行设置,这在某些应用场合下是很有用的。 

    为了对上述三个按钮作出响应,CPropertyPage类提供了OnOK、OnCancel和OnApply函数,用户可覆盖这三个函数以完成所需的工作。需要指出的是这三个函数并不是直接响应按钮的BN_CLICKED消息的,但在按钮按下后它们会被间接调用。这些函数的说明如下: 

    virtual void OnOK( );
    在按下OK或Apply按钮后,该函数将被调用。缺省的OnOK函数几乎什么也不干,象数据交换和关闭对话框这样的工作是在别的地方完成的,这与普通对话框的OnOK函数是不同的。 

    virtual void OnCancel( );
    在按下Cancel按钮后,该函数将被调用。缺省的OnCancel函数也是几乎什么都不干。 

    virtual BOOL OnApply( );
    在按下OK或Apply按钮后,该函数将被调用。缺省的OnApply会调用OnOK函数。函数的返回值如果是TRUE,则对话框中的设置将生效,否则无效。 

    按理说,CPropertySheet类也应该提供上述函数,特别是OnApply。但奇怪的是,MFC并未考虑CPropertySheet类的按钮响应问题。读者不要指望能通过ClassWizard来自动创建按钮的BN_CLICKED消息处理函数,如果需要用到这类函数,那么只好手工创建了。 

    下列几个CPropertyPage类的成员函数也与标签对话框的运行机制相关。 

    void SetModified( BOOL bChanged = TRUE );
    该函数用来设置修改标志。若参数bChanged为TRUE,则表明对话框中的设置已改动,否则说明设置未改动。该函数的一个主要用途是允许或禁止Apply按钮。在缺省情况下,Apply按钮是禁止的。只要一调用SetModified(TRUE),Apply按钮就被允许,而调用SetModified(FALSE)并不一定能使Apply按钮禁止,只有在所有被标为改动过的页都调用了SetModified(FALSE)后,Apply按钮才会被禁止。另外,该函数对OnApply的调用也有影响,当Apply按钮被按下后,只有那些被标为改动过的页的OnApply函数才会被调用。在调用该函数之前,程序需要判断页中的内容是否已被修改,可以通过处理诸如BN_CLICKED、EN_CHANG这样的控件通知消息来感知页的内容的改变。 

    virtual BOOL OnSetActive( );
    当页被激活或被创建时,都会调用该函数。该函数的缺省行为是若页还未创建,就创建之,若页已经创建,则将其激活,并调用UpdateData(FALSE)更新控件。用户可覆盖该函数完成一些刷新方面的工作。 

    virtual BOOL OnKillActive( );
    当原来可见的页被覆盖或被删除时,都会调用该函数。该函数的缺省行为是调用UpdateData(TRUE)更新数据。用户可覆盖该函数完成一些特殊数据的有效性检查工作。 

    需要说明的是,标签对话框中的所有页不一定都会被创建。实际上,那些从未打开过的页及其控件是不会被创建的。因此,在CPropertyPage类的派生类中,只有在确定了页已存在后,才能调用与对话框及控件相关的函数(如UpdateData)。如果收到控件通知消息,或OnSetActive函数被调用,则说明页已经存在。正是由于上述原因,使得标签式对话框的内部数据交换只能在OnSetActive和OnKillActive函数中进行。 

    5.5.3 标签式对话框的具体实例 

    通过上面的分析,读者对标签式对话框已经比较了解了。现在,让我们在前面做过的Register程序中加入一个标签式对话框来试验一下其功能。 

    在Register程序的登录数据对话框中有“个人情况”和“单位情况”两组控件,显然,我们可以创建一个标签式对话框并把两组控件分别放到两个页中。为了简单起见,我们仅要求输入姓名和单位名,简化后的标签式对话框如图5.11所示。 



    图5.11 简化后的标签式对话框 



    通过对标签式对话框的分析,读者已经知道CPropertySheet类未对Apply按钮的控件通知消息进行处理,这是一个不足之处。Register的新版本将向读者演示如何在CPropertySheet类的派生类中手工加入Apply按钮的BN_CLICKED消息处理函数。另外,新版本还演示了对话框与外部对象交流的一种较好办法,即通过发送用户定义消息来向外部对象传递信息。在登录数据对话框中,与外界交流的方法是在对话框内部直接访问派生的视图对象,这样做的优点是方便快捷,缺点则是对外界依赖较大,不利于移植。而用发送用户定义消息的方法则可以避免这个缺点。 

    具体工作请按下面几步进行: 

    在菜单资源中的Edit菜单的“登录数据...”项的后面插入一个名为“标签式对话框...”的菜单项,并指定其ID为ID_EDIT_PROPDLG。然后用ClassWizard,在CRegisterView类内为该菜单命令创建命令处理函数OnEditPropdlg,该函数将用来显示标签式对话框。 

    为标签式对话框的第一页创建对话框模板。去掉缺省的OK和Cancel按钮。注意应选择中文语种和宋体字体。在属性对话框中,指定对话框的ID为IDD_PERSONAL,标题为“个人情况”,在Styles页中,选中TitleBar项,并在Style栏中选择Child,在Border栏中选择ThinBorder。在More Styles页中,选中Disable。然后,在模板中加入控件,如图5.11和表5.6所示。 



    表5.6 

    控件类型  
    控件ID  
    控件标题  

    静态正文  
    缺省  
    姓名:  

    编辑框  
    IDC_NAME  
     





    用ClassWizard为模板IDD_PERSONAL创建CPropertyPage类的派生类,类名为CPersonalPage。在该类中为控件IDC_NAME加入对应的成员变量,变量名为m_strName,类型为CString。为控件IDC_NAME加入EN_CHANGE消息处理函数OnChangeName,当编辑框的内容被改变时,控件会向对话框发出EN_CHANGE消息。在OnChangeName中,应该使Apply按钮允许。 

    仿照步2,为标签式对话框的第二页创建对话框模板。指定其ID为IDD_UNIT,标题为“单位情况”。在模板中加入的控件如图5.11和表5.7所示。 



    表5.7 

    控件类型  
    控件ID  
    控件标题  

    静态正文  
    缺省  
    工作单位:  

    编辑框  
    IDC_UNIT  
     

    用ClassWizard为模板IDD_UNIT创建CPropertyPage类的派生类,类名为CUnitPage。在该类中为控件IDC_UNIT加入对应的成员变量,变量名为m_strUnit,类型为CString。为控件IDC_UNIT加入EN_CHANGE消息处理函数OnChangeUnit。 

    用ClassWizard创建一个CPropertySheet的派生类,类名为CRegisterSheet。 

    在CRegisterApp类的头文件的开头加入下面一行
    #define WM_USER_OUTPUT (WM_USER+200)
    WM_USER_OUTPUT不是标准的Windows消息,而是一个用户定义消息。在本例中,当标签式对话框的Apply按钮被按下后,程序会向编辑视图发送该消息,编辑视图对应的消息处理函数应该输出对话框的数据。用户定义消息的编码范围是WM_USER—0x7FFF。 

    请读者按清单5.13、5.14、5.15修改程序,限于篇幅,这里仅列出了需要修改的部分源代码。 



    清单5.13 CPersonalPage类和CUnitPage类的部分代码 

    void CPersonalPage::OnChangeName()  

    { 

    // TODO: Add your control notification handler code here 



    SetModified(TRUE); //使Apply按钮允许 

    UpdateData(TRUE); 

    } 



    void CUnitPage::OnChangeUnit()  

    { 

    // TODO: Add your control notification handler code here 



    SetModified(TRUE); //使Apply按钮允许 

    UpdateData(TRUE); 

    } 

    当页中的编辑框的内容被改变时,页会收到EN_CHANGE消息,这将导致OnChangeName或OnChangeUnit被调用。对该消息的处理是使Apply按钮允许并调用UpdateData(TRUE)更新数据。 

    清单5.14 CRegisterSheet类的部分代码 

    //文件RegisterSheet.h 

    class CRegisterSheet : public CPropertySheet 

    { 



    . . . . . . 

    // Construction 

    public: 

    CRegisterSheet(UINT nIDCaption, CWnd* pParentWnd = NULL, UINT iSelectPage = 0); 

    CRegisterSheet(LPCTSTR pszCaption, CWnd* pParentWnd = NULL, UINT iSelectPage = 0); 



    public: 

    CPersonalPage m_PersonalPage; 

    CUnitPage m_UnitPage; 

    . . . . . . 

    protected: 

    //{{AFX_MSG(CRegisterSheet) 

    // NOTE - the ClassWizard will add and remove member functions here. 

    //}}AFX_MSG 



    afx_msg void OnApplyNow(); 

    DECLARE_MESSAGE_MAP()  

    }; 





    //文件RegisterSheet.cpp 

    #include "stdafx.h" 

    #include "Register.h" 



    #include "PersonalPage.h" 

    #include "UnitPage.h" 

    #include "RegisterSheet.h" 



    CRegisterSheet::CRegisterSheet(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage) 

    :CPropertySheet(pszCaption, pParentWnd, iSelectPage) 

    { 



    AddPage(&m_PersonalPage); //向标签对话框中添加页 

    AddPage(&m_UnitPage); 

    } 



    BEGIN_MESSAGE_MAP(CRegisterSheet, CPropertySheet) 

    //{{AFX_MSG_MAP(CRegisterSheet) 

    // NOTE - the ClassWizard will add and remove mapping macros here. 

    //}}AFX_MSG_MAP 



    ON_BN_CLICKED(ID_APPLY_NOW, OnApplyNow) 

    END_MESSAGE_MAP() 





    void CRegisterSheet::OnApplyNow() 

    { 

    CFrameWnd* pFrameWnd = (CFrameWnd*) AfxGetMainWnd(); 

    //获取指向视图的指针 

    CView* pView = pFrameWnd->GetActiveFrame()->GetActiveView(); 



    //发送用户定义消息,在视图中输出信息 

    pView->SendMessage(WM_USER_OUTPUT, (WPARAM)this); 

    m_PersonalPage.SetModified(FALSE); 

    m_UnitPage.SetModified(FALSE); //使Apply按钮禁止 

    } 

    在CRegisterSheet类内嵌入了CPersonalPage和CUnitPage对象,在该类的构造函数中调用CPropertySheet::AddPage将两个页添加到对话框中。 

    标签式对话框的OK、Cancel和Apply按钮的ID分别是IDOK、IDCANCEL和ID_APPLY_NOW。在按下Apply按钮后,CRegisterSheet对象应该作出响应,由于ClassWizard不能为CRegisterSheet类提供Apply按钮的BN_CLICKED消息处理函数,故必须手工声明和定义消息处理函数OnApplyNow,并在消息映射表中手工加入ID_APPLY_NOW的BN_CLICKED消息映射,该映射是通过ON_BN_CLICKED宏实现的。 

    函数OnApplyNow用CWnd::SendMessage向视图发送用户定义消息WM_USER_OUTPUT,并调用CPropertyPage::SetModified(FALSE)来禁止Apply按钮。在发送消息时,将this指针作为wParam参数一并发送,这是因为视图对象需要指向CRegisterSheet对象的指针来访问该对象。该函数演示了如何在程序的任意地方获得当前活动视图的方法:首先,调用AfxGetMainWnd()返回程序主窗口的CWnd类指针,然后将该指针强制转换成CFrameWnd类型,接着调用CFrameWnd::GetActiveFrame返回当前活动的框架窗口的一个CFrameWnd型指针,最后调用CFrameWnd::GetActiveView返回当前活动视图的一个Cview型指针。 

    在函数OnApplyNow中主要调用了下列函数: 

    CWnd* AfxGetMainWnd( );
    该函数返回一个指向程序的主窗口CWnd指针。程序的主窗口可以是一个框架窗口,也可以是一个对话框。 

    virtual CFrameWnd* GetActiveFrame( );
    函数返回一个CFrameWnd型的指针。如果是MDI(多文档界面)程序,则该函数将返回当前活动的子框架窗口,如果是SDI(单文档界面)程序,该函数将返回主框架窗口本身。 

    CView* GetActiveView( ) const;
    返回一个指向当前活动视图的Cview型指针。 

    LRESULT SendMessage( UINT message, WPARAM wParam = 0, LPARAM lParam = 0 );
    用于向本窗口发送消息。SendMessage会直接调用发送消息的处理函数,直到发送消息被处理完后该函数才返回。参数message说明了要发送的消息,wParam和lParam则提供了消息的附加信息。 

    清单5.15 CRegisterView类的部分代码 

    //文件RegisterView.h 

    class CRegisterView : public CEditView 

    { 



    . . . . . . 

    // Generated message map functions 

    protected: 

    //{{AFX_MSG(CRegisterView) 

    afx_msg void OnEditRegister(); 

    afx_msg void OnEditPropdlg(); 

    //}}AFX_MSG 



    afx_msg LRESULT OnOutput(WPARAM wParam, LPARAM lParam); 

    DECLARE_MESSAGE_MAP() 

    }; 



    //文件RegisterView.cpp 

    #include "stdafx.h" 

    #include "Register.h" 



    #include "RegisterDoc.h" 

    #include "RegisterView.h" 

    #include "RegisterDialog.h" 



    #include "PersonalPage.h" 

    #include "UnitPage.h" 

    #include "RegisterSheet.h" 





    BEGIN_MESSAGE_MAP(CRegisterView, CEditView) 



    . . . . . . 

    ON_MESSAGE(WM_USER_OUTPUT, OnOutput) 

    END_MESSAGE_MAP() 



    void CRegisterView::OnEditPropdlg()  

    { 

    // TODO: Add your command handler code here 



    CRegisterSheet RegisterSheet("登录");  

    RegisterSheet.m_PersonalPage.m_strName="张颖峰"; 

    RegisterSheet.m_UnitPage.m_strUnit="南京邮电学院"; 



    if(RegisterSheet.DoModal()==IDOK) 

    OnOutput((WPARAM)&RegisterSheet,0); 

    } 





    //用户定义消息WM_USER_OUTPUT的处理函数 

    LRESULT CRegisterView::OnOutput(WPARAM wParam, LPARAM lParam) 

    { 

    CRegisterSheet *pSheet=(CRegisterSheet*)wParam; 

    CString str; 



    GetWindowText(str); 



    str+="/r/n"; 



    str+="姓名:"; 

    str+=pSheet->m_PersonalPage.m_strName; 

    str+="/r/n"; 



    str+="工作单位:"; 

    str+=pSheet->m_UnitPage.m_strUnit; 

    str+="/r/n"; 



    SetWindowText(str); 



    return 0; 

    } 

    OnEditPropdlg函数负责初始化和创建标签式对话框,这一过程与创建普通对话框差不多。如果用户是按OK按钮返回的,则调用OnOutput函数输出数据。 

    CRegisterView类的OnOutput函数负责处理标签对话框发来的用户定义消息WM_USER_OUTPUT。用户定义消息的处理函数只能用手工的方法加入。用户定义消息的消息映射是用ON_MESSAGE宏来完成的。 

    函数OnOutput的两个参数wParam和lParam分别对应消息的wParam和lParam值。该函数从wParam参数中获得指向CRegisterSheet对象的指针,然后将该对象中的数据输出到视图中。 



    编译并运行Register,试一试自己设计的标签式对话框。 

    5.6 公用对话框

    在使用Windows的过程中,用户经常会遇到一些常用的有特定用途的对话框。例如,当选择File->Open,会弹出一个文件选择的对话框,用户可以在其中选择想要打开的文件。象文件选择这样的对话框,使用的非常普遍,因此Windows系统本身提供了对该对话框的支持,用户不必自己设计文件选择对话框。与文件选择对话框类似的还有颜色选择、字体选择、打印和打印设置以及正文搜索和替换对话框。这五种对话框均由Windows支持,被称为公用对话框。 

    MFC提供了一些公用对话框类,它们均是CDialog类的派生类,封装了公用对话框的功能。表5.6列出了MFC的公用对话框类。 

    表5.6 公用对话框类 

    通用对话框类  
    用途  

    CColorDialog  
    选择颜色  

    CFileDialog  
    选择文件名,用于打开和保存文件  

    CFindReplaceDialog  
    正文查找和替换  

    CFontDialog  
    选择字体  

    CPrintDialog  
    打印和打印设置  


    通用对话框类使用方便,读者只需知道怎样创建对话框和访问对话框的数据,不必关心它们的内部细节。 

    5.6.1 CColorDialog类 

    CColorDialog类用于实现Color(颜色选择)公用对话框。Color对话框如图5.12所示,在Windows的画板程序中,如果用户在颜色面板的某种颜色上双击鼠标,就会显示一个Color对话框来让用户选择颜色。 



    图5.12 Color对话框 



    Color对话框的创建与一般的对话框没什么两样:首先是在堆栈上构建一个CColorDialog对象,然后调用CColorDialog::DoModal( )来启动对话框。CColorDialog的构造函数为 

    CColorDialog( COLORREF clrInit = 0, DWORD dwFlags = 0, CWnd* pParentWnd = NULL );  
     
    参数clrInit用来指定初始的颜色选择,dwFlags用来设置对话框,pParentWnd用于指定对话框的父窗口或拥有者窗口。 

    根据DoModal返回的是IDOK还是IDCANCEL可知道用户是否确认了对颜色的选择。DoModal返回后,调用CColorDialog::GetColor()可以返回一个COLORREF类型的结果来指示在对话框中选择的颜色。COLORREF是一个32位的值,用来说明一个RGB颜色。GetColor返回的COLORREF的格式是0x00bbggrr,即低位三个字节分别包含了蓝、绿、红三种颜色的强度。 

    读者将在后面的章节中看到颜色选择对话框的例子。 





    5.6.2 CFileDialog类 

    CFileDialog类用于实现文件选择对话框,以支持文件的打开和保存操作。用户要打开或保存文件,就会和文件选择对话框打交道,图5.13显示了一个标准的用于打开文件的文件选择对话框。用MFC AppWizard建立的应用程序中自动加入了文件选择对话框,在File菜单选Open或Save As命令会启动它们。 



    图5.13 文件选择对话框 

    文件选择对话框的创建过程与一般对话框的类似,首先是在堆栈上构建一个CFileDialog对象,然后调用CFileDialog::DoModal( )来启动对话框。文件对话框的构造函数为 

     
    CFileDialog( BOOL bOpenFileDialog, LPCTSTR lpszDefExt = NULL, LPCTSTR lpszFileName = NULL, DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, LPCTSTR lpszFilter = NULL, CWnd* pParentWnd = NULL ); 

    如果参数bOpenFileDialog的值为TRUE,将创建Open(打开文件)对话框,否则就创建Save As(保存文件)对话框。参数lpszDefExt用来指定缺省的文件扩展名。lpszFileName用于规定初始文件名。dwFlags用于设置对话框的一些属性。lpszFilter指向一个过滤字符串,用户如果只想选择某种或某几种类型的文件,就需要指定过滤字符串。参数pParentWnd是指向父窗口或拥有者窗口的指针。  
    过滤字符串有特定的格式,它实际上是由多个子串组成,每个子串由两部分组成,第一部分是过滤器的字面说明,如“Text file (*.txt)”,第二部分是用于过滤的匹配字符串,如“*.txt”,子串的两部分用竖线字符“ | ”分隔开。各子串之间也要用“ | ”分隔,且整个串的最后两个字符必须是两个连续的竖线字符“ || ”。一个典型的过滤字符串如下面所示: 

    char szFilter[]= 

    “All files (*.*)|*.*|Text files(*.txt)|*.txt|Word documents(*.doc)|*.doc||”; 

    若CFileDialog::DoModal返回的是IDOK,那么可以用表5.7列出的CFileDialog类的成员函数来获取与所选文件有关的信息。 

    表5.7 CFileDialog类辅助成员函数 

    函数名  
    用途  

    GetPathName  
    返回一个包含有全路径文件名的CString对象。  

    GetFileName  
    返回一个包含有文件名(不含路径)的CString对象。  

    GetFileExt  
    返回一个只含文件扩展名的CString对象。  

    GetFileTitle  
    返回一个只含文件名(不含扩展名)的CString对象。  


    5.6.3 CFindReplaceDialog类 

    CFindReplaceDialog类用于实现Find(搜索)和Replace(替换)对话框,这两个对话框都是非模态对话框,用于在正文中搜索和替换指定的字符串。图5.14显示了一个Find对话框,图5.15显示了一个Replace对话框。 



    图5.14 Find对话框 



    图5.15 Replace对话框 

    由于Find和Replace对话框是非模式对话框,它们的创建方式与其它四类公用对话框不同。CFindReplaceDialog对象是用new操作符在堆中创建的,而不是象普通对话框那样以变量的形式创建。要启动Find/Replace对话框,应该调用CFindReplaceDialog::Create函数,而不是DoModal。Create函数的声明是 

    BOOL Create( BOOL bFindDialogOnly, LPCTSTR lpszFindWhat, LPCTSTR lpszReplaceWith = NULL, DWORD dwFlags = FR_DOWN, CWnd* pParentWnd = NULL ); 

    当参数bFindDialogOnly的值为TRUE时,创建的是Find对话框,为FALSE时创建的是Replace对话框。参数lpszFindWhat指定了要搜索的字符串,lpszReplaceWith指定了用于替换的字符串。dwFlags用来设置对话框,其缺省值是FR_DOWN(向下搜索),该参数可以是几个FR_XXX常量的组合,用户可以通过该参数来决定诸如是否要显示Match case、Match Whole Word检查框等设置。参数pParentWnd指明了对话框的父窗口或拥有者窗口。  
    Find/Replace对话框与其它公用对话框的另一个不同之处在于它在工作过程中可以重复同一操作而对话框不被关闭,这就方便了频繁的搜索和替换。CFindReplaceDialog类只提供了一个界面,它并不会自动实现搜索和替换功能。CFindReplaceDialog使用了一种特殊的通知机制,当用户按下了操作的按钮后,它会向父窗口发送一个通知消息,父窗口应在该消息的消息处理函数中实现搜索和替换。 

    CFindReplaceDialog类提供了一组成员函数用来获得与用户操作有关的信息,如表5.8所示,这组函数一般应在通知消息处理函数中调用。 

    表5.8 CFindReplaceDialog类的辅助成员函数 

    函数名  
    用途  

    FindNext  
    如果用户点击了Findnext按钮,该函数返回TRUE。  

    GetNotifier  
    返回一个指向当前CFindReplaceDialog对话框的指针。  

    GetFindString  
    返回一个包含要搜索字符串的CString对象。  

    GetReplaceString  
    返回一个包含替换字符串的CString对象。  

    IsTerminating  
    如果对话框终止了,则返回TRUE。  

    MatchCase  
    如果选择了对话框中的Match case检查框,则返回TRUE。  

    MatchWholeWord  
    如果选择了对话框中的Match Whole Word检查框,则返回TRUE。  

    ReplaceAll  
    如果用户点击了Replace All按钮,该函数返回TRUE。  

    ReplaceCurrent  
    如果用户点击了Replace按钮,该函数返回TRUE。  

    SearchDown  
    返回TRUE表明搜索方向向下,返回FALSE则向上。  


    CEditView类自动实现了Find和Replace对话框的功能,但MFC AppWizard并未提供相应的菜单命令。读者可以在前面的Register工程的Edit菜单中加入&Find...和&Replace...两项,并令其ID分别为ID_EDIT_FIND和ID_EDIT_REPLACE,则Find/Replace对话框的功能就可以实现。 

    5.6.4 CFontDialog类 

    CFontDialog类支持Font(字体)对话框,用来让用户选择字体。图5.16显示了一个Font对话框。Font对话框的创建过程与Color对话框的类似,首先是在堆栈上构建一个CFontDialog对象,然后调用CFontDialog::DoModal来启动对话框。 



    图5.16 Font对话框 

    CFontDialog类的构造函数如下所示 

    CFontDialog( LPLOGFONT lplfInitial = NULL, DWORD dwFlags = CF_EFFECTS | CF_SCREENFONTS, CDC* pdcPrinter = NULL, CWnd* pParentWnd = NULL );     
    参数lplfInitial指向一个LOGFONG结构,用来初始化对话框中的字体设置。dwFlags用于设置对话框。pdcPrinter指向一个代表打印机的CDC对象,若设置该参数,则选择的字体就为打印机所用。pParentWnd用于指定对话框的父窗口或拥有者窗口。 

    若DoModal返回IDOK,那么可以调用CFontDialog的成员函数来获得所选字体的信息,这些函数在表5.9列出。 

    表5.9 CFontDialog类的辅助成员函数 

    函数名  
    用途  

    GetCurrentFont  
    用来获得所选字体的属性。该函数有一个参数,该参数是指向LOGFONT结构的指针,函数将所选字体的各种属性写入这个LOGFONT结构中。  

    GetFaceName  
    返回一个包含所选字体名字的CString对象。  

    GetStyleName  
    返回一个包含所选字体风格名字的CString对象。  

    GetSize  
    返回所选字体的尺寸(以10个象素为单位)。  

    GetColor  
    返回一个含有所选字体的颜色的COLORREF型值。  

    GetWeight  
    返回所选字体的权值。  

    IsStrikeOut  
    若用户选择了空心效果则返回TRUE,否则返回FALSE。  

    IsUnderline  
    若用户选择了下划线效果则返回TRUE,否则返回FALSE。  

    IsBold  
    若用户选择了黑体风格则返回TRUE,否则返回FALSE。  

    IsItalic  
    若用户选择了斜体风格则返回TRUE,否则返回FALSE。  


    .6.5 CPrintDialog类 

    CPrintDialog类支持Print(打印)和Print Setup(打印设置)对话框,通过这两个对话框用户可以进行与打印有关的操作。图5.17显示了一个Print对话框,图5.18显示了一个Print Setup对话框。 



    图5.17 Print对话框 



    图5.18 Print Setup对话框 

    Print和Print Setup对话框的创建过程与Color对话框类似。该类的构造函数是 

    CPrintDialog( BOOL bPrintSetupOnly, DWORD dwFlags = PD_ALLPAGES | PD_USEDEVMODECOPIES | PD_NOPAGENUMS | PD_HIDEPRINTTOFILE | PD_NOSELECTION, CWnd* pParentWnd = NULL );     
    参数bPrintSetupOnly的值若为TRUE,则创建的是Print对话框,否则,创建的是Print Setup对话框。dwFlags用来设置对话框,缺省设置是打印出全部页,禁止From和To编辑框(即不用确定要打印的页的范围),PD_USEDEVMODECOPIES使对话框判断打印设备是否支持多份拷贝和校对打印(Collate),若不支持,就禁止相应的编辑控件和Collate检查框。pParentWnd用来指定对话框的父窗口或拥有者窗口。 

    程序可以调用如表5.10所示的CPrintDialog的成员函数来获得打印参数。 

    表5.10 CPrintDialog的辅助成员函数 

    函数名  
    用途  

    GetCopies  
    返回要求的拷贝数。  

    GetDefaults  
    在不打开对话框的情况下返回缺省打印机的缺省设置,返回的设置放在m_pd数据成员中。  

    GetDeviceName  
    返回一个包含有打印机设备名的CString对象。  

    GetDevMode  
    返回一个指向DEVMODE结构的指针,用来查询打印机的设备初始化信息和设备环境信息。  

    GetDriverName  
    返回一个包含有打印机驱动程序名的CString对象。  

    GetFromPage  
    返回打印范围的起始页码。  

    GetToPage  
    返回打印范围的结束页码。  

    GetPortName  
    返回一个包含有打印机端口名的CString对象。  

    GetPrinterDC  
    返回所选打印设备的一个 HDC 句柄。  

    PrintAll  
    若要打印文档的所有页则返回TRUE。  

    PrintCollate  
    若用户选择了Collate Copies检查框(需要校对打印拷贝)则返回TRUE。  

    PrintRange  
    如果用户要打印文档的一部分页,则返回TRUE。  

    PrintSelection  
    若用户想打印当前选择的部分文档,则返回TRUE。  


    用缺省配置的MFC AppWizard建立的程序支持Print和Print Setup对话框,用户可以在File菜单中启动它们。 

    5.6.6 公用对话框的使用实例 

    现在,让我们来测试一下公用对话框的使用。请读者用AppWizard创建一个单文档的MFC应用程序,名为CommonDlg。注意别忘了在AppWizard的第一步中选Single document。 

    CommonDlg程序要对所有的公用对话框进行了测试。为此,首先要提供用户命令接口。请读者在CommonDlg的菜单资源中插入一个名为&Common的新菜单,这个菜单插在Help菜单之前。然后,在Common菜单中,请按表5.11创建菜单项。 

    表5.11 Common菜单的菜单项 

    Caption  
    ID  

    &Color...  
    ID_COMMON_COLOR  

    &Open file...  
    ID_COMMON_OPENFILE  

    &Save file...  
    ID_COMMON_SAVEFILE  

    &Font...  
    ID_COMMON_FONT  

    &Print...  
    ID_COMMON_PRINT  

    P&rint setup...  
    ID_COMMON_PRINTSETUP  

    F&ind...  
    ID_COMMON_FIND  

    &Replace...  
    ID_COMMON_REPLACE  


    接下来的工作是编写测试程序的源代码。首先,利用ClassWizard为表5.11的菜单项创建消息处理函数,注意这些处理函数都是CCommonDlgView的成员。接着,请按清单5.10和5.11修改程序。限于篇幅,这里仅列出与测试相关的部分源代码。 

    清单5.10 头文件CommonDlgView.h 

    class CCommonDlgView : public CView 

    { 



    . . . . . . 

    #ifdef _DEBUG 

    virtual void AssertValid() const; 

    virtual void Dump(CDumpContext& dc) const; 

    #endif 



    protected: 

    void DispPrintInfo(CPrintDialog& dlg); 

    protected: 

    CFont m_Font; //正文的字体 

    COLORREF m_ForeColor; //正文的前景色 

    COLORREF m_BackColor; //正文的背景色 



    CFindReplaceDialog *m_pFindReplaceDlg; 

    BOOL m_bFindOnly; 

    // Generated message map functions 

    protected: 



    //Find和Replace对话框通知消息处理函数 

    afx_msg LRESULT OnFindReplaceCmd(WPARAM, LPARAM lParam); 

    //{{AFX_MSG(CCommonDlgView) 

    afx_msg void OnCommonColor(); 

    afx_msg void OnCommonFont(); 

    afx_msg void OnCommonOpenfile(); 

    afx_msg void OnCommonSavefile(); 

    afx_msg void OnCommonPrint(); 

    afx_msg void OnCommonPrintsetup(); 

    afx_msg void OnCommonFind(); 

    afx_msg void OnCommonReplace(); 

    //}}AFX_MSG 

    DECLARE_MESSAGE_MAP() 

    }; 



    . . . . . . 





    清单5.11 文件CCommonDlgView.cpp 

    #include "stdafx.h" 

    #include "CommonDlg.h" 



    #include "CommonDlgDoc.h" 

    #include "CommonDlgView.h" 



    #ifdef _DEBUG 

    #define new DEBUG_NEW 

    #undef THIS_FILE 

    static char THIS_FILE[] = __FILE__; 

    #endif 





    IMPLEMENT_DYNCREATE(CCommonDlgView, CView) 





    //获取对本进程唯一的消息编号 

    static const UINT nMsgFindReplace = ::RegisterWindowMessage(FINDMSGSTRING); 



    BEGIN_MESSAGE_MAP(CCommonDlgView, CView) 

    //{{AFX_MSG_MAP(CCommonDlgView) 

    ON_COMMAND(ID_COMMON_COLOR, OnCommonColor) 

    ON_COMMAND(ID_COMMON_FONT, OnCommonFont) 

    ON_COMMAND(ID_COMMON_OPENFILE, OnCommonOpenfile) 

    ON_COMMAND(ID_COMMON_SAVEFILE, OnCommonSavefile) 

    ON_COMMAND(ID_COMMON_PRINT, OnCommonPrint) 

    ON_COMMAND(ID_COMMON_PRINTSETUP, OnCommonPrintsetup) 

    ON_COMMAND(ID_COMMON_FIND, OnCommonFind) 

    ON_COMMAND(ID_COMMON_REPLACE, OnCommonReplace) 

    //}}AFX_MSG_MAP 

    // Standard printing commands 

    ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint) 

    ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint) 

    ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview) 



    ON_REGISTERED_MESSAGE(nMsgFindReplace, OnFindReplaceCmd) 

    END_MESSAGE_MAP() 



    CCommonDlgView::CCommonDlgView() 

    { 

    // TODO: add construction code here 



    //缺省前景色为黑色,背景色为白色,字体为系统字体 

    m_ForeColor=0; 

    m_BackColor=0xFFFFFF; 

    m_Font.CreateStockObject(SYSTEM_FONT); 



    m_pFindReplaceDlg=NULL;  



    } 



    void CCommonDlgView::OnDraw(CDC* pDC) 

    { 

    CCommonDlgDoc* pDoc = GetDocument(); 

    ASSERT_VALID(pDoc); 



    // TODO: add draw code for native data here 



    int x,y; 

    CFont *pOldFont; 

    TEXTMETRIC TM; 

    int textHeight; 



    //设置正文的字体 

    pOldFont=pDC->SelectObject(&m_Font); 

    //设置正文的前景色和背景色 

    pDC->SetTextColor(m_ForeColor); 

    pDC->SetBkColor(m_BackColor); 



    //计算每行正文的高度 

    pDC->GetTextMetrics(&TM); 

    textHeight=TM.tmHeight+TM.tmExternalLeading; 



    //输出正文 

    x=5;y=5; 

    pDC->TextOut(x,y,"ABCDEFG"); 

    y+=textHeight; 

    pDC->TextOut(x,y,"abcdefg"); 



    //恢复原来的字体 

    pDC->SelectObject(pOldFont); 



    } 



    void CCommonDlgView::OnCommonColor()  

    { 

    // TODO: Add your command handler code here 



    CColorDialog dlg; 



    if(dlg.DoModal()==IDOK) 

    { 

    m_BackColor=dlg.GetColor(); 



    //重绘视图 

    Invalidate(); 

    UpdateWindow(); 

    } 

    } 



    void CCommonDlgView::OnCommonFont()  

    { 

    // TODO: Add your command handler code here 



    CFontDialog dlg; 



    if(dlg.DoModal()==IDOK) 

    { 

    LOGFONT LF; 



    //获取所选字体的信息 

    dlg.GetCurrentFont(&LF); 

    m_ForeColor=dlg.GetColor(); 

    //建立新的字体 

    m_Font.DeleteObject(); 

    m_Font.CreateFontIndirect(&LF); 



    Invalidate(); 

    UpdateWindow(); 

    } 

    } 



    void CCommonDlgView::OnCommonOpenfile()  

    { 

    // TODO: Add your command handler code here 



    //过滤字符串 

    char szFileFilter[]= 

    "Cpp files(*.cpp)|*.cpp|" 

    "Header files(*.h)|*.h|" 

    "All files(*.*)|*.*||"; 



    CFileDialog dlg(TRUE, //Open对话框 

    "cpp", //缺省扩展名 

    "*.cpp", 

    OFN_HIDEREADONLY|OFN_FILEMUSTEXIST, //文件必须存在 

    szFileFilter, 

    this); 



    if(dlg.DoModal()==IDOK) 

    { 

    CString str="The full path name is:"; 

    str+=dlg.GetPathName(); 

    AfxMessageBox(str); 

    } 



    } 



    void CCommonDlgView::OnCommonSavefile()  

    { 

    // TODO: Add your command handler code here 



    char szFileFilter[]= 

    "Cpp files(*.cpp)|*.cpp|" 

    "Header files(*.h)|*.h|" 

    "All files(*.*)|*.*||"; 



    CFileDialog dlg(FALSE, //Save对话框 

    "cpp", 

    "*.cpp", 

    OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT, 

    szFileFilter, 

    this); 



    if(dlg.DoModal()==IDOK) 

    { 

    CString str="The file name is:"; 

    str+=dlg.GetFileName(); 

    AfxMessageBox(str); 

    } 



    } 





    void CCommonDlgView::OnCommonPrint()  

    { 

    // TODO: Add your command handler code here 



    CPrintDialog dlg(FALSE, PD_ALLPAGES); //Print对话框 

    //设置Print对话框的属性 

    dlg.m_pd.nCopies=2; 

    dlg.m_pd.nMinPage=1; 

    dlg.m_pd.nMaxPage=50; 

    dlg.m_pd.nFromPage=1; 

    dlg.m_pd.nToPage=50; 



    if(dlg.DoModal()==IDOK) 

    DispPrintInfo(dlg); 

    } 



    void CCommonDlgView::OnCommonPrintsetup()  

    { 

    // TODO: Add your command handler code here 



    CPrintDialog dlg(TRUE) //Print Setup对话框 

    if(dlg.DoModal()==IDOK) 

    DispPrintInfo(dlg); 

    } 





    void CCommonDlgView::DispPrintInfo(CPrintDialog& dlg) 

    { 

    CString str; 

    CString temp; 



    str+="Driver name:"; 

    str+=dlg.GetDriverName(); 

    str+="/nDevice name:"; 

    str+=dlg.GetDeviceName(); 

    str+="/nPort name:"; 

    str+=dlg.GetPortName(); 

    str+="/nNumber of copies:"; 

    temp.Format("%d",dlg.GetCopies()); 

    str+=temp; 

    str+="/nCollate:"; 

    str+=dlg.PrintCollate()?"Yes":"No"; 

    str+="/nPrint all:"; 

    str+=dlg.PrintAll()?"Yes":"No"; 

    str+="/nPrint range:"; 

    str+=dlg.PrintRange()?"Yes":"No"; 

    str+="/nSelection:"; 

    str+=dlg.PrintSelection()?"Yes":"No"; 

    str+="/nFrom page:"; 

    temp.Format("%d",dlg.GetFromPage()); 

    str+=temp; 

    str+="/nTo page:"; 

    temp.Format("%d",dlg.GetToPage()); 

    str+=temp; 



    AfxMessageBox(str); 



    } 



    void CCommonDlgView::OnCommonFind()  

    { 

    // TODO: Add your command handler code here 



    //判断是否已存在一个对话框 

    if(m_pFindReplaceDlg) 

    { 

    if(m_bFindOnly) 

    { 

    //若Find对话框已打开,则使之成为活动窗口 

    m_pFindReplaceDlg->SetActiveWindow(); 

    return; 

    } 

    else 

    //关闭Replace对话框 

    m_pFindReplaceDlg->SendMessage(WM_CLOSE); 

    } 

    m_bFindOnly=TRUE; 

    //创建Find对话框 

    m_pFindReplaceDlg=new CFindReplaceDialog; 

    m_pFindReplaceDlg->Create(TRUE,NULL,NULL,FR_DOWN,this); 

    } 



    void CCommonDlgView::OnCommonReplace()  

    { 

    // TODO: Add your command handler code here 



    //判断是否已存在一个对话框 

    if(m_pFindReplaceDlg) 

    { 

    if(!m_bFindOnly) 

    { 

    //若Replace对话框已打开,则使之成为活动窗口 

    m_pFindReplaceDlg->SetActiveWindow(); 

    return; 

    } 

    else 

    //关闭Find对话框 

    m_pFindReplaceDlg->SendMessage(WM_CLOSE); 

    } 

    m_bFindOnly=FALSE; 

    //创建Replace对话框 

    m_pFindReplaceDlg=new CFindReplaceDialog; 

    m_pFindReplaceDlg->Create(FALSE,NULL,NULL,FR_DOWN,this); 



    } 





    //Find和Replace对话框通知消息处理函数 

    LRESULT CCommonDlgView::OnFindReplaceCmd(WPARAM, LPARAM lParam) 

    { 

    //判断对话框是否被关闭 

    if(m_pFindReplaceDlg->IsTerminating()) 

    m_pFindReplaceDlg=NULL; 



    return 0; 

    }  

    让我们先来看看对Color对话框的测试。在CCommonDlgView::OnCommonColor中创建了一个Color对话框,在此处该对话框的用途是为视图中显示的正文指定背景色。在CCommonDlgView的构造函数中将背景色m_BackColor的初值设置为白色(0x00000000)。若DoModal返回IDOK,则调用CColorDialog::GetColor获取用户选择的颜色并将之保存在m_BackColor成员中。然后,调用Invalidate和UpdateWindow函数以重绘视图。这两个函数的说明如下:   

    void Invalidate( BOOL bErase = TRUE );
    该函数的作用是使整个窗口客户区无效。窗口的客户区无效意味着需要重绘,例如,如果一个被其它窗口遮住的窗口变成了前台窗口,那么原来被遮住的部分就是无效的,需要重绘。这时Windows会在应用程序的消息队列中放置WM_PAINT消息。MFC为窗口类提供了WM_PAINT的消息处理函数OnPaint,OnPaint负责重绘窗口。视图类有一些例外,在视图类的OnPaint函数中调用了OnDraw函数,实际的重绘工作由OnDraw来完成。参数bErase为TRUE时,重绘区域内的背景将被擦除,否则,背景将保持不变。 

    void UpdateWindow( );
    该函数的作用是使窗口立即重绘。调用Invalidate等函数后窗口不会立即重绘,这是由于WM_PAINT消息的优先级很低,它需要等消息队列中的其它消息发送完后才能被处理。调用UpdateWindow函数可使WM_PAINT被直接发送到目标窗口,从而导致窗口立即重绘。 

    在CCommonView::OnDraw函数中调用了CDC::SetBkColor来设置背景色。CDC类用于绘图,在后面的几章里将会对其作详细介绍。CDC::TextOut函数用于输出正文。两个函数的说明如下: 

     
    virtual COLORREF SetBkColor( COLORREF crColor );
    用于设置背景色。参数crColor指定了背景色的RGB值。返回的是原来的背景色。 

    BOOL TextOut( int x, int y, const CString& str );
    在指定的位置输出正文。参数x和y指定了输出起点的横向和纵向坐标。str参数是输出的字符串。若该函数调用成功则返回TRUE。 

    对文件选择对话框的测试比较简单。在CCommonDlgView::OnCommonOpenfile和CCommonDlgView:OnCommonSavefile函数中,分别创建了一个Open对话框和一个Save对话框。在创建Open对话框时,在CFileDialog的构造函数中规定了OFN_FILEMUSTEXIST属性,这样当用户试图打开一个不存在的文件时,对话框会发出错误信息并让用户从新选择文件。在创建Save对话框时,在CFileDialog的构造函数中规定了OFN_OVERWRITEPROMPT属性,这样,当用户试图覆盖一个已存在的文件时,对话框会询问用户是否真的要覆盖该文件。  
    若用户确认了对文件的选择,那么在文件选择对话框关闭后,程序会将所选文件的文件名或全路径文件名输出到屏幕上。 

    Find和Replace对话框的创建工作分别由CCommonDlgView::OnCommonFind和CCommonDlgView::OnCommonReplace完成。 

    在OnCommonFind函数中,首先判断是否已经打开了一个Find/Replace对话框。这个判断是完全必要的,因为Find/Replace对话框是非模态对话框,打开一个对话框后,用户有可能通过菜单再次执行Find或Replace命令。成员m_pFindReplaceDlg是指向CFindReplaceDialog对象的指针,若该指针不为空,则说明对话框已打开。接着,根据成员m_bFindOnly来判断原先打开的是否是Find对话框,如果原先打开的是一个Find对话框,则此时不必创建新的对话框,只需激活已打开的Find对话框就行了;如果原先打开的是一个Replace对话框,则应该先关闭该对话框,然后再创建Find对话框。然后,给m_bFindOnly赋TRUE值,以表明现在打开的是一个Find对话框。最后,创建一个非模态的Find对话框,请注意其过程与创建模态对话框的不同之处: 

    对话框对象是用new操作符在堆上创建的,而不是以变量的形式创建。 

    对话框的启动是靠调用Create函数实现的,而不是DoModal函数。 

    调用CWnd::SetActiveWindow以激活窗口。调用CWnd::SendMessage(WM_CLOSE)来关闭窗口,这是因为WM_CLOSE消息会导致CWnd::DestroyWindow函数的调用。 

    OnCommonReplace函数的过程与OnCommonFind函数类似。在该函数中,对m_bFindOnly赋值FALSE以表明打开的是Replace对话框。 

    Find/Replace对话框通知消息的处理函数是CCommonDlgView::OnFindReplaceCmd,这个消息处理函数及消息映射均是手工加入的。请注意在CommonDlgView.cpp文件的开头部分定义了一个静态全局变量nMsgFindReplace 

    static const UINT nMsgFindReplace = :: RegisterWindowMessage( FINDMSGSTRING ); 

    nMsgFindReplace变量用于存放消息码,这个消息是由函数RegisterWindowMessage提供的,该函数的声明为 

    UINT RegisterWindowMessage(LPCTSTR lpString); 

    参数lpString是一个消息字符串。调用RegisterWindowMessage函数会返回一个Windows注册消息,注册消息的编码在系统中是唯一的。当有多个应用程序需要处理同一个消息时,应调用该函数注册消息。如果消息是本应用程序专有的,则不必注册。如果两个应用程序使用相同的字符串注册消息,则会返回相同的消息,这样,通过该消息,两个应用程序可以进行通信。 

    注册消息的消息映射宏是ON_REGISTERED_MESSAGE,在CommonDlgView的消息映射中可以找到它。 

    在函数OnFindReplaceCmd中应该进行实际的搜索和替换工作,但在本例中该函数什么工作也不作。该函数只是判断一下对话框是否被关闭,若是,则给m_pFindReplaceDlg赋NULL值,以表明对话框已不存在了。 

    Font对话框的创建由函数CCommonView:: OnCommonFont完成。该函数收集了用户选择的字体的信息,并利用这些信息创建新的字体。成员m_ForeColor用来保存所选字体的颜色,成员m_Font是一个CFont对象,用来保存用户选择的字体。在CCommonView的构造函数中,m_ForeColor被初始化成黑色(0xFFFFFF),m_Font被初始化为系统字体。系统字体的获得是通过调用CGdiObject::CreateStockObject(SYSTEM_FONT)实现的,该函数用于获得系统库存的绘图对象,包括字体、画笔、刷子、调色板等。 

    在OnCommonFont函数中,主要调用了下列函数: 

    调用CFontDialog:: GetCurrentFont以获得用户选择字体的信息,该函数的声明为
    void GetCurrentFont( LPLOGFONT lplf );
    参数lplf是一个指向LOGFONT结构的指针,LOGFONT结构用来存放与字体有关的信息。  
    调用CFontDialog::GetColor来获得所选字体的颜色(前景色)。 

    调用CGdiObject:: DeleteObject()来删除存放在CFont对象m_Font中的老字体。 

    调用CFont::CreateFontIndirect以创建一种字体,该函数的声明是
    BOOL CreateFontIndirect(const LOGFONT* lpLogFont );
    参数lpLogFont是一个指向LOGFONT结构的指针,函数根据该结构提供的信息来初始化Cfont对象。 

    调用CWnd::Invalidate和CWnd::UpdateWindow重绘视图。 

    在CCommonView::OnDraw函数中,利用选择的字体和颜色输出两行正文。当视图需要重绘时,OnDraw就会被调用。在OnDraw函数中主要调用了下列函数:   
    在输出正文前,调用CDC::SelectObject指定输出正文的字体,输出完成后,调用CDC::SelectObject恢复被替换的字体。SelectObject有五个版本,用于为绘图指定画笔、刷子、字体、位图等绘图对象。在用该函数指定绘图对象时,应该把被替换的对象保存起来,在绘图完成后,需要再次调用该函数恢复被替换的绘图对象。如果不进行恢复,则可能会使设备对象CDC中含有非法的句柄。指定字体的SelectObject函数的声明是
    virtual CFont* SelectObject( CFont* pFont );
    参数pFont是指向CFont对象的指针。函数返回一个CFont对象的指针,指向被替代的字体。 

    调用CDC::SetTextColor来指定正文显示的前景色,该函数的声明为
    virtual COLORREF SetTextColor( COLORREF crColor );
    参数crColor指定了RGB颜色。函数返回的是原来的正文颜色。 

    调用CDC:: GetTextMetrics函数获得与绘图字体有关的各种信息,该函数的声明为 BOOL GetTextMetrics( LPTEXTMETRIC lpMetrics ) const; 参数lpMetrics是一个指向TEXTMETRIC结构的指针,该结构包含有字体的信息,其中tmHeight成员说明了字体的高度,tmExternalLeading成员说明了行与行之间的空白应该是多少。把这两个值相加就得到了每行正文的高度。 

    调用CDC::TextOut在指定位置输出正文。 

    在CCommonDlgView::OnCommonPrint和CCommonDlgView::OnCommonPrintsetup()函数中,分别创建了一个Print对话框和Print Setup对话框。在创建Print对话框时,通过CPrintDialog对象的m_pd成员,对对话框进行了一些初始化,这包括对拷贝份数、打印范围等的设置。在两个对话框DoModal返回IDOK后,均调用CCommonDlgView::DispPrintInfo报告打印信息。DispPrintInfo函数的代码较简单,这里就不作解释了。   

    小 结

    本课的要点为: 

    对话框的设计包括对话框模板的设计和对话框类的设计。对话框模板的设计是通过模板编辑器来完成的。对话框类的设计可借助ClassWizard来完成,这包括创建CDialog类的派生类,为对话框类增加与控件对应的成员变量,增加控件通知消息的处理函数等。 

    对话框的数据成员的初始化工作一般在其构造函数中完成,而对话框和控件的初始化是在OnInitDialog函数中完成的。 

    模态对话框拥有自己的消息循环,它垄断了用户的输入。模态对话框对象是以变量的形式构建的,CDialog::DoModal用来启动一个模态对话框,在对话框关闭后该函数才返回。如果用户按下了IDOK按钮确认设置,那么DoModal返回IDOK,若用户按下了IDCANCEL按钮取消设置,则DoModal返回IDCANCEL。 

    非模态对话框与应用程序共用消息循环,它不垄断用户的输入。非模态对话框对象应该用new操作符在堆中创建,应该调用CDialog::Create而不是CDialog::DoModal来显示对话框,需要注意对话框的可见性问题。应该调用CWnd::DestroyWindow而不是CDialog::EndDialog来关闭非模态对话框,所以一般需要重新编写OnOK和OnCancel函数。非模态对话框对象应该是自动清除的,所以应该重写PostNcDestroy函数并在该函数中用delete删除对象本身。

    除了主框架窗口类、视图类和非模态对话框类以外,MFC的窗口类一般都是非自动清除的。不必调用delete来删除一个具有自动清除功能的窗口对象。 

    标签式对话框由多个页(子对话框)组成,可以容纳大量的控件。CPropertySheet类代表对话框的框架,CPropertyPage类代表莫一页。标签式对话框有一个特殊的Apply按钮,可以使用户在不退出对话框的情况下使设置生效。 

    Windows支持五种公用对话框,包括文件选择、颜色选择、字体选择、打印和打印设置以及正文搜索和替换对话框。正文搜索和替换对话框与其它公用对话框不同,它是一个非模态对话框。 

    展开全文
  • 非模态对话框特点与使用

    千次阅读 2009-02-18 20:43:00
    非模态对话框特点与使用 与模态对话框不同,非模态对话框不垄断用户的输入,用户打开非模态对话框后,仍然可以与其它界面进行交互。非模态对话框的设计与模态对话框基本类似,也包括设计对话框模板设计CDialog...

    非模态对话框的特点与使用

       与模态对话框不同,非模态对话框不垄断用户的输入,用户打开非模态对话框后,仍然可以与其它界面进行交互。

    非模态对话框的设计与模态对话框基本类似,也包括设计对话框模板和设计CDialog类的派生类两部分。但是,在对话框的创建和删除过程中,非模态对话框与模态对话框相比有下列不同之处:

      • 非模态对话框的模板必须具有Visible风格,否则对话框将不可见,而模态对话框则无需设置该项风格。更保险的办法是调用CWnd::ShowWindow(SW_SHOW)来显示对话框,而不管对话框是否具有Visible风格。

      • 非模态对话框对象是用new操作符在堆中动态创建的,而不是以成员变量的形式嵌入到别的对象中或以局部变量的形式构建在堆栈上。通常应在对话框的拥有者窗口类内声明一个指向对话框类的指针成员变量,通过该指针可访问对话框对象。

      • 通过调用CDialog::Create函数来启动对话框,而不是CDialog::DoModal,这是模态对话框的关键所在。由于Create函数不会启动新的消息循环,对话框与应用程序共用同一个消息循环,这样对话框就不会垄断用户的输入。Create在显示了对话框后就立即返回,而DoModal是在对话框被关闭后才返回的。众所周知,在MFC程序中,窗口对象的生存期应长于对应的窗口,也就是说,不能在未关闭屏幕上窗口的情况下先把对应的窗口对象删除掉。由于在Create返回后,不能确定对话框是否已关闭,这样也就无法确定对话框对象的生存期,因此只好在堆中构建对话框对象,而不能以局部变量的形式来构建之。

      • 必须调用CWnd::DestroyWindow而不是CDialog::EndDialog来关闭非模态对话框。调用CWnd::DestroyWindow是直接删除窗口的一般方法。由于缺省的CDialog::OnOKCDialog::OnCancel函数均调用EndDialog,故程序员必须编写自己的OnOKOnCancel函数并且在函数中调用DestroyWindow来关闭对话框。

      • 因为是用new操作符构建非模态对话框对象,因此必须在对话框关闭后,用delete操作符删除对话框对象。在屏幕上一个窗口被删除后,框架会调用CWnd::PostNcDestroy,这是一个虚拟函数,程序可以在该函数中完成删除窗口对象的工作,具体代码如下
        void CModelessDialog::PostNcDestroy
        {
        delete this; //
        删除对象本身
        }
        这样,在删除屏幕上的对话框后,对话框对象将被自动删除。拥有者对象就不必显式的调用delete来删除对话框对象了。

      • 必须有一个标志表明非模态对话框是否是打开的。这样做的原因是用户有可能在打开一个模态对话框的情况下,又一次选 择打开命令。程序根据标志来决定是打开一个新的对话框,还是仅仅把原来打开的对话框激活。通常可以用拥有者窗口中的指向对话框对象的指针作为这种标志,当 对话框关闭时,给该指针赋NULL值,以表明对话框对象已不存在了。

    提示:在C++编程中,判断一个位于堆中的对象是否存在的常用方法是判断指向该对象的指针是否为空。这种机制要求程序员将指向该对象的指针初始化为NULL值,在创建对象时将返回的地址赋给该指针,而在删除对象时将该指针置成NULL值。

    根据上面的分析,我们很容易把Register程序中的登录数据对话框改成非模态对话框。这样做的好处在于如果用户在输入数据时发现编辑视图中有错误的数据,那么不必关闭对话框,就可以在编辑视图中进行修改。

    请读者按下面几步操作:

    在登录数据对话框模板的属性对话框的More Styles页中选择Visible项。

    RegisterView.h头文件的CRegisterView类的定义中加入
    public:
    CRegisterDialog* m_pRegisterDlg;

    RegisterView.h头文件的头部加入对CRegisterDialog类的声明
    class CRegisterDialog;
    加入该行的原因是在CRegisterView类中有一个CRegisterDialog类型的指针,因此必须保证CRegisterDialog类的声明出现在CRegisterView之前,否则编译时将会出错。解决这个问题有两种办法,一种办法是保证在#include RegisterView.h”语句之前有#include RegisterDialog.h”语句,这种办法造成了一种依赖关系,增加了编译负担,不是很好;另一种办法是在CRegisterView类的声明之前加上一个对CRegisterDialog的声明来暂时“蒙蔽”编译器,这样在有#include RegisterView.h”语句的模块中,除非要用到CRegisterDialog类,否则不用加入#include RegisterDialog.h”语句。

    RegisterDialog.cpp文件的头部的#include语句区的末尾添加下面两行
    #include "RegisterDoc.h"
    #include "RegisterView.h"

    利用ClassWizardCRegisterDialog类加入OnCancelPostNcDestroy成员函数。加入的方法是进入ClassWizard后选择Message Maps页,并在Class name栏中选择CRegisterDialog。然后,在Object IDs栏中选择IDCANCEL后,在Messages栏中双击BN_CLICKED,这就创建了OnCancel。要创建PostNcDestroy,先在Object IDs栏中选择CRegisterDialog,再在Messages栏中双击PostNcDestroy即可。

    分别按清单5.105.11,对CRegisterView类和CRegisterDialog类进行修改。

     

    清单5.10 CRegisterView类的部分代码

    CRegisterView::CRegisterView()

    {

    // TODO: add construction code here

     

    m_pRegisterDlg=NULL; //指针初始化为NULL

    }

     

    void CRegisterView::OnEditRegister()

    {

    // TODO: Add your command handler code here

     

     

    if(m_pRegisterDlg)

    m_pRegisterDlg->SetActiveWindow(); //激活对话框

    else

    {

    //创建非模态对话框

    m_pRegisterDlg=new CRegisterDialog(this);

    m_pRegisterDlg->Create(IDD_REGISTER,this);

    }

    }

     

     

    清单5.11 CRegisterDialog的部分代码

    void CRegisterDialog::PostNcDestroy()

    {

    // TODO: Add your specialized code here and/or call the base class

     

    delete this; //删除对话框对象

    }

     

    void CRegisterDialog::OnCancel()

    {

    // TODO: Add extra cleanup here

     

    ((CRegisterView*)m_pParent)->m_pRegisterDlg=NULL;

    DestroyWindow(); //删除对话框

    }

    CRegisterView::OnEditRegister函数判断登录数据对话框是否已打开,若是,就激活对话框,否则,就创建该对话框。该函数中主要调用了下列函数:

    调用CWnd::SetActiveWindow激活对话框,该函数的声明为
    CWnd* SetActiveWindow( );
    该函数使本窗口成为活动窗口,并返回原来活动的窗口。

    调用CDialog::Create来显示对话框,该函数的声明为
    BOOL Create( UINT nIDTemplate, CWnd* pParentWnd = NULL );
    参数nIDTemplate是对话框模板的IDpParentWnd指定了对话框的父窗口或拥有者。

     

    当用户在登录数据对话框中点击“取消”按钮后,CRegisterDialog::OnCancel将被调用,在该函数中调用CWnd::DestroyWindow来关闭对话框,并且将CRegisterView的成员m_pRegisterDlg置为NULL以表明对话框被关闭了。调用DestroyWindow导致了对CRegisterDialog::PostNcDestroy的调用,在该函数中用delete操作符删除了CRegisterDialog对象本身。

    编译并运行Register,现在登录数据对话框已经变成一个非模态对话框了。

    5.4.2 窗口对象的自动清除

    一个MFC窗口对象包括两方面的内容:一是窗口对象封装的窗口,即存放在m_hWnd成员中的HWND(窗口句柄),二是窗口对象本身是一个C++对象。要删除一个MFC窗口对象,应该先删除窗口对象封装的窗口,然后删除窗口对象本身。

    删除窗口最直接方法是调用CWnd::DestroyWindow::DestroyWindow,前者封装了后者的功能。前者不仅会调用后者,而且会使成员m_hWnd保存的HWND无效(NULL)。如果DestroyWindow删除的是一个父窗口或拥有者窗口,则该函数会先自动删除所有的子窗口或被拥有者,然后再删除父窗口或拥有者。在一般情况下,在程序中不必直接调用DestroyWindow来删除窗口,因为MFC会自动调用DestroyWindow来删除窗口。例如,当用户退出应用程序时,会产生WM_CLOSE消息,该消息会导致MFC自动调用CWnd::DestroyWindow来删除主框架窗口,当用户在对话框内按了OKCancel按钮时,MFC会自动调用CWnd::DestroyWindow来删除对话框及其控件。

    窗口对象本身的删除则根据对象创建方式的不同,分为两种情况。在MFC编程中,会使用大量的窗口对象,有些窗口对象以变量的形式嵌入在别的对象内或以局部变量的形式创建在堆栈上,有些则用new操作符创建在堆中。对于一个以变量形式创建的窗口对象,程序员不必关心它的删除问题,因为该对象的生命期总是有限的,若该对象是某个对象的成员变量,它会随着父对象的消失而消失,若该对象是一个局部变量,那么它会在函数返回时被清除。

    对于一个在堆中动态创建的窗口对象,其生命期却是任意长的。初学者在学习C++编程时,对new操作符的使用往往不太踏实,因为用new在堆中创建对象,就不能忘记用delete删除对象。读者在学习MFC的例程时,可能会产生这样的疑问,为什么有些程序用new创建了一个窗口对象,却未显式的用delete来删除它呢?问题的答案就是有些MFC窗口对象具有自动清除的功能。

    如前面讲述非模态对话框时所提到的,当调用CWnd::DestroyWindow::DestroyWindow删除一个窗口时,被删除窗口的PostNcDestroy成员函数会被调用。缺省的PostNcDestroy什么也不干,但有些MFC窗口类会覆盖该函数并在新版本的PostNcDestroy中调用delete this来删除对象,从而具有了自动清除的功能。此类窗口对象通常是用new操作符创建在堆中的,但程序员不必操心用delete操作符去删除它们,因为一旦调用DestroyWindow删除窗口,对应的窗口对象也会紧接着被删除。

    不具有自动清除功能的窗口类如下所示。这些窗口对象通常是以变量的形式创建的,无需自动清除功能。

    所有标准的Windows控件类。

    CWnd类直接派生出来的子窗口对象(如用户定制的控件)。

    切分窗口类CSplitterWnd

    缺省的控制条类(包括工具条、状态条和对话条)。

    模态对话框类。

     

    具有自动清除功能的窗口类如下所示,这些窗口对象通常是在堆中创建的。

    主框架窗口类(直接或间接从CFrameWnd类派生)。

    视图类(直接或间接从CView类派生)。

     

    读者在设计自己的派生窗口类时,可根据窗口对象的创建方法来决定是否将窗口类设计成可以自动清除的。例如,对于一个非模态对话框来说,其对象是创建在堆中的,因此应该具有自动清除功能。

    综上所述,对于MFC窗口类及其派生类来说,在程序中一般不必显式删除窗口对象。也就是说,既不必调用DestroyWindow来删除窗口对象封装的窗口,也不必显式地用delete操作符来删除窗口对象本身。只要保证非自动清除的窗口对象是以变量的形式创建的,自动清除的窗口对象是在堆中创建的,MFC的运行机制就可以保证窗口对象的彻底删除。

    如果需要手工删除窗口对象,则应该先调用相应的函数(如CWnd::DestroyWindow)删除窗口,然后再删除窗口对象.对于以变量形式创建的窗口对象,窗口对象的删除是框架自动完成的.对于在堆中动态创建了的非自动清除的窗口对象,必须在窗口被删除后,显式地调用delete来删除对象(一般在拥有者或父窗口的析构函数中进行).对于具有自动清除功能的窗口对象,只需调用CWnd::DestroyWindow即可删除窗口和窗口对象。注意,对于在堆中创建的窗口对象,不要在窗口还未关闭的情况下就用delete操作符来删除窗口对象.

    提示:在非模态对话框的OnCancel函数中可以不调用CWnd::DestroyWindow,取而代之的是调用CWnd::ShowWindow(SW_HIDE)来隐藏对话框.在下次打开对话框时就不必调用Create了,只需调用CWnd::ShowWindow(SW_SHOW)来显示对话框.这样做的好处在于对话框中的数据可以保存下来,供以后使用.由于拥有者窗口在被关闭时会调用DestroyWindow删除每一个所属窗口,故只要非模态对话框是自动清除的,程序员就不必担心对话框对象的删除问题.

    展开全文
  • 对话框和常用控件

    千次阅读 2011-04-20 09:22:00
    对话框Windows应用程序中最重要的用户界面元素之一,是与用户交互的重要手段,在程 序运行过程中,对话框可用于扑捉用户的输入信息或数据。对话框是一个特殊类型的窗口,任何对窗口进行的操作(如:移动、...

    对话框是Windows应用程序中最重要的用户界面元素之一,是与用户交互的重要手段,在程 序运行过程中,对话框可用于扑捉用户的输入信息或数据。对话框是一个特殊类型的窗口,任何对窗口进行的操作(如:移动、最大化、最小化等)都可在对话框中 实施,一般来说,在对话框中通过各种控件(如:按钮、编辑框、列表框、组合框等)来和用户进行交互。控件是在系统内部定义的用于和用户交互的基本单元。

    一、对话框的使用

        Visual C++提供的对话框编辑器能“可视”地进行设计、编辑,并可用

    ClassWizard为对话框从CDialog基类中派生一个类,MFC的CDialog类封装了用于对话框的显示、关闭等操作的许多功能函数,例如:DoModal函数用来显示模式对话框并返回用户操作的结果。

    1、模式对话框(为186附加举例页)

        所谓模式对话框是指,当对话被弹出时,用户必须在对话框中进行相应的操作,在退出对话框之前,对话框所在的应用程序不能继续执行。平常我们所见到的对话框大多是模式对话框。

    例:模式对话框(通过菜单命令弹出)

    1)  建一个单文档(SDI)应用程序

    2)  创建对话框模板

    InsertàResourceà选中Dialogà单击New

    拖过一个静态文本,鼠标对准它,按右键点properties改标题为“新建模式对话框”。

    3)鼠标右键对准对话框的任何位置单击,选择properties选项,设置ID为IDD_MYDIALOG

    4)给对话框创建类

         双击新建对话框的任何位置,单击OK,写类名为“CMyDlg”,保

    证”CDialog”作为该类的基类。

    5)创建菜单命令:

    a)打开资源编辑器的菜单项Menu

    b)  双击IDR_MAINFRAME

    c)  双击右边空白菜单,点开pop_up(让它是空白),在名字处写”弹出对话框(&A)”,ID处写ID_PUPDIALOG

    6)将菜单命令连接到主框架程序中,完成ID_PUPDIALOG的消息映射:

        ViewàClassWizardà保证Class name里是CMainFrame,在ObjectIDs

    里找到ID_PUPDIALOG点黑àMessages里(右边)点COMMAND建立主框架对象方法并加代码:

    void CMainFrame::OnPupdialog()

    { CMyDlg  MyDlg;

    MyDlg.DoModal(); //DoModal()是CDialog类成员函数,通过调用该

    //函数将显示对话框。

    }               

      7)在CMainFrame.cpp文件里加:

              #include “MyDlg.h”   // 之后运行。

     2、无模式对话框(为186附加页)

    非模式对话框,弹出后,可一直保留在屏幕上,用户可继续在应用中进行其它操作或启动其它应用程序,当需要使用对话框时,只需象激活一般窗口一样激活对话框即可。

    1)建一个SDI(单文档)应用程序

    2)创建对话框模板

    InsertàResourceà点黑DialogàNew

    拖过一个静态文本,鼠标对准它,按右键点properties改标题为“新建非模式对话框”。

    3)为对话框创建类

    点出对话框(IDD_DIALOG1缺省的ID号),双击对话框中的任意位置,出现一个表,你点OKà出现一个对话框,你写类名:CDlg保证基类为CDialog

    4)创建菜单

    打开工作区àMenuàIDR_MAINFRAMEà双击空白菜单写“非模式对话框”关闭à再点下面空菜单写名字“显示非模式对话框”ID处写ID_DLG.

    5)添加菜单命令消息

     WiewàClassWizardàMessage Mapsà保证Class name里是Wiew类(视图类),在Object IDS里找到ID_DLG(菜单的ID)点黑右边COMMAND双击它àOK

    6)a、你在Wiew.h里加:#include “Dlg.h”

       b、在public:里加:CDlg *dlg;//创建CDlg对象

       c、在View.cpp的OnDlg()函数里加:

          CMyView::OnDlg()

           { 

              dlg=new CDlg(this);

              dlg->Create(IDD_DIALOG1);//使对话框摸板从资源中创建一个非

    //模式对话框

              dlg->ShowWindow(SW_RESTORE);//显示对话框

            }

    二、使用对话框编辑器(对话框编程)(187页)

    *1、可建一个单文档应用程序名为“对话框编程”,在此程序中加入一个对话框:

       InsertàResourceà点黑Dialogànew 出现188页的图

    2、拖入一些控件,对这些控件进行排序、布局、大小调整、上下对齐、测试等。*最后在对话框IDD_DIALOG1上留一个按钮控件,其标识符为:IDC_BUTTON1

    3、识别控件工具栏(188页)

    4、在加对话框时,InsertàResourceà点开+Dialog见有7类对话框,分别了解其

    不同用途(见192页)。

    5、对话框的属性

    A、ViewàProperties B、按Alt+Enter C、用鼠标右键单击对话框模板àProperties

    都能弹出对话框的属性框,见书193页对属性General的解释。

    *6、为对话框添加类(194页)

        对准对话框的任意非控件区域双击鼠标,将弹出书194页图5.11所示

    的对话框,询问是否为对话框资源创建一个新类à单击OKà弹出书194页

    图5.12所示的对话框à你定义一个新类的名字如:CMyDlg(注意:类名必

    须以C打头),下面的基类Base class和ID标识符Dialog ID内容一般不改。

    * 7、添映射消息(195页)

      接上,点OK出现“MFC ClassWizard”对话框,如书195页图5.13所示à(保证类名处是CMyDlg)选定点黑IDC_BUTTON1à单击BN_CLICKED 消息àAdd Functionà出现书195页图5.14对话框àOKàEdit Code

    *8、添加用户代码(195页)

    接上,到MyDlg.cpp文件中,写:

    void CMyDlg::OnButton1()

    {

       MessageBox(“欢迎进入对话框的设计!”);

    }//这时运行还不见对话框,接下

    *9、在程序中使用对话框(196页)

        由于对话框的代码是以类为模块来设计的,使用时需要在程序中加入该类

    的头文件,并定义一个类对象,然后就可以使用该类的相关成员。

    项目工作区àFileViewà打开应用程序的.cpp文件,在前面加:

      #include “MyDlg.h”

       在InitInstance函数体中的return TRUE语句之前添加下列代码:

         CMyDlg dlg;

         dlg.DoModal();//DoModal()函数是负责对话框的显示和终止。

    运行!则直接显示出对话框,单击按钮则出现“欢迎进入对话框的设计”

    的字样。

    本例建的CMyDlg类及以后在各个项目中建立的类,在文档、视图、主框

    架类中都可同样使用。

    (1)添加对话框资源

    对话框资源类型:

    IDD_DIALOGBAR 对话条,往往和工具条放在一起。

    IDD_FORMVIEW  一个表状风格的对话框,用于无模式对话框或视图类

    IDD_OLE_PROPPAGE_LARGE一个大的OLE属性页

    IDD_OLE_PROPPAGE_SMALL一个小的OLE属性页

    IDD_PROPPAGE_LARGE一个大属性页,用于属性对话框

    IDD_PROPPAGE_MEDIUM一个中等大小的属性页,用于属性对话框

    IDD_PROPPAGE_SMALL一个小的属性页,用于属性对话框

    (2)改变对话框的属性

    ID框:修改或选择对话框的标识符名称

    Caption框:输入对话框的标题名称,中英文均可。

    Font按钮:单击此按钮可选择字体的种类(如宋体)及尺寸(如9号)

    Xpos/Ypos:对话框左上角在父窗口中的X,Y坐标都为0时表示居中

    Menu框:默认值为无,当对话框需要选单时输入或选择指定的选单资源

    Class name:默认值为无,它提供C/C++语言编程时所需要的对话框类名,对

               MFC类库的资源文件来说,该项不被激活

    三、控件的创建和使用方法(197页)

       控件是在系统内部定义的能够完成特定功能的控制程序单元。在应用程序中使用控件不仅简化了编程,还能完成常用的各种功能。为了更好地发挥控件的作用,用户还必须理解和掌握控件的属性、消息以及创建和使用方法。

    注:控件工具栏及各按钮含义:

    1、控件的选择2、静态文本3、组框4、复选框5、组合框6、水平滚动条

    7、旋转按钮8、滑动条9、列表视图10、标签11、复合编辑12、月历

    13、用户定制工具14、静态图片15、编辑框16、按钮17、单选框18、列表框

    19、垂直滚动条20、进展条21、热键22、树形视图23、动画24、日期选择

    25、IP地址26、组合框的扩展(从左边往下数,再从右边往下数)

     

    1、控件的创建和使用方法(197页)

        控件的创建方法有2种:1)是在对话框摸板中用编辑器指定控件,也就是说,将控件的父窗口指定为对话框,如:上面的BUTTON1按钮控件。2)是将控件 看做任意一窗口的子窗口,并通过调用相应的Create函数来创建。下面我们用第2种方法创建控件(此种方法涉及的内容复杂,也不能发挥对话框编辑器可视 化编程的优点,故不提倡此种方法,而用第一种方法)。

    (1)打开上个项目,在CMyDlg类的头文件MyDlg.h里添加一个按钮类CButton

         指针变量:(public:里)

             CButton *m_btnWnd;

    (2)按Ctrl+W或ViewàClassWizard打开MFClassWizard对话框,并切换到

    Message Maps页面,在Object IDs列表中选定点黑CMyDlg项,并在

    Message列表中找到WM_INITDIALOG消息点黑àAddFunctionàEditCode

    (3)添加代码:

    BOOL CMyDlg::OnInitDialog()

    { -----

     m_btnWnd=new CButton();//构造按钮控件

    //下面“”按钮上的字,创建子窗口|窗口最初是可见的|创建的是按键按钮

     m_btnWnd->Create(“你好”,WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,

                CRect(20,20,120,60),this,201);//创建

     CFont *font=this->GetFont();//获取对话框的字体

     m_btnWnd->SetFont(font);//设置控件字体

     return TRUE;//(程序原有的)

    }

    代码中,Create用来创建一个按钮控件,该函数的第一个参数用来指定该按钮的标题,第二个参数用来指定控件的风格,第三个参数用来指定它在父窗口中的位置和大小,第四个参数用来指定父窗口指针,最后一个参数是指定该控件的标识值。

    WS_CHILD表示按钮是作为对话框的一个子窗口来创建的。

    WS_VISIBLE是使控件可见。

    BS_PUSHBUTTON表示创建的是按键按钮。

     (4)编译并运行

    2、控件的数据交换和数据效验(数据成员)(198页)

        使用ClassWizard可以很容易地定义一个控件的成员变量及其数据范围。例如:上面的CMyDlg类的按钮控件IDC_BUTTON1添加并使用其成员变量m_MyBtn,步骤如下:

    (1)打开上例项目,ViewàClassWizardàMember Variablesà选定Class name

    中为CMyDlg,然后在Control IDs列表中à点黑IDC_BUTTON1àAddVariable

    (或双击鼠标左键),弹出Add Member Variable对话框,如书198页图5.17

    à写好数据成员名:m_MyBtn  下面Category和Variables type里的不动

    àOKà见表上已建成(如:书199页图5.18)

    下面是建成员变量的三个对话框图:

    (2)再向对话框加一个编辑控件,并加成员变量m_strEdit,类型为CString置

    数值为Value(注:Category框内可选择Value或Control两种类型。 Control所对应的变量类型就是MFC为该控件封装的控件类。Value所对应的是数值类型。不同控件所提供的关联的数值类型各不同,例如:对于编辑 框来说,Variables type中的数值类型有CString (字符串),int,UINT(32位无符号整数),long(32位带符号整数), DWORD(32位无符号整数,段地址和相关的偏移),float,double,BYTE(8位无符号整数),short,BOOL等)。OK后在下面 写20为最大值。(下面见书199—200页)

    打开本项目的MyDlg.h见到:

       CButton m_MyBtn;

       CString m_strEdit;

    在MyDlg.cpp里见到:

       m_strEdit=_T(“ “);

    在DoDataExchange函数体内见:

      DDX_Control(pDX,IDC_BUTTON1,m_MyBtn);

        DDX_Text(pDX,IDC_EDIT1,m_strEdit);//IDC_EDIT1是标识m_strEdit

    //是成员变量

    DDV_MaxChars(pDX,m_strEdit,20);//校验m_strEdit的最大字符个数不超过20。

     (3)将CMyDlg::OnButton1()修改成:

          void CMyDlg::OnButton1()

    {

       UpdateData();//默认参数值是真TRUE

       m_MyBtn.SetWindowText(m_strEdit);

       //GetDlgItem(IDC_BUTTON1)->SetWindowText(“欢迎”);//用此条代替

    //上条也行,

                                  //单击Button1按钮,此按钮名就是“欢迎”。

    }

    之后运行该程序,当在编辑框输入Hello后,单击Button1按钮,则该按

    钮的名称就变成了编辑框中的内容Hello了,见书200页。

    八、控件的通用属性 (201页)

            在控件的属性对话框中含有许多属性,如:书201页(按钮的属性对话

    框),General(一般属性)、Styles(控件的风格)、Extended Styles(控件的扩

    展风格)。Styles和Extended Styles是用来设定控件的外观的、辅助功能的。

    不同控件具有不同的风格和扩展风格。见201页表5.5.

    控件的General属性:

    ID:控件的标识符,每种控件都有默认的ID,例如按钮控件为IDC_BUTTON1

    Caption:控件的标题,大多数控件都有默认的标题,例如按钮控件为Button1

    Visible:指定控件初始化时是否可见

    Group:指定控件组中的第一个控件,如果该项被选中,则此控件后的所有控件均被看成一组,成组的目的是可以让用户键盘方向键在同一组控件中进行切换

    Help ID:若该项被选中,则为该控件建立一个上下文相关的帮助标识符

    Disabled:指定控件初始化时是否禁用

    TabStop:若该项被选中,则用户可以使用Tab键来选择控件

    九、控件的消息

         对于每个消息,系统都会用一个MSG结构来记录(见201页)。对一般控件来说,其通知消息是一条WM_COMMAND消息(见202页)。例:

    1、打开上面的项目“对话框”

    2、ViewàClassWizardà在CMyDlg里(左边的Object IDs里点黑CMyDlg)

       à在Message里找到OnCommand点黑àAdd FunctionàEdit Codeà写:

     BOOL CMyDlg::OnCommand(WPARAM wParam,LPARAM lParam)

       {

           WORD nCode=HIWORD(wParam);//控件的通知消息

              WORD nID=LOWORD(wParam);//控件的ID号

            if((nID==201)&&(nCode==BN_CLICKED))//用户单击按钮产生的消息

                 MessageBox("你按下了/"你好/"按钮!");

            ----

       }//WORD是16位无符号整数

        //EN_CHANGE是编辑框中的文本被改变时发出的消息

    BN_CLICKED是当用户单击按钮产生的消息:

    单击对话框中的“你好”按钮,弹出"你按下了/"你好/"按钮!"的消息对话框。由于 Create创建的控件无法用ClassWizard直接映射其消息,因此上述方法祢补了ClassWizard的不足,使用时要特别注意。见书202页 通知消息是所有Windows控件所共有的:

    NM_CLICK    在控件中单击鼠标左键按钮

    NM_DBLCLK  在控件中双击鼠标左键按钮

    NM_RDBLCLK在控件中双击鼠标右键按钮

    NM_RETURN当控件具有输入焦点时按下ENTER键

    NM_SETFOCUS控件得到输入焦点

    NM_KILLFOCUS控件失去输入焦点

    NM_OUTOFMEMORY没有足够的内存使控件

    十、常用控件

    静态控件 CStatic  用于向用户显示一些几乎固定不变的文字或图形描述

    按    钮 CButton 用于产生某些命令或改变某些选项设置

    编辑框   CEdit  可完成文字的输入、输出双向操作,使用户能查看并编辑文字

    列表框   CListBox显示一个列表,让用户从中选取一个或多个项

    组合框   CComboBox它把列表框和编辑框有机地组合在一起,用户不仅能选择

    列表中已有的项,还能编辑出新的项

    滚动条   CScrollBar通过滚动块在滚动条上的移动来改变某些数值

    进展条   CProgressCtrl 用于指示一个操作的进度

    旋转按钮 CSpinButtonCtrl 又称“上下控制”,是一对箭头按钮,用户单击它们

    可以增加或减小某个值

    滚动条   CSliderCtrl 是一个包含一个滑动块和可选的刻度线,用户可以用鼠标

    或方向键沿某个方向移动滑动块

    图象列表 CImageList是一系列相同大小的图象的集合

    标签控件 CTabCtrl类似于一个笔记本的分割器或一个文件柜上的标签,使用它

    可以将一个窗口或对话框的相同区域定义为多个页面

    1、静态控件

        静态控件是用于显示一个字符、框、矩形、图标、位图或增强的图元文件,它可以用做标签、框或用于分隔其它的控件。一个静态控件一般不接受用户输入,也不产生通知消息。

    在对话框编辑器的控件工具栏中,属于静态控件的有:静态文本(Static Text)、

    组框(Group Box)、图片控件(Picture)三种。其中,静态图片控件的属性对话框如:书204页图5.21所示,表5.7列出了其一般属性和风格的各个项的意 义。我们可以选择Type(图片类型)、Image(图象资源)两个组合框中的有关选项内容,并可将应用程序资源中的图标、位图等内容显示在该静态图片控 件中。另外,用户还可设置其风格来改变控件的外观以及图象在控件的位置等。

    静态图片控件的General和Style属性对话框:

    Type     图片类型,用户可以从中选择Frame(框)、Rectangle(矩形区域)、Icon(图标)、Bitmap(位图)、Enhanced Metafile(增强图元文件,它是各种绘图命令的集合)

    Image 当图片类型为Icon或Bitmap时,通过此框可选择指定的资源ID号

          设置Frame和Rectangle的颜色,它可以是black(黑色)、white(白色)、

          gray(灰色)、或者是具有3D外观的etched(腐蚀色)

    Sunken        选中时,在控件的周围有下沉的边框

    Notify         选中时,当用户单击或双击图片时会向其父窗口发出通知消息

    Right justify     选中时,用户重置图片大小,图片右下角是固定不变的

    Border          选中时,图片周围有边框

    Center image    选中时,图片显示在控件中央,其余区域由图片左上角的象素

    颜色来填充

    Real size image选中时,按图片的原始大小来显示,超过控件区域的部分被裁剪

       在它的属性中,用户可以选择Type(图片)、Image(图象资源)两个组合框中的有关选项内容,并可将应用程序资源中的图标、位图等内容显示在该静态图片控件中,另外,用户还可以设置其风格来改变控件的外观以及图象在控件的位置等。

    例1:图片控件(将一个.bmp图形显示在图片控件上)(附加举例)

    1)建一个单文档(SDI)应用程序

    2)创建对话框模板:  InsertàResourceà点黑Dialogànew

    3)将图片控件Picture拖到对话框上,并拉大些,将OK和CANCEL拖到下面。

    4)向项目中插入一个 .bmp图片:InsertàResourceà点黑BitmapàImportà在

    出现的表中要下拉出:所有文件(*.*)在某处找到一个.bmp图形àImport放到此项目中。

    5)将这个图片放到图片控件上

         右键对准图片控件单击à出现属性框àType处下拉置Bitmapàimage处下拉置IDB_BITMAP1,就将图片加到了图片控件上。

    6)为刚才建的对话框添加类

         双击新建对话框的任何位置àOKà类名写:CMyDlg

    7)创建一个菜单项,用来显示对话框

       ResourceViewàMenuàIDR_MAINFRAMEà双击空白菜单à点POPUP

    ID处写:ID_DLG 菜单名写:显示图片对话框à关闭

    8)将菜单命令映射到View中去 

         ViewàClassWizardà要加到View中àID-DLGàCOMMANDàEditCode

      (在View.h的头部加:#include “MyDlg.h” 在public:下加:CMyDlg *dlg;)并

    加代码:

            CMyView::OnDlg()

    {  dlg=new CMyDlg(this);

                  dlg->Create(IDD_DIALOG1);

                  dlg->ShowWindow(SW_RESTORE);

            }

    9)编译运行

    2:按钮

    常见的按钮有三种类型:(204页)

    (1)按键按钮

         按键按钮通常可以立即产生某个动作,执行某个命令,因此也常被称为命

    令按钮。

    (2)单选按钮

         其外形是在文本前有一个圆圈,当它被选中时,就标上一个黑点。

    (3)复选框

    其外形是在文本前有一个空心方框,当它被选中时,就加上一个“∨”标记

    1)按钮的消息

       常见的按钮映射消息有两个:

    (1)    BN_CLICKED(单击按钮)(2)BN_DOUBLE_CLICKED(双击按钮)

    见下图:

    2)按钮选中操作

      最常用的按钮操作是设置或获取一个按钮或多个按钮的选中状态。

    CButton类的以下2个成员函数原型如下:

      void SetCheck(int nCheck); //设置指定按钮的选中状态

      int GetCheck()const;      //获取指定按钮的选中状态

      其中:nCheck和GetCheck函数返回的值可以是:0表示不选中,1表示选中,

      2表示不确定(仅用于三态按钮)

      而对于多个单选按钮的选中状态的设置或获取,需要使用CWnd类的成员函数CheckRadioButton和GetCheckedRadioButton,它们的原型如下:

    void CheckRadioButton(int nIDFirstButton,int nIDLastButton,int nIDCheckButton);

    int GetCheckedRadioButton(int nIDFirstButton,int nIDLastButton);

    其中,nIDFirstButton和nIDLastButton分别指定这组单选按钮的第一个和最后一个按钮ID值,nIDCheckButton用于指定要设置选中状态的按钮ID值,函数GetCheckedRadioButton返回被选中的按钮ID值。

    例2:用静态图片、单选按钮、复选框和按键按钮控件设计界面,运行结果如:书205页图 5.23所示。刚开始,所有单选按钮都是灰显的,我们不能选择它们,这种情况称为“禁用”,而当选中“允许”复选框后,所有单选按钮可以使用,用户选定一 个单选框后,单击[应用]按钮,则弹出相应的消息对话框。

    (1)用MFC AppWizard(exe)创建一个名为:“按钮的使用”的基于对话框的应用

    程序。(第一步将类型选择为Dialog Based,然后按[Finish]按钮即可。)

    (2)打开属性对话框,将其标题改为“使用Windows常用控件”。

    (3)参看书205页图5.23的控件布局,用编辑器为对话框添加如书206页表5.8

    所示的一些控件。

    (4)说明:上面4个单选按钮的Tab次序应连续,而且从IDC_RADIO1到IDC_RADIO4依次增加。

    (5)ViewàClassWizard(或按Ctrl+W快捷键)à切换到Member Variables页面,在Class name中选择CMyDlg,在Control IDs里选中点黑IDC_CHECK1复选框ID号àAdd Variables按钮,为其添加一个BOOL类型的成员变量

         m_bEnabledàOK。

    (6)切换到ClassWizard的Message Maps页面,分别选中点黑复选框IDC_CHECK1和按钮IDC_BUTTON1,分别为其添加映射消息,并添如下代码:

          void CMyDlg::OnCheck1()

           {

             UpdateData();

             for(int i=0;i<4;i++)

             GetDlgItem(IDC_RADIO1+i)->EnableWindow(m_bEnabled);

    }//EnableWindow是使一个控件窗口禁用或允许使用,它取决于该函数

    //的参数,为TRUE时表示可以使用,否则禁用。

    void CMyDlg::OnButton1()

    {

      UpdateData();

      if(!m_bEnabled) return;

      int nID=GetCheckedRadioButton(IDC_RADIO1,IDC_RADIO4);

      if(nID==IDC_RADIO1)

       { MessageBox(“1”); }

        if(nID==IDC_RADIO2)

        { MessageBox(“2”); }

    if(nID==IDC_RADIO3)

        { MessageBox(“3”); }

    if(nID==IDC_RADIO4)

        { MessageBox(“4”); }

    (7)在此文件中找到CMyDlg::OnInitDialog函数体,添加下列代码:

         BOOL  CMyDlg::OnInitDialog()

          {

             CheckRadioButton(IDC_RADIO1,IDC_RADIO4,IDC_RADIO1);

                         //设置第一个单选按钮为选中

             OnCheck1();

             return TRUE;//此条是原有的

    (8)编译运行

    3、编辑框

       编辑框是一个让用户从键盘输入和编辑文本的矩形窗口,用户可以通过它,很方便地输入各种文本、数字或口令,也可使用它来编辑和修改简单的文本内容。

       当编辑框被激活且具有输入焦点时,就会出现一个闪动的插入符(又可称为文本光标),表明当前插入点的位置。

      1)编辑框的属性和风格

      

    Align text 各行文本对齐方式:Left,Center,Right,默认时为Left

    Multiline  选中时为多行编辑框,否则为单行编辑框

    Number   选中时控件只能输入数字

    Horizontal scroll 水平滚动,仅对多行编辑框有效

    Auto HScroll当用户在行尾键入一个字符时,文本自动向右滚动

    Vertical scroll 垂直滚动,仅对多行编辑框有效

    Auto VScroll 当用户在最后一行按ENTER键时,文本自动向上滚动一页,仅对

    多行编辑框有效

    Password      选中时,键入编辑框的字符都将显示为”*”,用于口令设置,仅对

    单行编辑框有效

    No hide selection通常情况下,当编辑框失去键盘焦点时,被选择的文本仍然反

    色显示,选中时,则不必具备此功能

    OEM convert   选中时,实现对特定字符集的字符转换

    Want return     选中时,用户按下ENTER键,编辑框中就会插入一个回车符

    Border         选中时,在控件的周围存在边框

    Uppercase      选中时,键入在编辑框的字符全部转换成大写形式

    Lowercase      选中时,键入在编辑框的字符全部转换成小写形式

    Read-Only      选中时,防止用户键入或编辑文本

    多行编辑框具有简单文本编辑器的常用功能,例如:它可以有滚动条,用户按Enter键另起一行以及文本的选定、复制、粘贴等常见操作。而单行编辑框功能较简单,它仅用于单行文本的显示和操作

      2)编辑框的基本操作

    A、设置口令

       口令设置在编辑框中不同于一般的文本编辑框,用户输入的每个字符都被一个特殊的字符代替显示。这个特殊的字符称为口令字符。默认的口令字符是”*”。

    应用程序可以用成员函数CEdit::SetPasswordChar 来定义自己的口令字符,其函数原形如下:

        void SetPasswordChar(TCHAR ch);

    其中,参数ch表示设定的口令字符;当ch=0时,编辑框内将显示实际字符。

    B、选择文本

    编程选择文本,调用成员函数CEdit::SetSel来实现,还有:

    CEdit::GetSel和CEdit::ReplaceSel,它们分别用来获取编辑框中选择的开始和结束的位置以及替换被选择的文本。

    C、设置编辑框的页面边距

    用CEdit::SetMargins函数来实现,其函数原型如下:

    void SetMargins(UINT nLeft,UINT nRight);

      其中,参数nLeft和nRight分别用来指定左、右边距的象素大小。

    D、剪帖板操作

    E、获取多行编辑框文本(见书208页---209页)。

    如何设置口令(密码):附加例题见后面(教案页)

    3)编辑框的通知消息

    当编辑框的文本修改或者被滚动时,会向其父窗口发送一些消息,这些消息

    是:(参看书209页表5.10并由下图所示:)

    EN_CHANGE当编辑框中的文本已被修改,在新的文本显示之后发送此消息

    EN_HSCROLL当编辑框的水平滚动条被使用,在更新显示之前发送此消息

    EN_KILLFOCUS编辑框失去键盘输入焦点时发送此消息

    EN_MAXTEXT文本数目到达了限定值时发送此消息

    EN_SETFOCUS编辑框得到键盘输入焦点时发送此消息

    EN_UPDATE编辑框中的文本已被修改,新的文本显示之前发送此消息

    EN_VSCROLL当编辑框的垂直滚动条被使用,在更新显示之前发送此消息

     例:使用静态文本、组框、编辑框以及按钮等控件设计界面,运行结果参书209

    页图5.25即下图所示。当用户在“成绩1”、“成绩2”、和“成绩3”编辑框中输

    入成绩后,单击[计算平均分]按钮,将显示出这三个成绩的平均分。

     程序设计步骤如下:

    (1)    打开前面的项目“按钮的使用”。

    (2)    向应用程序中添加一个对话框资源,insertàResourceà点黑Dialogànew

    à出现一个新的对话框à右键单击这个新对话框àpropertiesà打开其属

    性对话框àFontà将其字体设置为“新宋体,9”,标题改为“使用编辑框”,

    ID号改为IDD_EDIT,删除默认的Cancel按钮。

    (3)、(4)、(5)按书210页往下作

    书(6)m_strAve="0.00";

              UpdateData(FALSE);//将成员变量数据传给控件,并在控件中显示

    书(7)UpdateData();//将控件显示的数据传给成员变量

              double ave=(double)(m_nScore1+m_nScore2+m_nScore3)/3.0;

              m_strAve.Format("%6.2f",ave);//Format是CString类的一个经常使用的成

    //员函数,它通过格式操作使任意类型的数据转换成一个字符串

              UpdateData(FALSE);//将成员变量数据传给控件,并在控件中显示

    书(8)定位到void CMyDlg::OnButton1()

           { ---------//注意,这是你上个程序的按钮命令,在里找到:

             if(nID= =IDC_RADIO1)

             {  CEditDlg dlg;     //注意,将原来MessageBox("1");去掉

                dlg.DoModal();   //加上这2条

             }

    书(9)在上个程序的:按钮的使用Dlg.cpp即CMyDlg.cpp的头文件处加:

           #include "EditDlg.h"(就是(8)所在的文件)

        编译运行

    4、列表框

    列表框是一个列有许多项目让用户选择的控件。它与单选按钮组或复选框组一样,都可让用户在其 中选择一个或多个项。但不同的是,列表框中项的数目是可灵活变化的,程序运行时可往列表框中添加或删除某些项。并且,当列表框中项的数目较多而不能一次全 部显示时,还可以自动提供滚动条来让用户浏览其余的列表项。

    1)列表框的风格

    按性质来分,列表框有单选、多选、扩展多选以及非选四种类型,默认风格

    下的单选列表框让用户一次只能选择一个项,多列表框可让用户一次选择几个项,而扩展多项列表 框允许用户用鼠标拖动或其它特殊组合键进行选择,非选列表框不提供选择功能。还有其它一系列风格,用于定义列表框的外观及操作方式,这些风格可在下图所示 的列表框属性对话框中设置:列表框的Styl属性:

    Selection指定列表框的类型:单选(Single)、多选(Multiple)、扩展多选(Extended)、不选(None)

    Owner draw自画列表框,默认为No

    Has strings选中时,在自画列表框的项目中含有字符串文本

    Border选中时,使列表框含有边框

    Sort选中时,列表框的项目按字母顺序排列

    Notify选中时,当用户对列表框操作就会向父窗口发送通知消息

    Multi-column选中时,指定一个具有水平滚动的多列列表框

    Horizontal scroll选中时,在列表框中创建一个水平滚动条

    Vertical scroll选中时,在列表框中创建一个垂直滚动条

    No redraw选中时,列表框发生变化后不会自动重画

    Use tabstops选中时,允许使用停止位来调整列表项的水平位置

    Want key input选中此项,当用户按键且列表框有输入焦点时,就会向列表框的父窗口发送

    相应消息

    Disable no scroll选中时,即使列表框的列表项能全部显示,垂直滚动条也会显示,但此时

    是禁用的(灰显)

    No integral height选中时,在创建列表框的过程中,系统会把用户指定的尺寸完全作为列表

    框的尺寸,而不论是否有项目在列表框,也不能完全显示出来

    2)  列表框的基本操作

    当列表框创建之后,往往要添加、删除、改变或获取列表框中的列表项,这些操作都可以调用MFC的CListBox类成员函数加以实现。

    索引:表明项目在列表框中排列的位置,它是以0为基数的,即列表框中第一项的索引是0,第二项的索引是1,依次类推。

    (1)添加列表项

    列表框创建时是一个空的列表,需要用户添加或插入一些列表项,其函数原型为:

    int AddString(LPCTSTR lpszItem);         

    int InsertString(int nIndex,LPCTSTR lpszItem);

     其中:列表项的字符串文本由参数pszItem来指定,成功返回列表在列表框的索引,错

    误返回LB_ERR,空间不够返回LB_ERRSPACE。

    但:InsertString函数不会将列表项进行排序,不论列表框控件是否具有sort属性,只是将列表项插在指定索引的列表项之前,若nIndex等于-1,则列表项添加在列表框末尾。

    而:AddString函数在当列表框控件具有sort属性时会自动将添加的列表项进行排序。

    以上2个函数只能将字符串增加到列表框中,但有时用户还会需要根据列表项使用其他数据。这时,ListBox的SetItemData和SetItemDataPtr能有效解决这个问题,它们能使用户数据和某个列表项关联起来:

    int SetItemData(int nIndex,DWORD dwItemData);

    int SetItemDataPtr(int nIndex,void *pData);

    其中,SetItemData是将一个32位数与某列表项(由nIndex指定)关联起来,而

          SetItemDataPtr可以将用户的数组、结构体等大量的数据与列表项关联

          若产生错误,它们都返回LB_ERR

    而:GetItemData和GetItemDataPar分别用来获取相关联的用户数据。

     

    以下等待修改

    (2)删除列表项(3)查找列表项(4)列表框的单项选择

    (5)列表框的多项选择

    3)列表框的通知消息

        当列表框中发生了某个动作,如用户双击选择了列表框中某仪项时,列表框就会向父窗口发送一条通知消息。常用的通知消息如书214页表5.14所示。

    例:将一个SCORE结构(含有三门成绩的数据成员)数据和列表中每一个学生

    姓名列表关联起来。当用户单击[添加记录]按钮时,学生成绩记录中的“姓

    名“被添加在列表框中,且该学生的成绩与该列表项关联。当用户单击[删

    除记录]按钮时,列表框中当前选择项被删除,相关联的数据所占的内存空

    间被释放。任何时候选中列表框中某个学生,相应的记录数据被显示出来,

    如:书214页图5.27所示。

    (1)    打开前面的基于对话框的项目“按钮的使用”。

    (2)    向项目中添加一个对话框资源IDD_LISTBOX,标题为“使用列表框”,并用ClassWizard为此对话框建类为:CListBoxDlg。

    接着按书214页往下作:(3)、(4)-------

    (可在此处将第9步、第11步作出…………. 因为第5步要用到SCORE结构体 )

    书(5)

    UpdateData(TRUE);

           if(m_strName.IsEmpty())//判断m_strName是否为空

           { MessageBox("姓名不能为空!");

             return;

           }

           m_strName.TrimLeft();//裁剪m_strName左边的空格

           m_strName.TrimRight();//裁剪m_strName右边的空格

           if((m_List.FindString(-1,m_strName))!=LB_ERR)

           {  MessageBox("列表框中已有相同姓名,不能添加!");

              return;

           }

           int nIndex=m_List.AddString(m_strName);//向列表框添加学生

                             //姓名将该学生成绩与新增的列表项关联起来

           SCORE data;

           data.score1=m_nScore1;

           data.score2=m_nScore2;

           data.score3=m_nScore3;

           m_List.SetItemDataPtr(nIndex,new SCORE(data));

    书(6)

           int nIndex=m_List.GetCurSel();//获得当前选项的索引

           if(nIndex!=LB_ERR)

           {  m_List.DeleteString(nIndex);//删除当前选择项

              m_strName.Empty();

              m_nScore1=m_nScore2=m_nScore3=0;

              UpdateData(FALSE);

           }

           else MessageBox("当前没有选择项或列表框操作失败!");

    书(7)

    int nIndex=m_List.GetCurSel();

           if(nIndex!=LB_ERR)

           {  m_List.GetText(nIndex,m_strName);

              SCORE *data=(SCORE *)m_List.GetItemDataPtr(nIndex);

              m_nScore1=data->score1;

           m_nScore2=data->score2;

           m_nScore3=data->score3;

              UpdateData(FALSE);

           }

    书(8)

    for(int nIndex=m_List.GetCount()-1;nIndex>=0;nIndex--)

           { //删除所有与列表相关联的SCORE结构数据,并释放内存

                  delete(SCORE *)m_List.GetItemDataPtr(nIndex);

           }

    CDialog::OnDestroy();//关闭对话框

    说明:对话框被清除时发送WM_DESTROY消息。用户在此消息的映射函数中添加一些对象删除代码,以便在对话框清除前有效地释放内存空间。

    书(9)

    struct SCORE

       {  int score1;

          int score2;

             int score3;

       };

    书(10)定位到void CMyDlg::OnButton1()

           { ---------//注意,这是你上个程序的按钮命令,在里找到:

             if(nID= =IDC_RADIO2)

             {  CListBoxDlg dlg;     //注意,将原来MessageBox("2");去掉

                dlg.DoModal();   //加上这2条

             }

    }

    书(11)在上个程序的:按钮的使用Dlg.cpp即CMyDlg.cpp的头文件处加:

           #include "ListBoxDlg.h"

    //(就是(10)所在的文件)

        编译运行

    注意:第4步完后,可以先将第9步和第11步作出,以能点出相应函数。

    5、组合框

          列表框中不能输入列表项之外的内容,而编辑框也没有列表框的选择操

    作。于是就把常用的项,列在列表框中以供选择,而同时提供编辑框,允许

    用户输入列表框中所没有的新项,这就是组合框控件。它结合列表框和编辑

    框的特点,取二者之长,从而完成较为复杂的输入功能。

    1)  组合框的风格及类型

    按照组合框的主要风格特征,可分为三类:简单组合框、下拉式组合框、

    下拉式列表框。简单组合框和下拉式组合框都包含列表框和编辑框,但是简单组合框中的列表框不需要下拉,是直接显示出来的,而当用户单击下拉式组合框中的下拉按钮时,下拉的列表框才被显示出来。下拉式列表框虽然具有下拉式的列表,却没有文字编辑功能。

    组合框的一些其它风格,见书217页图5.28所示的组合框的属性对话框

    中的设置。其各项含义见书218页表5.17所示。

    2)  组合框常见操作

       组合框的操作大致分为两类,一类是对组合框中的列表框进行操作,

    另一类是对组合框中的编辑框进行操作。这些操作都可以调用CComboBox

    成员函数来实现,见书218页表5.18所示。由于组合框的一些编辑操作与

    编辑框CEdit的成员函数相似,如:GetEditSet,SetEditSel等,因此这些

    成员函数没有在上述表中列出。

    3)  组合框的通知消息

       在组合框的通知消息中,有的是操作列表列表框发出的,有的是操作

    编辑框发出的,如:书219页表5.19所示。

    例:根据用户从组合框中选择的填充样式,在对话框中绘制一个矩形区域,      如:书220页图5.29所示。由于对话框是一个窗口,所以用户可以调用GDI绘图函数在对话框内的任何部分进行绘图,至于绘图时需要的一些技巧见书 220页的说明:(1)、(2)、(3)。本例操作步骤如下:

    (1)打开前面创建的基于对话框应用程序“按钮的使用”。

    (2)向应用程序中添加一个对话框资源IDD_COMBO,标题为“使用组合框”,如:书220页图5.30所示。并用ClassWizard为新加的对话框定义类,名为:CComboDlg。

    (3)、(4)、(5)按书220--221页整个操作步骤做完。

    书(6)

    int nIndex=m_Pattern.GetCurSel();//获得当前选项的索引

              if(nIndex!=CB_ERR)

              {  m_nDrawPattern=m_Pattern.GetItemData(nIndex);//获得与当

                                                    //前选项相关联的数据

                 Invalidate();//强制系统调用OnPaint函数重新绘制

              }

    书(7)

    CWnd *pWnd=GetDlgItem(IDC_DRAW);//获得控件IDC_DRAW的窗口指针

              pWnd->UpdateWindow();//避免系统自动重绘

              CDC *pDC=pWnd->GetDC();//获得所需要的绘图设备环境

              CBrush drawBrush;//定义一个画刷

              drawBrush.CreateHatchBrush(m_nDrawPattern,RGB(0,0,0));//创建画刷

              CBrush *pOldBrush=pDC->SelectObject(&drawBrush);//将画刷选入当前设

    //备环境中

              CRect rcClient;//定义一个CRect变量

              pWnd->GetClientRect(rcClient);//获得窗口客户区大小

              pDC->Rectangle(rcClient);//用当前画刷绘制一个矩形区域

              pDC->SelectObject(pOldBrush);//恢复设备环境原来的画刷设置

    书(8)

    CDialog::OnInitDialog();

              CString str[6]={"水平线","竖直线","向下斜线","向上斜线",

                  "十字线","交叉线"};

              int nIndex;

              for(int i=0;i<6;i++)

              {  nIndex=m_Pattern.AddString(str[i]);

                 m_Pattern.SetItemData(nIndex,i);

              }

              m_Pattern.SetCurSel(0);

              m_nDrawPattern=0;

    书(9)

    CComboDlg dlg;

       dlg.DoModal();

    书(10)#include “ComboDlg.h”

    -------------------------

    6、滚动条和进展条

        进展条通常用来说明操作的进度,并在操作完成时从左到右充填进展条,

    这个过程可以让用户看到还有多少任务要完成。滚动条也是一种控件,它能

    完成诸如定位之类的操作。

    1)  滚动条

    滚动条是一个独立的窗口,虽然它具有直接的输入焦点,但却不能自动地滚动窗口的内容,因此,它的使用受到一定的限制。根据滚动条的走向,可分为垂直滚动条和水平滚动条两种类型。

    (1)滚动条的基本操作:滚动条的基本操作一般包括设置和获取滚动条的范围及滚动块的相应位置。由于滚动条控件的默认滚动范围是0---0,因此如果不设置滚动条范围,那麽滚动条中的滚动块就滚动不起来。

    在MFC的CScrollBar类中,函数SetScrollRange是用来设置滚动条范围的,其原型如下:

       SetScrollRange(int nMinPos,int nMaxPos,BOOL bRedraw=TRUE);

    其中:nMinPos和nMaxPos表示滚动位置的最小值和最大值。bRedraw

    为重画标志,当为TRUE时,滚动条被重画。在CScrollBar类中,设置滚

    动块位置操作是由SetScrollPos函数来完成的,其原型如下:

      int SetScrollPos(int nPos,BOOL bRedraw=TRUE);

    其中:nPos表示滚动块的新位置,它必须是在滚动范围之内。获取滚动条

    的当前范围以及当前滚动位置的两个函数原型是:

         void GetScrollRange(LPINT lpMinPos,LPINT lpMaxPos);

         int GetScrollPos();

    需要说明的是:在CScorllBar类的成员函数中,还可以用SetScrollInfo

    和GetScrollInfo来代替上面提到的4个函数。与前面的函数相比,使用

    SetScrollInfo函数还能使滚动块的大小随内容的多少而改变,并且这两个函

    数都使用下面的SCROLLINFO结构:见书223页这个结构体的描述:

    (2)当用户对滚动条进行操作时,滚动条就会向父窗口发送WM_HSCROLL(水平滚动 条)或WM_VSCROLL(垂直滚动条)消息。这些消息是通过ClassWziard在其对话框(滚动条的父窗口)中进行映射的,并产生相应的消息映射 函数OnHScroll和OnVScroll,这两个函数原型如下:

    afx_msg void OnHScroll(UINT nSBCode,UINT nPos,CScrollBar *pScrollBar);

    afx_msg void OnVScroll(UINT nSBCode,UINT nPos,CScrollBar *pScrollBar);

    其中:nPos表示滚动块的当前位置,pScrollBar表示由滚动条控件的指针,

    nSBCode表示滚动条的通知消息。见书223页表5.21所示。

    2)  进展条

    进展条是一个如书224页图5.33所示的控件。除了能表示一个过程的进展情况外,使用进展条还可以表明温度、水平面或类似的测量值。进展条的设置范围、当前位置、设置增量等,都是通过:CProgressCtrl类的成员函数实现的。

      int SetPos(int nPos);

      int GetPos();

    这两个函数分别用来设置和获取进展条的当前位置,需要说明的是,这个当前位置是指在SetRange中的上限和下限范围之间的位置。

     voit SetRange(short nLower,short nUpper);

     void SetRange32(int nLower,int nUpper);

    void GetRange(int &nLower,int &nUpper);

    它们分别用于设置和获取进展条范围的上限和下限值,一旦设置后,还会重画

    此进展条来反映新的范围,成员函数SetRange32为进展条设置32位的范围。

    参数:nLower和nUpper分别表示范围的下限(默认值为0)和上限(默认值

    为100)。

       int SetStep(int nStep);

    该函数用于设置进展条的步长并返回原来的步长,默认步长为10。

       int SetpIt();

    该函数将当前位置向前移动一个步长并重画进展条,以反映新的位置,函数

    返回进展条上一次的位置。

    7、  旋转按钮控件和滑动条

    “旋转按钮控件”(也称为上下控件)是一对箭头按钮,用户单击它们来增加

    或减小某个值,比如一个滚动位置或显示在相应控件中的一个数字。

    一个“滑动条控件”(也称为跟踪器)是一个包含一个滑动块和可选的刻度线的窗口。这两个控件的说明见书225页。

    1)旋转按钮控件

    (1)旋转按钮控件常用的风格

    见书225页图5.36所示,其中各项含义见书225页表5.22所示

    (2)旋转按钮控件的基本操作

    MFC的CSpinButtonCtrl类提供了旋转按钮控件的各种操作函数,使用

    它们用户可以进行基数、范围、位置设置和获取等基本操作。成员函数:

    SetBase是用于设置基数的,这个基数值决定了伙伴窗口显示的数字是十进

    制的还是十六进制的。函数原型如下:

    int SetBase(int nBase);// 是用于设置基数的

    其中参数nBase表示控件的新的基数。与此函数相对应的成员函数GetBase

    是获取旋转按钮控件的基数。

    int SetPos(int nPos);//设置旋转按钮控件的当前位置

    int SetRange(int nLower,int nUpper);// 设置旋转按钮控件的当前范围。

    参数:nPos表示控件的新位置,它必须在控件的上限和下限指定的范围之内。

          nLower和nUpper表示控件的上限和下限,任何一个界限值都不能大于Ox7fff或小于-Ox7fff.

    函数GetPos()和GetRange()分别用于获取旋转按钮控件的当前位置和范围成员函数:SetAccel()和GetAccel()分别用于设置和获取旋转按钮控件的加速度。其中函数SetAccel的原型如下:

         BOOL SetAccel(int nAccel,UDACCEL *pAccel);

    参数:nAccel表示由pAccel指定的UDACCEL结构的数目,pAccel指向一

    个UDACCEL结构数组的指针,该数组包含加速信息。见书226页说明。

    (3)旋转按钮的通知消息是:UDN_DELTAPOS它是当控件的当前数值将要改

    变时向其父窗口发送的。

    3)滑动条

        滑动条控件是由滑动块和可选的刻度线组成的。当用户用鼠标或方向键移动滑动块时,该控件发送通知消息来表明这些改变。见书226—227页说明。

    (1)滑动条的风格

    见书227页图5.38所示,其中各项含义见书227页表5.23所示。

    (2)滑动条的基本操作

    MFC的CSliderCtrl类提供了滑动条控件的各种操作函数,这其中包括范围、位置设置和获取等。

    void SetPos(int nPos);//设置滑动条的位置

    void SetRange(int nMin,int nMax,BOOL bRedraw=FALSE);// 设置滑动条的范围

    void GetPos();//获取滑动条的位置

    void GetRange();//获取滑动条的范围

    BOOL SetTic(int nTic);//设置滑动条控件中的一个刻度线的位置

    void SetTicFreq(int nFreq);//设置显示在滑动条中的刻度线的疏密程度

    void ClearTics(BOOL bRedraw=FALSE);

    void SetSelection(int nMin,int nMax);

    参数:nPos表示新的滑动条位置

          bMin和nMax表示滑动条的最小和最大位置

          bRedraw表示重画标志,为TRUE时,滑动条被重画。

          nTic表示刻度线的位置

          nFreq表示刻度线的疏密程度,如果参数设置为2,则在滑动条的范围中,

               每两个增量显示一个刻度线,要使这个函数有效,必须在属性对话

    框中选中Auto ticks项。

          bRedraw表示重画标志,若该参数为TRUE,则在选择被清除后,重画滑

    动条

          nMin和nMax表示滑动条的开始和结束位置

    (3)    滑动条的通知消息

              滑动条的通知消息代码常见的有:

    TB_BOTTOM,TB_ENDTRACK,

    TB_LINE_DOWN,TB_LINEUP,TB_PAGEDOWN,TB_PAGEUP,

    TB_THUMBPOSITION,TB_THUMBTRACK和TB_TOP等。这些消息代码都来自于WM_HSCROLL或WM_VSCROLL消息,其具体含义同滚动条。

    例:用滚动条、滑动条和旋转按钮控件分别来调整RGB的三种颜色分量:R

    (红色分量)、G(绿色分量)和B(蓝色分量),并根据用户指定的颜色填充

    一个矩形区域,如书228页图5.39所示:

    操作步骤如下:

    (1)打开前面创建的基于对话框的应用程序“按钮的使用”。

    (2)、(3)、(4)、(注意:在这可先将第(9)、(10)步作出,因第5步要用m_bEditOK

    和Draw)按书228---231页做完

    书(5)

    if(!m_bEditOK) return;

           UpdateData();

           m_Scroll.SetScrollPos(m_RValue);

           m_Slider.SetPos(m_GValue);

           Draw();

    书(6)

    CWnd *pWnd=GetDlgItem(IDC_DRAW);

           pWnd->UpdateWindow();

           Draw();

    书(7)

           //设置滚动条和滑动条的范围和当前位置

           m_Scroll.SetScrollRange(0,255);

           m_Scroll.SetScrollPos(m_RValue);

           m_Slider.SetRange(0,255);

           m_Slider.SetPos(m_GValue);

           //设置旋转按钮的范围

           m_Spin.SetRange(0,255);

           UpdateData(FALSE);//将数据传给控件

           m_bEditOK=TRUE;

    书(8)

    int nID=pScrollBar->GetDlgCtrlID();

           if(nID==IDC_SLIDER1)//是滑动条产生水平滚动消息

           {  m_GValue=m_Slider.GetPos();  }//获得滑动条当前的位置

           if(nID==IDC_SCROLLBAR1)//是滚动条产生水平滚动消息

           {

                  switch(nSBCode)

                  { 

                case SB_LINELEFT:m_RValue--;//单击滚动条左边箭头

                               break;

                    case SB_LINERIGHT:m_RValue++;//单击滚动条右边箭头

                                        break;

                    case SB_PAGELEFT:m_RValue-=10;

                                        break;

                    case SB_PAGERIGHT:m_RValue+=10;

                                        break;

              case SB_THUMBTRACK:m_RValue=nPos;

                                        break;

                  }

                  if(m_RValue<0)  m_RValue=0;

                  if(m_RValue>255)m_RValue=255;

                  m_Scroll.SetScrollPos(m_RValue);

           }

           UpdateData(FALSE);

           Draw();

    书(9)第4步将Draw()函数加上了,在这里添加代码:

           CWnd *pWnd=GetDlgItem(IDC_DRAW);

           CDC *pDC=pWnd->GetDC();//获得窗口当前的设备环境指针

           CBrush drawBrush;//定义画刷变量

           drawBrush.CreateSolidBrush(RGB(m_RValue,m_GValue,m_BValue));

           //以上创建一个填充颜色画刷,RGB是一个颜色宏,用来将指定的红、

           //绿、蓝三种颜色分量转换成一个32位的RGB颜色值

           CBrush *pOldBrush=pDC->SelectObject(&drawBrush);

           CRect rcClient;

           pWnd->GetClientRect(rcClient);

           pDC->Rectangle(rcClient);

           pDC->SelectObject(pOldBrush);

    书(10)第4步已作了,别忘了在CScrollDlg.cpp的构造函数例将m_bEditOK

            设为:FALSE

    书(11)还是定位到原始程序的OnButton1函数处,改MessageBox("4")代码为:

    CScrollDlg dlg;

          dlg.DoModal();

    书(12)在按钮的使用Dlg.cpp的开头处加:#include “ScrollDlg.h”

    书(13)编译运行

    附:设置口令(附加举例)

    (1)建一个多文档应用程序(选MultipleDocuments后,直接按Finish)

    (2)插入一个对话框ResourseViewà点中Dialog单击右键àInsertDialogà出现对话框

    (3)将这个对话框的ID改为IDD_PASSWORD_DIALOG,名字处写:口令

    (4)将OK和CANCEL改为确定和取消(并拖到最下边)

    (5)拖一个静态文本StaticText写请输入口令,并拖一个编辑控件Edit Box

         改ID为:IDC_PASSWORD_EDIT,在Styles中选PassWord属性。

    (6)再加一个静态文本StaticText写:口令为0----9999之间的一个整数。

      在风格ExtendedStyles中置Client edge、Static edge、Modal frame。

    (7)可测试一下对话框布局

    (8)创建对话框类:

          双击对话框任意位置àOKà出见类表你写类名为:CPassWordDialog

    (9)加成员变量:

      ViewàClassWizardàMemberVariablesà(classname中是CPassWordDialog

    点中IDC_PASSWORD_EDITàAddVariableà名字写:m_password,TYPE

    类型写:int àOK后回来处,在下面的Minimumvalue处写:0

    Maximumvalue处写:9999。

    (10)在应用程序的InitInstance()函数中加代码:

         Bool CMyApp::InitInstance()

          {  -----

             CPassWordDialog  CDlg;

             if(CDlg.DoModal()==IDOK)  //用户按了确定按钮

                {

                  if(CDlg.m_password!=1949) //口令为整数1949

                  {

                    AfxMessageBox(“口令错误,确定后将退出程序”);

                    return FALSE;

                   }

                 }

              else  //如果按下取消按钮程序也结束

                 return FALSE;

              return TRUE;

            }

    (11)在文件“设置口令.cpp“头部加:

           #include “PassWordDialog.h”

    (12)编译、运行

     

    ---------------------

    8、图像列表和标签控件

        “图像列表”是一系列相同大小的图像的集合,每一个图像提供一个以0为基数的索引号。而一个“标签控件”类似于一个笔记本中的分割器,或一个文件上的标签。

      1)图象列表控件  

        图象列表控件常用来有效地管理多个位图和图标。在MFC中,图象列表控件是使用CImageList类来创建、显示或管理图象的。

    (1)创建一个图象列表首先要声明一个CImageList对象,然后调用Create

    函数。由于Create函数的重载很多,故这里只给出最常用的一个原型:

    BOOL Create(int cx,int cy,UINT nFlags,int nInitial,int nGrow);

    参数:cx和cy用于指定图象的像素大小;

    nFlags表示要创建的图象类型,一般取其ILC_COLOR和ILC_MASK(指定屏蔽图像)的组合,默认的ILC_COLOR为ILC_COLOR4(16色),ILC_COLOR8(256色)ILC_COLOR16(16位色)等。

    nInitial用于指定图像列表中最初的图像数目

    nGrow表示当图像列表的大小发生改变时,图像可以增加的数目。

    (2)图像列表常见的基本操作:

       int Add(CBitmap *pbmImage,CBitmap *pbmMask);

       int Add(CBitmap *pbmImage,COLORREF crMask);

       int Add(HICON hIcon);

    如上函数用来向一个图像列表添加一个图标或多个位图,成功时返回第一个新图像的索引号,否则返回-1。

         参数:pbmImage表示包含图像的位图指针

               pbmMask表示包含屏蔽的位图指针

               crMask表示屏蔽色

               hIcon表示图标句柄

     BOOL Remove(int nImage);//用于从图像列表中删除一个由nImage指定的图像

     BOOL Drew(CDC *pdc,int nImage,POINT pt,UINT nStyle);//用于在由pt指定的位//置处绘制一个图像

    参数:pdc表示绘制的设备环境指针

    nImage表示要绘制的图像的索引号

    nStyle用于指定绘制图像时采用的方式

    HICON ExtractIcon(int nImage);//用于将nImage指定的图像扩展为图标

    COLORREF SetBkColor(COLORREF cr);//设置图像列表的背景色,COLORREF是一种专门用于定义颜色的数据类型。

    2)标签控件

    通过使用标签控件,应用程序可以将一个窗口或对话框的相同区域定义为多个页面。每一页包含一 套信息或一组控件。当用户选择了相应的标签时,应用程序就会显示相应的信息或控件。一种特殊类型的标签控件把标签显示得象按钮一样。单击一个按钮将立即执 行一条命令而不是显示一个页,这也是标签控件与属性页的最根本区别。

    (1)标签控件的风格

    见书232页图5.40所示,书233页表5.26列出该属性对话框的各项含义。

    (2)标签控件的基本操作

     MFC的CTabCtrl类提供了标签控件的各种操作函数。

      CSize SetItemSize(CSize size);//设置某个项的宽度和高度

      void SetPadding(CSize size);//设置标签和标签周围的间隔

      int SetMinTabWidth(int cx);//设置标签项的最小宽度

     BOOL GetItemRect(int nItem,LPRECT lpRect)const;//获取标签的边界大小

     BOOL InsertItem(int nItem,LPCTSTR lpszItem);//

     BOOL InsertItem(int nItem,LPCTSTR lpszItem,int nImage);

     //如上函数表示在一个标签控件中插入某一标签项

     BOOL DeleteItem(int nItem);// 表示在一个标签控件中删除某一标签项

     BOOL DeleteAllItem();//表示在一个标签控件中删除所有标签项

    参数:nItem用于指定一个标签索引(0表示第一个标签)

          lpszItem用于指定标签文本

          nImage用于指定标签的图标在图像列表中的图像索引。

    说明:在标签控件中使用图像列表时,必须调用:

        CTabCtrl::SetImageList()函数来指定一个已创建的图像列表,函数

    原型如下:

     CImageList *SetImageList(CImageList *pImageList);

                 参数:pImageList用于表示一个图像列表指针

            int SetCurSel(int nItem);//设置当前选择的标签

            int GetCurSel()const;//获取当前选择的标签

          参数:nItem用于表示当前选择项的索引

            void DeselectAll(BOOL fExcludeFocus);//重新设置一个标签控件中的

    //项,清除任何被按下的项

            BOOL HighlightItem(int idItem,BOOL fHighlight=TRUE);//使某一个标签

    //项处于高亮状态

            参数:fExcludeFocus用于指定一个重排标志

                  idItem用于指定一个标签的索引

                  fHighlight指定要设置的高亮状态

     (3)标签控件的通知消息。

              常见的标签通知消息有:

    TCN_KEYDOWN  //表示用户按下某键

    TCN_SELCHANGE//表示标签选项已被改变

    和TCN_SELCHANGING//当前标签选项将要改变

    例: 使用无模式对话框来构造某个标签页面,一开始将所有的标签页面创建好,然后根据所选择的当前标签选项,决定哪个页面需要显示,哪个页面需要隐藏和禁用。

       操作步骤如下:

    (1)用MFC AppWizard(exe)创建一个基于对话框的应用程序“图像列表和标签控件”。按书234---237页做完。

    (2)、(3)、(4)、(5)、(6)、(7)、(8)、(9)注意:第9步的工作已经在第8

    步自动加完了,这里就不做什么了。

    书(10)切到ClassView右键对准CScrollDlg击出对话框后,加函数时要这样加:

         类型:void  函数名:SetDlgState(CWnd *pWnd,BOOL bShow)

         类型:void  函数名:DoTab(int nTab)

    void CMyDlg::SetDlgState(CWnd *pWnd,BOOL bShow)  

    {  pWnd->EnableWindow(bShow);

       if(bShow)

       {  pWnd->ShowWindow(SW_SHOW);

          pWnd->CenterWindow();//居中显示

       }

       else pWnd->ShowWindow(SW_HIDE);

    }

    void CMyDlg::DoTab(int nTab)

    {  if(nTab>2) nTab=2;//确定nTab值不能超过范围

       if(nTab<0) nTab=0;

       BOOL bTab[3];

       bTab[0]=bTab[1]=bTab[2]=FALSE;

       bTab[nTab]=TRUE;

       //切换对话框的显示和隐藏

       SetDlgState(m_pTab1Dlg,bTab[0]);

       SetDlgState(m_pTab2Dlg,bTab[1]);

       SetDlgState(m_pTab3Dlg,bTab[2]);

    }

    书(11)     int nSelect=m_Tab.GetCurSel();

                  if(nSelect>=0)

                      DoTab(nSelect);

    书(12)……

    书(13)

    m_pTab1Dlg=new CTab1Dlg();//为无模式对话框分配空间

        m_pTab2Dlg=new CTab2Dlg();

        m_pTab3Dlg=new CTab3Dlg();

           //创建无模式对话框,指定标签控件为无模式对话框的父窗口

           m_pTab1Dlg->Create(IDD_DIALOG1,&m_Tab);

        m_pTab2Dlg->Create(IDD_DIALOG2,&m_Tab);

        m_pTab3Dlg->Create(IDD_DIALOG3,&m_Tab);

           m_ImageList.Create(16,16,ILC_COLOR|ILC_MASK,3,0);//创建图象列表

           m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_ICON1));//从图标加到图象列

    //表中

        m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_ICON2));

        m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_ICON3));

           m_Tab.SetImageList(&m_ImageList);//设置TAB控件所使用的图象列表

           m_Tab.InsertItem(0,"基本信息",0);

        m_Tab.InsertItem(1,"成绩",1);

        m_Tab.InsertItem(2,"备注",2);

           m_Tab.SetMinTabWidth(80);//设置标签项的最小宽度

           m_Tab.SetPadding(CSize(12,3));//设置标签项和图标周围的间隔

           m_Tab.SetCurSel(0);

           DoTab(0);

    书(14)if(m_pTab1Dlg) delete m_pTab1Dlg;

            if(m_pTab2Dlg) delete m_pTab2Dlg;

            if(m_pTab3Dlg) delete m_pTab3Dlg;

    书(15)CheckRadioButton(IDC_RADIO_MAN_1,IDC_RADIO_WOMAN_1,

                   IDC_RADIO_MAN_1);

    书(16)编译运行

    9、通用对话框和消息对话框

       A)通用对话框

         Windows提供了一组标准用户对话框,它们都具有相应的MFC库中的类

    来支持。所有通用对话框类都是从一个公共的基类CCommonDialog派生而来

    的。见书238页表5.28列出了这些通用对话框。

        这些对话框都有一个共同特点:它们都从用户获取信息,但并不对信息进

    行处理。例如:文件对话框可以帮助用户选择一个用于打开的文件,但它实际

    上只是给程序提供了一个文件路径名,用户的程序必须调用相应的成员函数才

    能打开文件。类似地,字体对话框只是填充一个描述字体的逻辑结构,但它并

    不创建字体。

    *** 例如:制作一个利用CFileDialog(文件对话框)类,弹出“打开”对话框的应用程序

    在打开一个文本文件或是位图文件时,往往要使用到CFileDialog类,CFileDilog类可以创建一个“打开”对话框,再通过调用其成员函数完成相应的功能,这里举出一些常用的函数。

            CFileDialog(BOOL bOpenFileDialog,LPCTSTR lpszDefExt=NULL,

              LPCTSTR lpszFileName=NULL,DWORD dwFlags=

    OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,

    LPCTSTR lpszFilter=NULL,CWnd *pParentWnd=NULL);

    //该函数用于创建CFileDialog类对象,如:

    CFileDialog dlg;

             Virtual int DoModal();//用于显示对话框,如:dlg.DoModal();

             CString GetPathName()const;//用于获取文件名和文件路径名

             CString GetFileName()const;//用于获取文件名

    操作步骤如下:

    1)创建一个单文档应用程序,名为:通用对话框

    2)创建弹出“打开”对话框的菜单命令

    MenuàIDR_MAINFRAMEà双击空白菜单à名处写:打开,ID处写:

    ID_FILEOPEN

    3)将ID_FILEOPEN连到view.cpp中去

    4)添加代码:

    (1)在view.h的public里加:

    CString FilePathname;//用于存储将要打开文件的路径名

    CString FileName; //用于存储文件名

    (2)完成菜单命令函数的代码:

       void CMyView::OnFileopen()

    {

       CFileDialog dlg(TRUE,_T(“TXT”),_T(“*.TXT”),

         OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,

         _T(“文本文件(*.TXT)|*.TXT|”));//设置打开文件为”.TXT”

       if(IDOK==dlg.DoModal())

        {

          FilePathname.Format(“%s %s”,”filepath:”,dlg.GetPathName());

          FileName.Format(“%s %s”,”filename:”,dlg.GetFileName());

    }

            Invalidate();

    }

     (3)在OnDraw()函数中添加如下代码:

    -----

     pDCàTextOut(0,0,FileName);//显示文件名

     pDCàTextOut(0,20,FilePathname);//显示路径名

    5)运行后,点刚建立的菜单,出现对话框,你随便写一个已有的文本文件(*.txt)文件名,便在屏幕上显示处文件名、路径名。

    B)消息对话框

         消息对话框是最简单的一类对话框,它只是用于显示信息。在Visual C++6.0

    的MFC类库中就提供相应的函数实现这样的功能,使用时,只要在用户程序

    任何地方调用它们即可。函数原型如下:

    int AfxMessageBox(LPCTSTR lpszText,UINT nType=MB_OK,UINT nIDHelp=0);

     //是全程函数,可以用在任何地方

    int MessageBox(LPCTSTR lpszText,LPCTSTR lpszCaption=NULL,UINT nType=

         MB_OK); //只能用于控件、对话框、窗口等一些窗口类中。

    参数:IDOK表示用户单击[OK]按钮

          lpszText表示在消息对话框中显示的字符串文本

          lpszCaption表示消息对话框的标题,为NULL时使用默认标题

          nIDHelp表示消息的上下文帮助ID号

          nType表示消息对话框的图标类型以及所包含的按钮类型,这些类型是

    用MFC预先定义的一些标识符来指定的,见书240页表5.29和5.30所示。

    例:消息对话框的使用

    (1)建一个基于对话框的应用程序

    (2)拖入对话框中两个按钮控件:BUTTON1 和BUTTON2

    (3)将这两个控件的映射消息分别加到:CMyDlg.cpp中

         ViewàClassWizard àClassnameàCMyDlgàIDC_BUTT

    本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/z443538426/archive/2008/01/12/2040082.aspx

    展开全文
  • 对话框和对话框类CDialog 对话框经常被使用,因为对话框可以从模板创建,而对话框模板是可以使用资源编辑器方便地进行编辑的。 模式无模式对话框 对话框分两种类型,模式对话框和无模式对话框
  • 在WIN32中,模式对话框的创建一般是使用DialogBox来进行创建的。而非模式对话框则是利用CreateWindow来创建的。在MFC或是WTL中,模式对话框一般是使用DoModal,而非模式对话框的创建则是使用Create。模式对话框创建...
  • 对话框概述

    2007-07-30 10:29:00
    对话框和对话框类CDialog 对话框经常被使用,因为对话框可以从模板创建,而对话框模板是可以使用资源编辑器方便地进行编辑的。 模式无模式对话框 对话框分两种类型,模式对话框和无模式对话框。 模式对话框
  • 只看懂了一小部分......不过刚刚好够 用.... ...VC 模式对话框和非模式对话框的创建,销毁区别 分类: 读书笔记2010-11-11 11:19 192人阅读 评论(0) 收藏 举报 在WIN32中,模式对话框的创建一般是
  • 首先,一个标准的对话框应该严格具备至少如下特点: 只要背后父窗体显示,它一定会显示,并且覆盖在父窗体之上。 对话框的窗口标题不会显示在任务栏中的,任务栏仅会显示主窗体的名称。 对于模式对话框(Model ...
  • VC 模式对话框和非模式对话框的创建,销毁区别 在WIN32中,模式对话框的创建一般是使用DialogBox来进行创建的。而非模式对话框则是利用CreateWindow来创建的。在MFC或是WTL中,模式对话框一般是使用DoModal...
  • 第十二讲 对话框和对话框类CDialog 对话框经常被使用,因为对话框可以从模板创建,而对话框模板是可以使用资源编辑器方便地进行编辑的。 模式无模式对话框 对话框分两种类型,模式对话框和无模式对话框...
  • MFC 对话框

    千次阅读 2013-10-30 15:15:47
    其类型分为模式对话框和非模式对话框对话框由一个rc资源文件描述外观,通过ID与一个CPP类相连接,对话框内的控件使用基于ID的变量映射通讯。 模式对话框对象被定义后,通过调用DoModal()函数来显示对话框并进行...
  • 12_对话框和对话框类CDialog

    千次阅读 2008-02-01 14:23:00
    1. 模式无模式对话框 对话框分两种类型,模式对话框和无模式对话框。1. 模式对话框 一个模式对话框是一个有系统菜单、标题栏、边线等的弹出式窗口。在创建对话框时指定WS_POPUP, WS_SYSMENU, WS_CAPTION DS_...
  • [转]非模态对话框特点与使用

    千次阅读 2010-02-02 12:12:00
    非模态对话框相对于模态对话框,他的创建销毁过程模态对话框有一定的区别 先看一下MSDN的原文: When you implement a modeless dialog box, always override the On Cancel member ...
  • 对话框和对话框类CDialog 对话框经常被使用,因为对话框可以从模板创建,而对话框模板是可以使用资源编辑器方便地进行编辑的。 模式无模式对话框 对话框分两种类型,模式对话框和无模式对话框
  • 非模态对话框

    2012-03-28 10:05:57
    1 非模态对话框特点   与模态对话框不同,非模态对话框不垄断用户的输入,用户打开非模态对话框后,仍然可以与其它界面进行交互。   非模态对话框的设计与模态对话框基本类似,也包括设计对话框模板设计...
  • 对话框和对话框类CDialog 对话框经常被使用,因为对话框可以从模板创建,而对话框模板是可以使用资源编辑器方便地进行编辑的。模式无模式对话框 对话框分两种类型,模式对话框和无模式对话框。模式对话框 一个模式...
  • 对话框和对话框类CDialog 对话框经常被使用,因为对话框可以从模板创建,而对话框模板是可以使用资源编辑器方便地进行编辑的。模式无模式对话框 对话框分两种类型,模式对话框和无模式对话框。模式对话框 一个模式...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 23,100
精华内容 9,240
关键字:

windows7对话框的功能和特点