精华内容
下载资源
问答
  • 对话框

    2020-10-12 11:34:26
    当完成交换后,单击对话框的按钮将关闭该对话框对话框只是应用程序生命周期的一部分。 #对话框的创建 import wx #定义窗口类MyFrame,包含aboutMenu子菜单并单击创建MyDialog对话框 class MyFrame(wx.Frame): ...

    对话框分为模态对话框、提示对话框、文本对话框、打开文件对话框等多种类型。在wxpthon中对话框都继承自wx.Dialog类。下面展示一下这些对话框。
    一、对话框的创建和使用与Frame相似,不同的对话框表示一次信息交换的活动。当完成交换后,单击对话框的按钮将关闭该对话框,对话框只是应用程序生命周期的一部分。

    #对话框的创建
    import wx
    
    #定义窗口类MyFrame,包含aboutMenu子菜单并单击创建MyDialog对话框
    class MyFrame(wx.Frame):
        def __init__(self):
            wx.Frame.__init__(self,None,-1,u'自定义对话框',size=(300,200))
            MenuHelp = wx.Menu()
            aboutMenu = MenuHelp.Append(-1,u'关于(&A)')
            menuBar = wx.MenuBar()
            menuBar.Append(MenuHelp,u'帮助(&H)')
            self.Bind(wx.EVT_TOOL,self.ShowAboutDlg,aboutMenu)
            self.SetMenuBar(menuBar)
    
    #定义showaboutdlg(),用该函数创建显示aboutmenu菜单
        def ShowAboutDlg(self,evt):
            pos = self.GetPosition()
    
    #创建对话框dialog,并显示MyFrame的相对位置
            dialog = MyDialog(self,-1,u'关于')
            dialog.SetPosition((pos[0] + 100,pos[1] + 60))
    
    #创建了MyDialog对话框类,继承自wx.Dialog,创建 确定 按钮打开对话框
    class MyDialog(wx.Dialog):
        def __init__(self,parent,id,title):
            wx.Dialog.__init__(self,parent,id,title,size=(100,100))
            self.panel = wx.Panel(self)
            self.OkBtn = wx.Button(self,10,u'确定',pos=(10,20),size=(80,30))
            self.Bind(wx.EVT_BUTTON,self.CloseDlg,self.OkBtn)
            self.Show() #调用show()显示对话框
    
        def CloseDlg(self,evt):
            self.Close()
    
    if __name__=='__main__':
        app = wx.PySimpleApp()
        MyFrame().Show()
        app.MainLoop()
    
    #由于模态对话框的约束性,因此模态对话框通常作为获取数据的窗口。
    #只有从模态对话框中返回数据后,才能关闭对话框
    

    二、提示对话框有助于用户体验的完善,例如,退出应用程序时显示退出提示,删除数据时显示删除提示。

    #提示对话框:通常用于帮助用户进行交互式操作
    import wx
    class MyFrame(wx.Frame):
        def __init__(self):
            wx.Frame.__init__(self,None,-1,u'对话框',size=(300,200))
            button = wx.Button(self,wx.ID_OK,u'退出',pos=(10,10))
            button.SetDefault()
            self.Bind(wx.EVT_BUTTON,self.OnClick,button)
        #创建对话框,根据wx.YES_NO,生成“是”、“否”按钮
        def OnClick(self,evt):
            #使用MessageDialog()类创建选择对话框
            dlg = wx.MessageDialog(None,u'是否退出?',u'退出',wx.YES_NO|wx.ICON_QUESTION)
            #判断用户选择的返回值
            if(dlg.ShowModal() == wx.ID_YES):
                frame.Close()   #关闭主程序
            #调用释放对话框对象
            dlg.Destroy()
    
    if __name__=='__main__':
        app = wx.PySimpleApp()
        frame = MyFrame()
        frame.Show()
        app.MainLoop()
    

    三、文本输入对话框:用于返回输入字符串的值,通常由一个静态文本框、一个输入文本框和OK、Cancel按钮组成,可由wx.TextEnTryDialog类创建。

    import wx
    class MyFrame(wx.Frame):
        def __init__(self):
            wx.Frame.__init__(self,None,-1,"TextEntryDialog",size=(300,200))
            panel = wx.Panel(self,-1)
    
            #创建TextCtrl实例textCtrl,并用wx.TE_PROCESS_ENTER处理回车事件
            self.textCtrl = wx.TextCtrl(panel,-1," ",pos=(10,10),style=wx.TE_PROCESS_ENTER)
            self.textCtrl.Bind(wx.EVT_TEXT_ENTER,self.OnClick,self.textCtrl)
    
        def OnClick(self,evt):
            #创建文本对话框,包含“ok”和“cancel”按钮
            self.dialog = wx.TextEntryDialog(None,u'输入文本',u'文本对话框',"",style=wx.OK|wx.CANCEL)
            #当按下OK按钮时
            if self.dialog.ShowModal() == wx.ID_OK:
                #获取文本输入内容,调用SetLabel把文本填入textCtrl控件的文本框
                self.textCtrl.SetLabel(self.dialog.GetValue())
    if __name__=='__main__':
        app = wx.PySimpleApp()
        MyFrame().Show()
        app.MainLoop()
    #创建1个TextCtrl控件,当按下回车键后,将显示TextEntryDialog对话框
    #用户输入文本后,单击“OK”按钮将把输入的值返回给TextCtrl控件
    

    四、特殊对话框:包括打开文本对话框、字体对话框、颜色选择对话框。打开文件对话框由wx.FileDialog类创建。

    #演示“打开”文件对话框的创建和使用方法
    import wx
    import os
    
    #创建打开文件对话框,显示当前目录中的文件,并可对类型进行过滤
    def ShowFileDialog():
    #文件类型的下拉列表提供“py”为后缀的文件和所有文件两种类型
        filterFile = "Python source(*.py)|*.py|All files(*.*)|*.*"  #过滤文件
        #对话框dialog。os.getcwd()获取当前文件所在的路径
        dialog = wx.FileDialog(None,u'选择文件',os.getcwd(),"",filterFile,wx.ART_FILE_OPEN)
        #以模式对话框的形式显示dialog对象
        dialog.ShowModal()
        dialog.Destroy()
    
    if __name__=='__main__':
        app = wx.PySimpleApp()
        ShowFileDialog()
        app.MainLoop()
    

    五、窗口与对话框的交互,对话框中的组件支持验证能力,而窗口不支持组件内容的验证。wx.PyValidator类的TransferWindow()和TransfromWindow()这两个方法实现窗口和对话框的交互。前者方法用于对话框打开时执行,后者方法用于对话框关闭的调用,可以实现窗口与对话框的数据交换。

    #创建Frame窗口,该窗口用于计算两个数字的和
    import wx
    class MyFrame(wx.Frame):
        def __init__(self):
            wx.Frame.__init__(self,None,-1,u"对话框的验证",size=(300,200))
            panel = wx.Panel(self,-1)
            sizer = wx.BoxSizer(wx.HORIZONTAL)
            self.addTextCtrl = wx.TextCtrl(panel,-1,"",pos=(10,10))
            addStaticText = wx.StaticText(panel,-1,"+")
            self.addTextCtrl2 = wx.TextCtrl(panel,-1,"")
            btn = wx.Button(panel,-1,u'计算')
            btn.Bind(wx.EVT_BUTTON,self.OnClick,btn)
            sizer.Add(self.addTextCtrl)
            sizer.Add(addStaticText)
            sizer.Add(self.addTextCtrl2)
            sizer.Add(btn)
            panel.SetSizer(sizer)
            panel.Fit()
        def OnClick(self,event):
        #定义字典data,把两个文本框中的数字存放到字典中
            data = {0:self.addTextCtrl.GetValue(),1:self.addTextCtrl2.GetValue()}
            dlg = MyDialog(data)
            dlg.ShowModal()
            dlg.Destroy()
    

    演示设计执行计算的对话框类,该对话框接收窗口定义的字典数据,然后进行计算。

    #演示设计执行计算的对话框类
    class MyDialog(wx.Dialog):
        def __init__(self,data):
            wx.Dialog.__init__(self,None,-1,u'验证器')
            addStaticText = wx.StaticText(self,-1,u'数字1:')
            addStaticText2 = wx.StaticText(self,-1,u'数字2:')
            self.addTextCtrl = wx.TextCtrl(self,validator = DataValidator(data,0))
            self.addTextCtrl2 = wx.TextCtrl(self,validator = DataValidator(data,1))
            btn = wx.Button(self,wx.ID_OK,u'确定')
            btn.Bind(wx.EVT_BUTTON,self.OnClick,btn)
            btn.SetDefault()
            sizer = wx.BoxSizer(wx.VERTICAL)
            gridSizer = wx.FlexGridSizer(2,2,5,5)
            gridSizer.Add(addStaticText,0,wx.ALIGN_LEFT)
            gridSizer.Add(self.addTextCtrl,0,wx.EXPAND)
            gridSizer.Add(addStaticText2,0,wx.ALIGN_LEFT)
            gridSizer.Add(self.addTextCtrl2,0,wx.EXPAND)
            sizer.Add(gridSizer,0,wx.EXPAND|wx.ALL,5)
            sizer.Add(btn,0,5)
            self.SetSize(sizer)
            sizer.Fit(self)
        def OnClick(self,evt):
            result = float(self.addTextCtrl.GetValue()) + float(self.addTextCtrl2.GetValue())
            wx.MessageBox(str(result),u'结果')
            self.Close()
    

    用DataValidator类实现创建数据的接收。

    class DataValidator(wx.PyValidator):
    #定义构造函数,用于接收data字典和相应的索引
        def __init__(self,data,key):
            wx.PyValidator.__init__(self)
            self.data = data
            self.key = key
         #覆盖close()方法,返回验证器的拷贝
        def Clone(self):    #close()方法必须实现
            return DataValidator(self.data,self.key)
        def Validata(self,w):   #验证数据
            return True
        def TransferToWindow(self): #对话框打开时调用
            textCtrl = self.GetWindow()	#获得当前组件
            #通过索引key查找当前组件的值,即把窗口文本框的内容传递给对话框的文本框
            textCtrl.SetValue(self.data.get(self.key,""))
            return True
        def TransferFromWindow(self): #对话框关闭时调用
            return True
    

    在DataValidator类的validata()方法实现对组件数据的验证,有兴趣者可以自己编写一些相关的验证规则来丰富对话框的验证功能。

    (好久没来写笔记了,希望大家勤快点哦)

    展开全文
  • android对话框

    千次阅读 2016-07-14 13:41:42
    android对话框

    在Android开发当中,在界面上弹出一个Dialog对话框使我们经常需要做的,本篇随笔将详细的讲解Dialog对话框这个概念,包括定义不同样式的对话框。

    一、Dialog

    我们首先来看看android官方文档对Dialog的介绍

    A dialog is a small window that prompts the user to make a decision or enter additional information. A dialog does not fill the screen and is normally used for modal events that require users to take an action before they can proceed.

    dialog就是一个在屏幕上弹出一个可以让用户做出一个选择,或者输入额外的信息的对话框,一个对话框并不会沾满我们整个的屏幕,并且通常用于模型事件当中需要用户做出一个决定后才会继续执行。

    Dialog类是dialog对话框的基类,但是我们应该避免直接使用这个类来实例化一个dialog对话框,我们应当使用其子类来得到一个对话框:

    复制代码
    java.lang.Object
       ↳     android.app.Dialog
    Known Direct Subclasses AlertDialog, CharacterPickerDialog, MediaRouteChooserDialog, MediaRouteControllerDialog, Presentation
    Known Indirect Subclasses DatePickerDialog, ProgressDialog, TimePickerDialog
    复制代码

    我们看到,Dialog有很多的子类实现,所以我们要定义一个对话框,使用其子类来实例化一个即可,而不要直接使用Dialog这个父类来构造。

    二、AlertDialog

    今天我们重点要来了解的就是AlertDialog对话框,我们看到,AlertDialog是Dialog的一个直接子类。

    使用AlertDialog,我们可以显示一个标题,最多3个按钮操作,以及一组选择框或者是自己定义的弹出框。

    这里借用android的官方文档提供的一个图来看看AlertDialog框的组成:

    ①区域1那里就是定义弹出框的头部信息,包括标题名或者是一个图标。

    ②区域2那里是AlertDialog对话框的content部分,在这里我们可以设置一些message信息,或者是定义一组选择框,还可以定义我们自己的布局弹出框。

    ③区域3那里使我们的Action Buttons部分,这里我们可以定义我们的操作按钮。

    说到Action Buttons这里要特别注意一下:

    在AlertDialog中,定义按钮都是通过 setXXXButton 方法来完成,其中一共有3种不同的Action Buttons供我们选择:

    复制代码
    1.setPositiveButton(CharSequence text, DialogInterface.OnClickListener listener)
    这是一个相当于OK、确定操作的按钮,
    
    2.setNegativeButton (CharSequence text, DialogInterface.OnClickListener listener)
    这是一个相当于取消操作的按钮。
    
    3. setNeutralButton (CharSequence text, DialogInterface.OnClickListener listener)
    这个是相当于一个忽略操作的按钮。
    复制代码

    我们每一种action buttons最多只能出现一个,即弹出对话框最多只能出现一个PositiveButton。

    接下来我们通过一个一个的具体实例来看看我们常用的几种AlertDialog对话框。

    1.弹出一个警告框,并有三个按钮可选择

    我们来看看代码部分:

    复制代码
    button.setOnClickListener(new OnClickListener()
            {
                @Override
                public void onClick(View v)
                {
                    //    通过AlertDialog.Builder这个类来实例化我们的一个AlertDialog的对象
                    AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                    //    设置Title的图标
                    builder.setIcon(R.drawable.ic_launcher);
                    //    设置Title的内容
                    builder.setTitle("弹出警告框");
                    //    设置Content来显示一个信息
                    builder.setMessage("确定删除吗?");
                    //    设置一个PositiveButton
                    builder.setPositiveButton("确定", new DialogInterface.OnClickListener()
                    {
                        @Override
                        public void onClick(DialogInterface dialog, int which)
                        {
                            Toast.makeText(MainActivity.this, "positive: " + which, Toast.LENGTH_SHORT).show();
                        }
                    });
                    //    设置一个NegativeButton
                    builder.setNegativeButton("取消", new DialogInterface.OnClickListener()
                    {
                        @Override
                        public void onClick(DialogInterface dialog, int which)
                        {
                            Toast.makeText(MainActivity.this, "negative: " + which, Toast.LENGTH_SHORT).show();
                        }
                    });
                    //    设置一个NeutralButton
                    builder.setNeutralButton("忽略", new DialogInterface.OnClickListener()
                    {
                        @Override
                        public void onClick(DialogInterface dialog, int which)
                        {
                            Toast.makeText(MainActivity.this, "neutral: " + which, Toast.LENGTH_SHORT).show();
                        }
                    });
                    //    显示出该对话框
                    builder.show();
                }
            });
    复制代码

    我们如果要创建一个AlertDialog对话框,需要使用AlertDialog的一个内部类,即AlertDialog.Builder来构建一个AlertDialog的对话框,然后通过setXX方法来设置我们想要显示的内容即可。

    我们看到,我们一共设置了3个action buttons,每一个button都为其绑定了一个 DialogInterface.OnClickListener() 的监听事件,然后在里面通过Toast吐司对话框(这个在后面随笔中会讲解)来弹出一下我们的一些信息,which方法表示的是action button所代表的int值:

    positive:  -1
    negative:  -2
    neutral:  -3

    我们可以知道,which=-1就表示点击的是确定按钮,-2表示点击的是取消按钮,-3表示点击的是忽略按钮。

    2.下拉列表弹出框

    关键代码如下:

    复制代码
    button2.setOnClickListener(new OnClickListener()
            {
                @Override
                public void onClick(View arg0)
                {
                    AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                    builder.setIcon(R.drawable.ic_launcher);
                    builder.setTitle("选择一个城市");
                    //    指定下拉列表的显示数据
                    final String[] cities = {"广州", "上海", "北京", "香港", "澳门"};
                    //    设置一个下拉的列表选择项
                    builder.setItems(cities, new DialogInterface.OnClickListener()
                    {
                        @Override
                        public void onClick(DialogInterface dialog, int which)
                        {
                            Toast.makeText(MainActivity.this, "选择的城市为:" + cities[which], Toast.LENGTH_SHORT).show();
                        }
                    });
                    builder.show();
                }
            });
    复制代码

    在这里我们通过 setItems(CharSequence[] items, DialogInterface.OnClickListener listener) 方法来设置我们的一个下拉列表框。注意:因为下拉列表框或者是下拉多选框这些都是显示在Content中的,所以message和下拉列表框这些是不能够同时存在的

    我们也可以给其绑定一个DialogInterface.OnClickListener监听器,当选中一个选项时,对话框就会消失掉。这里的which代表的是下拉列表的每个选项的索引,通过这个我们可以轻松得到用户选中的是哪一个选项。

    3.弹出一个下拉单选框

    复制代码
    button3.setOnClickListener(new OnClickListener()
            {
                @Override
                public void onClick(View v)
                {
                    AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                    builder.setIcon(R.drawable.ic_launcher);
                    builder.setTitle("请选择性别");
                    final String[] sex = {"男", "女", "未知性别"};
                    //    设置一个单项选择下拉框
                    /**
                     * 第一个参数指定我们要显示的一组下拉单选框的数据集合
                     * 第二个参数代表索引,指定默认哪一个单选框被勾选上,1表示默认'女' 会被勾选上
                     * 第三个参数给每一个单选项绑定一个监听器
                     */
                    builder.setSingleChoiceItems(sex, 1, new DialogInterface.OnClickListener()
                    {
                        @Override
                        public void onClick(DialogInterface dialog, int which)
                        {
                            Toast.makeText(MainActivity.this, "性别为:" + sex[which], Toast.LENGTH_SHORT).show();
                        }
                    });
                    builder.setPositiveButton("确定", new DialogInterface.OnClickListener()
                    {
                        @Override
                        public void onClick(DialogInterface dialog, int which)
                        {
                            
                        }
                    });
                    builder.setNegativeButton("取消", new DialogInterface.OnClickListener()
                    {
                        @Override
                        public void onClick(DialogInterface dialog, int which)
                        {
                            
                        }
                    });
                    builder.show();
                }
            });
    复制代码

    注意在弹出下拉单选框时,当我们选中一个选项,对话框是不会消失的,我们需要点击action button才能让对话框消失

    4.弹出一个下拉多选框

    复制代码
    button4.setOnClickListener(new OnClickListener()
            {
                @Override
                public void onClick(View v)
                {
                    AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                    builder.setIcon(R.drawable.ic_launcher);
                    builder.setTitle("爱好");
                    final String[] hobbies = {"篮球", "足球", "网球", "斯诺克"};
                    //    设置一个单项选择下拉框
                    /**
                     * 第一个参数指定我们要显示的一组下拉多选框的数据集合
                     * 第二个参数代表哪几个选项被选择,如果是null,则表示一个都不选择,如果希望指定哪一个多选选项框被选择,
                     * 需要传递一个boolean[]数组进去,其长度要和第一个参数的长度相同,例如 {true, false, false, true};
                     * 第三个参数给每一个多选项绑定一个监听器
                     */
                    builder.setMultiChoiceItems(hobbies, null, new DialogInterface.OnMultiChoiceClickListener()
                    {
                        StringBuffer sb = new StringBuffer(100);
                        @Override
                        public void onClick(DialogInterface dialog, int which, boolean isChecked)
                        {
                            if(isChecked)
                            {
                                sb.append(hobbies[which] + ", ");
                            }
                            Toast.makeText(MainActivity.this, "爱好为:" + sb.toString(), Toast.LENGTH_SHORT).show();
                        }
                    });
                    builder.setPositiveButton("确定", new DialogInterface.OnClickListener()
                    {
                        @Override
                        public void onClick(DialogInterface dialog, int which)
                        {
                            
                        }
                    });
                    builder.setNegativeButton("取消", new DialogInterface.OnClickListener()
                    {
                        @Override
                        public void onClick(DialogInterface dialog, int which)
                        {
                            
                        }
                    });
                    builder.show();
                }
            });
    复制代码

    我们看到在设置下拉多选框时使用的是setMultiChoiceItems方法,其各个参数的含义,在上面代码中已经阐述了。
    同样,对于下拉多选框,当我们选中其中一个选项时,对话框是不会消失的,只有点击了action button才会消失

    5.自定义弹出对话框

    对于自定义弹出对话框,我们就需要自己指定一个自定义的布局文件了,我们就给出一个最简单的输入用户名和密码的两个EditText:

    dialog.xml

    复制代码
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
        
        <EditText 
            android:id="@+id/username"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="username"/>
        
        <EditText
            android:id="@+id/password"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/username"
            android:hint="password"
            android:inputType="textPassword"/>
    
    </RelativeLayout>
    复制代码

    关键代码:

    复制代码
    button5.setOnClickListener(new OnClickListener()
            {
                @Override
                public void onClick(View v)
                {
                    AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                    builder.setIcon(R.drawable.ic_launcher);
                    builder.setTitle("请输入用户名和密码");
                    //    通过LayoutInflater来加载一个xml的布局文件作为一个View对象
                    View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.dialog, null);
                    //    设置我们自己定义的布局文件作为弹出框的Content
                    builder.setView(view);
                    
                    final EditText username = (EditText)view.findViewById(R.id.username);
                    final EditText password = (EditText)view.findViewById(R.id.password);
                    
                    builder.setPositiveButton("确定", new DialogInterface.OnClickListener()
                    {
                        @Override
                        public void onClick(DialogInterface dialog, int which)
                        {
                            String a = username.getText().toString().trim();
                            String b = password.getText().toString().trim();
                            //    将输入的用户名和密码打印出来
                            Toast.makeText(MainActivity.this, "用户名: " + a + ", 密码: " + b, Toast.LENGTH_SHORT).show();
                        } 
                    });
                    builder.setNegativeButton("取消", new DialogInterface.OnClickListener()
                    {
                        @Override
                        public void onClick(DialogInterface dialog, int which)
                        {
                            
                        }
                    });
                    builder.show();
                }
            });
    复制代码

    我们看到,通过自定义弹出框,我们首先需要写一个xml的布局文件,然后在里面定义我们的布局,我们不需要在布局文件里定义Button按钮,可以通过 AlertDialog.Builder 来设置 action buttons。

    通过 View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.dialog, null); 我们可以将我们的布局文件加载进来,得到一个View对象,然后通过 AlertDialog.Builder 的setView方法来设置我们的自定义弹出框

    展开全文
  • 1、对话框组成  从MFC角度看,分为两类:  (1)对话框模板资源。对话框模板用于指定对话框的控件及其分布,Windows根据对话框模板来创建并显示对话框  (2)对话框类。用于实现对话框的功能,一般从CDialog类...

    1、对话框组成

        从MFC角度看,分为两类:

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

        (2)对话框类。用于实现对话框的功能,一般从CDialog类、CpropertySheet类、CpropertyPage类派生新类,完成特定的功能。


    2、对话框分类

        对话框分为模态(Model)对话框和非模态(Modeless)对话框

        2.1、模态对话框

        是指这种对话框显示时,其父窗口将失效,只有处理完对话框要求的动作,才会将控制权交换给父窗口。

         <窗口风格>

        虽然可赋予对话框任何风格,但是Windows建议y用DS_MODALFRAME(使得模态对话框具有厚边)、WS_CAPTION及WS_SYSMENU。

         <注意>

         屏幕上显示模态对话框时,它拥有自己的消息循环,因此,所有的输入窗口信息都不能指定为WS_CHILD风格,否则父窗口将失效,子窗口(对话框自己)也会随之失效。


        2.2、非模态对话框

        又称共享式对话框,在打开非模态对话框时,依然可以对其他窗口进行操作。

        <窗口风格>

        大部分都指定WS_POPUP、WS_CAPTION、WS_BORDER、及WS_SYSTEMMENU风格。典型的都包括系统菜单和标题。

        <注意>

         (1)非模态对话框从主程序的消息循环中获得输入消息。如果非模态对话框中有控件存在,而且允许用户用键盘去选择,就必须在消息循环中调用CWnd类(或其继承类)的成员函数IsDialogMessage()来判断输入消息是否要送给非模态对话框,若是,就处理它,否则就按百折不回的途径处理它。

        (2)非模态对话框的建立靠CDialog类的成员函数Create()来完成,关闭则用CWnd类的成员函数DestroyWindow()来实现。


    3、创建对话框的一般步骤

        (1)添加一个新的对话框资源并用对话框编辑器设计对话框(修改ID等属性)

        (2)创建新的对话框类,该类应从CDialog类中派生

        (3)使用ClassWizard为对话框类加入与控件相关联的成员变量

        (4)在对话框类中添加需要的消息处理函数

        (5)创建并显示对话框


    4、模态对话框的创建

        步骤

        步骤1:新建MFC单文档应用程序,工程名为DIalog

        步骤2:在生成标准菜单后main添加一个弹出式菜单“Dialog”,下面有两个菜单命令分别为“Model Dialog”和"Modeless Dialog",它们的ID分别为IDM_MODEDLG和IDM_MODELESSDLG,并在CMainFrame类中添加这两个菜单命令的COMMAND消息处理函数,分别OnModeldlg和OnModelessdlg。如下图。






        步骤3:插入新的对话框资源,并用对话框编辑器设计对话框

            设置新的对话框的ID为IID_MYDLG,标题改为“对话框实例”

            分别在对话框上添加3个静态正文控件和3个编辑框控件,

             3个静态正文控件的Caption属性分别设为“加数”、“被加数”、“两数之和”

             3个编辑框控件的ID分别设为IDC_EDIE_NUM1,IDC_EDIT_NUM2,IDC_EDIT_SUM

             将对话框上的“Cancel”属性改为“相加”





        步骤4:创建新的对话框类,该类应从CDialog派生

        利用ClassWizard创建,这个新类只有两个函数,一个构造函数,一个析构函数


        <注意>现在还没有给这个对话框类添加与控件相关联的数据成员,还不能进行数据交换


        步骤5 :使用ClassWizard为对话框添加与控件相关联的成员变量

             勾选“Control variable”复选框,表示此事添加的是控件成员变量,默认选择的类是CMyDlg(即当前窗口的类),“Contro ID”会显示控件的ID

             在“control ID”选择控件ID;在“Variable name”中输入变量名,m_num1;在“Category”下拉列表选择Value选项;在“Variable type”中选择int选项,其余默认

             可以给这个成员变量一个限定值0-200


        其他的编辑控件也如此设置:




        步骤6:在对话框类中添加需要处理的消息函数

         打开IDD_MYDLG对话框资源,在“相加”控件右击选择“Add Event Handler”命令,选择Message type ,Class List以及handler name后点击OK


        在CMainFrame.cpp文件中添加代码:

        

       
    void CMainFrame::OnModeldlg()
    {
    	// TODO: 在此添加命令处理程序代码
    	CMyDlg m_mydlg;
    	m_mydlg.DoModal();
    }

        <说明>

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

    5、非模态对话框的创建

         <说明>

            非模态对话框必须定义为全局变量或用new操作符产生一个。

        步骤

         步骤1:构造一个对话框类的对象,两种方法
         
         方法1:在框架窗口类的头文件中声明一个对话框的对象成员变量,如下:
       
    
    // MainFrm.h : CMainFrame 类的接口
    //
    
    ........
    #include "MyDlg.h"
    
    class CMainFrame : public CFrameWnd
    {
    	
        .........................
    
    	//创建非模态对话框的变量
    public:
    	CMyDlg m_mydlgless;
    	...................
    };
    
    
    

        <注意>
         这里声明的CMyDlg类的 对象,因此还要在框架窗口类的头文件中加入语句#include “MyDlg.h”,将对话框的头文件包含进来,否则会报错

        方法2:是在框架窗口类中声明一个对话框类的指针变量,然后使用new操作符动态创建。
        例如CMyDlg* pMyDlg = new CMyDlg;不过最后要使用delete才操作符将其释放。

        步骤2:调用CDialog::Create()函数来创建对话框窗口,相应的添加事件响应函数中实现(本例在CMainFrm.cpp)
       
    void CMainFrame::OnModelessdlg()
    {
    	// TODO: 在此添加命令处理程序代码
    	if(!m_mydlgless.m_hWnd)
    	{
    		m_mydlgless.Create(IDD_MYDLG);
    	}
    	m_mydlgless.ShowWindow(SW_SHOW);
    }

        步骤3:调用CWnd::ShowWindow()函数来显示对话框窗口

    展开全文
  • MFC 对话框

    千次阅读 2013-10-30 15:15:47
    MFC 对话框 1、对话框基本要点和生命周期 MFC对话框支持“所见即所得”编程模式。其类型分为模式对话框和非模式对话框对话框由一个rc资源文件描述外观,通过ID与一个CPP类相连接,对话框内的控件使用...

    MFC 对话框

    1、对话框基本要点和生命周期
    MFC对话框支持“所见即所得”编程模式。其类型分为模式对话框和非模式对话框。
    对话框由一个rc资源文件描述外观,通过ID与一个CPP类相连接,对话框内的控件使用基于ID的变量映射通讯。
    模式对话框对象被定义后,通过调用DoModal()函数来显示对话框并进行相关操作,此函数当对话框被关闭时返回。其返回值标明了对话框是点“确定”退 出,还是“取消”。非模式对话框需要与某个View相关联,以便对话框退出时发送消息给对应的Vew进行必要的处理。
    在对话框显示前,系统会调用OnInitDialog() 函数,在这个函数中你可以设置一些控件属性,进行一些初始化工作。比如,设置滚动条的最大最小值,设置List列表的初始值等。其方法是将控件ID作为参 数,调用GetDlgItem函数获得控件的对象的指针(指针类型是CWnd*),然后使用对象提供的函数进行操作。
    ID对于一个组件来说非常重要,通过向导,我们可以将一个变量和一个组件进行关联(映射)来实现数据交换,而这种绑定的关键就是将一个组件的ID与成员变量关联。

    2、数据交换机制
    控件是对话框的重要组成部分,控件的访问可以通过关联变量实现,包括关联数据变量和控制变量。MFC编程中,通过建立类向导中 的操作可以将窗口控件和对应变量绑定,但是代码操作的是变量,用户操作窗口控件如何让他们同步?UpdateData(Bool true|false)函数正是实现这个功能。

    DoDataExchange由框架类调用,用于交换和检验对话框的数据,该函数不直接调用,而是被UpdateData调用。通过update(TRUE)取得控件上的值,处理修改后通过update(FALSE)传回控件。

    UpdateData(TRUE) -- 刷新控件的值到对应的变量
    UpdateData(FALSE) -- 拷贝变量值到控件显示

    UpdataData()---用来刷新对话框

    3、特殊的Radio Button
    Radio Button控件是分组的,同一组的Radio Button只能有一个被选中。这个机制的实现依赖于TAB顺序,在资源视图下按Ctrl-D键将显示对话框的TAB焦点顺序。举一个例子来说明:
    Radio1、Radio2、Radio3是三个不同的Radio Button控件,其焦点顺序为1、2、3。为了实现分组Radio1的Group属性应该为TRUE,其余两个为FALSE。如果又有两个 Radio4、Radio5焦点顺序为6、7。则Radio4的Group属性应为TRUE,Radio4,Radio5被分为一组。
    需要注意的是,Radio以Group属性来分组,为了结束前一个组,你应该将焦点顺序为4、8的控件的Group属性设为TRUE,否则编译器会产生一个警告。

    4、一些技巧
    通过向导,我们可以将一个类成员变量和控件关联以进行数据交换,例如将一个CString类型的变量和Edit控件关联。将一个int变量和一组Radio Button关联。但是,人总有错的时候,当我们修改或需要删除这种关联时,麻烦就来了。
    在我的使用VS2005过程中没有发现提供了删除“已被关联的控件成员变量”的向导,所以我使用的是比较麻烦的手动删除。
    1)在对话框头文件中删除成员变量的定义
    2)在对话框cpp文件中删除构造函数初始化列表中的对应变量的初始化
    3)在对话框cpp文件中,根据变量名删除DoDataExchange函数中的对应语句
    此时,以class view中的向导中,已经可以重新设定控件所关联的成员变量了。

    登录框的制作:
    在显示主窗口之前显示一个模式对话框来提示用户登录一个常用的功能。只需要在PreCreateWindow函数中加入显示对话框的代码就可以完成这个功能。

          有些时候,我们可能需要从一个控件对象来得到它的ID。比如,你的对话框中好几个滚动条,那么这些滚动条的事件都在OnHScroll,OnVScroll中被响应。如何区分是哪个滚动条就需要确定ID。
    在这两个函数中有一个CScrollBar *pScrollBar指针,我们可以通过调用pScrollBar->GetDlgCtrllD()来获得ID,ID是一个整数。

    在对话框编程中往往需要改变某个控件的文字,比如EDIT控件和Static text控件。此时使用SetDlgItemText(int nID,LPCTSTR lpzString)函数比较方便


    MFC窗口销毁过程

    考虑单窗口情况:

    假设自己通过new创建了一个窗口对象pWnd,然后pWnd->Create。则销毁窗口的调用次序:

    1.       手工调用pWnd->DestroyWindow();

    2.       DestroyWindow会发送WM_DESTROY;

    3.       WM_DESTROY对应的消息处理函数是OnDestroy();

    4.       DestroyWindow会发送WM_NCDESTROY;

    5.       WM_NCDESTROY对应的消息处理函数是OnNcDestroy;

    6.       OnNcDestroy最后会调用PostNcDestroy;

    7.       PostNcDestroy经常被用户重载以提供释放内存操作。例如可以使用delete this;

    通过这种方式,窗口对象对应的窗口和窗口对象本身都被释放了。

    如果含有子窗口:

          如果含有子窗口,则调用父窗口的DestroyWindow时,它会向子窗口发送WM_DESTROY和WM_NCDESTROY消息。

          具体调用顺序参考下文的例子。

    DestroyWindowdelete的影响:

          应该说前者对后者并没有什么影响。但经常在DestroyWindow间接导致执行的PostNcDestroy中delete窗口对象指针,即delete this。

          CView::PostNcDestroy中唯一的操作就是delete this;CframeWnd::PostNcDestory也是如此。而默认的CWnd::PostNcDestroy是空操作,CDialog中也没有对其进行重载,即也是空。

    deleteDestroy的影响:

          delete会导致析构函数。CWnd的析构函数中有对DestroyWindow的调用,但必须保证:

    m_hWnd != NULL &&

               this != (CWnd*) & wndTop && this != (CWnd*)&wndBottom &&

               this != (CWnd*)&wndTopMost && this != (CWnd*)&wndNoTopMost。

          Cdialog的析构函数中也有对DestroyWindow的调用,但条件比较松,只需要m_hWnd != NULL。另外Cdialog::DoModal也会调用DestroyWindow。

          CFrameWnd的OnClose中会调用DestroyWindow,但其析构中不会调用DestroyWindow。

          CView的析构也不会调用DestroyWindow。

    一个SDI程序的销毁过程

          有CMainFrame类、CMyView类。并且CMyView有两个子窗口CMyDlg和CmyWnd的实例。

          点击退出按钮,CMainFrame会收到WM_CLOSE消息。CframeWnd(CMainFrame的父类)间接会调用CWnd::DestroyWindow;它首先向CMyView发送WM_DESTORY和WM_NCDESTROY消息,并引发相应的处理函数;然后向CMyDlg发送WM_DESTORY和WM_NCDESTROY消息,并引发相应的处理函数;然后向CMyWnd发送WM_DESTORY和WM_NCDESTROY消息,并引发相应的处理函数。

          具体的执行顺序是:

    1.       调用CMainFrame::DestroyWindow

    2.       CFrameWnd::OnDestroy

    3.       CMyView::OnDestroy

    4.       CmyWnd::OnDestroy

    5.       CmyDlg::OnDestroy

    6.       CmyWnd::PostNcDestroy

    7.       CmyWnd的析构

    8.       CmyDlg::OnDestroy

    9.       CmyDlg的析构

    10.   CMyView::PostNcDestroy

    11.   CmyView的析构

    12.   CMainFrame的析构

    13.   CMainFrame::DestroyWindow退出

    上面情况是假设我们在CmyWnd和CmyDlg的PostNcDestroy中添加了delete this。如果没有添加,则7,10不会执行。

          因为CView::PostNcDestroy中调用了delete this,所以然后会执行CMyView的析构操作。因为CframeWnd::PostNcDestroy中调用了delete this,所以最后执行CMainFrame的析构操作。

          如果自己的CmyDlg和CmyWnd在PostNcDestroy中有delete this;则二者会被析构。否则内存泄漏。当然delete也可以放在CMyView的析构中做,只是不够OO。

    总结

          可以有两种方法销毁窗口对象对应的窗口和释放窗口对象指针。一种是通过DestroyWindow。这是比较好的方法,因为最后MFC会自动相应WM_CLOSE导致CframWnd::DestroyWindow被调用,然后会一次释放所有子窗口的句柄。用户需要做的是在PostNcDestroy中释放堆窗口对象指针。但因为某些对象是在栈中申请的,所以delete this可能出错。这就要保证写程序时自己创建的窗口尽量使用堆申请。

          另一种是delete。Delete一个窗口对象指针有的窗口类(如CWnd,Cdialog)会间接调用DestroyWindow,有的窗口类(如CView,CframeWn)不会调用DestroyWindow。所以要小心应对。

          二者是相互调用的,很繁琐。

     

     

    一个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控件类。

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

    2.       切分窗口类CSplitterWnd

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

    4.       模态对话框类。

     

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

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

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

     

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

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

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

     

    对话框经常被使用,因为对话框可以从模板创建,而对话框模板是可以使用资源编辑器方便地进行编辑的。

    一,模式与非模式对话框
    1.模式对话框
        一个模式对话框是一个有系统菜单、标题栏、边线等的弹出式窗口。在创建对话框时指定WS_POPUP, WS_SYSMENU, WS_CAPTION和 DS_MODALFRAME风格。即使没有指定WS_VISIBLE风格,模

    式对话框也会被显示。创建对话框窗口时,将发送WM_INITDIALOG消息(如果指定对话框的DS_SETFONT风格,还有WM_SETFONT消息)给对话框过程。
        对话框窗口被创建之后,Windows使得它成为一个激活的窗口,它保持激活直到对话框过程调用::EndDialog函数结束对话框的运行或者Windows激活另一个应用程序为止,在激活时,用户

    或者应用程序不可以激活它的所属窗口(Owner window)。从某个窗口创建一个模式对话框时,Windows自动地禁止使用(Disable)这个窗口和它的所有子窗口,直到该模式对话框被关闭和

    销毁。虽然对话框过程可以Enable所属窗口,但是这样做就失去了模式对话框的作用,所以不鼓励这样做。
        Windows创建模式对话框时,给当前捕获鼠标输入的窗口(如果有的话)发送消息WM_CANCLEMODE。收到该消息后,应用程序应该终止鼠标捕获(Release the mouse capture)以便于用户能

    把鼠标移到模式对话框;否则由于Owner窗口被禁止,程序将失去鼠标输入。
       为了处理模式对话框的消息,Windows开始对话框自身的消息循环,暂时控制整个应用程序的消息队列。如果Windows收到一个非对话框消息时,则它把消息派发给适当的窗口处理;如果收

    到了WM_QUIT消息,则把该消息放回应用程序的消息队列里,这样应用程序的主消息循环最终能处理这个消息。
       当应用程序的消息队列为空时,Windows发送WM_ENTERIDLE消息给Owner窗口。在对话框运行时,程序可以使用这个消息进行后台处理,当然应该注意经常让出控制给模式对话框,以便它能

    接收用户输入。如果不希望模式对话框发送 WM_ENTERIDlE消息,则在创建模式对话框时指定DS_NOIDLEMSG风格。
       一个应用程序通过调用::EndDialog函数来销毁一个模式对话框。一般情况下,当用户从系统菜单里选择了关闭(Close)命令或者按下了确认(OK)或取消(CANCLE)按钮,::EndDialog

    被对话框过程所调用。调用::EndDialog时,指定其参数nResult的值,Windows将在销毁对话框窗口后返回这个值,一般,程序通过返回值判断对话框窗口是否完成了任务或者被用户取消。

     

    2. 无模式对话框
       一个无模式对话框是一个有系统菜单、标题栏、边线等的弹出式窗口。在创建对话框模板时指定WS_POPUP、WS_CAPTION、WS_BORDER和WS_SYSMENU风格。如果没有指定WS_VISIBLE风格,无

    模式对话框不会自动地显示出来。一个无模式对话框既不会禁止所属窗口,也不会给它发送消息(WM_ENTERIDlE)。当创建一个无模式对话框时,Windows使它成为活动窗口,但用户或者程序

    可以随时改变和设置活动窗口。如果对话框失去激活,那么即使所属窗口是活动的,在Z轴顺序上,它仍然在所属窗口之上。
       应用程序负责获取和派发输入消息给对话框。大部分应用程序使用主消息循环来处理,但是为了用户可以使用键盘在控制窗口之间移动或者选择控制窗口,应用程序应该调

    用::IsDialogMessage函数。
       这里,顺便解释::IsDialogMessage函数。虽然该函数是为无模式对话框设计的,但是任何包含了控制子窗口的窗口都可以调用它,用来实现类似于对话框的键盘选择操作。
    当::IsDialogMessage处理一个消息时,它检查键盘消息并把它们转换成相应对话框的选择命令。例如,当Tab 键被压下时,下一个或下一组控制被选中,当Down Arrow键按下后,一组控制中

    的下一个控制被选择。::IsDialogMessage完成了所有必要的消息转换和消息派发,所以该函数处理的消息一定不要传递给TranslateMessage和DispatchMessage处理。一个无模式对话框不能

    像模式对话框那样返回一个值给应用程序。但是对话框过程可以使用::SendMessage给所属窗口传递信息。
       在应用程序结束之前,它必须销毁所有的无模式对话框。使用::DestroyWindow销毁一个无模式对话框,不是使用::EndDiaLog。一般来说,对话框过程响应用户输入,如用户选择了“取消

    ”按钮,则调用::DestroyWindow;如果用户没有有关动作,则应用程序必须调用::DestroyWindow。

     

    二,对话框的MFC实现
    1.在MFC中,对话框窗口的功能主要由CWnd和CDialog两个类实现。

    1. class CDialog : public CWnd  
    2. {  
    3.     DECLARE_DYNAMIC(CDialog)  
    4. //初始化函数  
    5. public:  
    6.     virtual BOOL Create(LPCTSTR lpszTemplateName, CWnd* pParentWnd = NULL);  
    7.     virtual BOOL Create(UINT nIDTemplate, CWnd* pParentWnd = NULL);  
    8.     virtual BOOL CreateIndirect(LPCDLGTEMPLATE lpDialogTemplate, CWnd* pParentWnd = NULL,void* lpDialogInit = NULL);  
    9.     virtual BOOL CreateIndirect(HGLOBAL hDialogTemplate, CWnd* pParentWnd = NULL);  
    10.         BOOL InitModalIndirect(LPCDLGTEMPLATE lpDialogTemplate, CWnd* pParentWnd = NULL,void* lpDialogInit = NULL);  
    11.     BOOL InitModalIndirect(HGLOBAL hDialogTemplate, CWnd* pParentWnd = NULL);  
    12. //构造函数  
    13. public:  
    14.         CDialog();  
    15.     explicit CDialog(LPCTSTR lpszTemplateName, CWnd* pParentWnd = NULL);  
    16.     explicit CDialog(UINT nIDTemplate, CWnd* pParentWnd = NULL);  
    17. // 操作函数  
    18. public:  
    19.     // modal processing  
    20.     virtual INT_PTR DoModal();  
    21.   
    22.     // support for passing on tab control - use 'PostMessage' if needed  
    23.     void NextDlgCtrl() const;  
    24.     void PrevDlgCtrl() const;  
    25.     void GotoDlgCtrl(CWnd* pWndCtrl);  
    26.   
    27.     // default button access  
    28.     void SetDefID(UINT nID);  
    29.     DWORD GetDefID() const;  
    30.   
    31.     // termination  
    32.     void EndDialog(int nResult);  
    33.   
    34. //重载函数  
    35. protected:  
    36.     virtual void OnOK();  
    37.     virtual void OnCancel();  
    38. public:  
    39.     virtual BOOL OnInitDialog();  
    40.     virtual void OnSetFont(CFont* pFont);  
    41. public:  
    42.     virtual ~CDialog();  
    43.     virtual BOOL PreTranslateMessage(MSG* pMsg);  
    44.     virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra,AFX_CMDHANDLERINFO* pHandlerInfo);  
    45.     virtual BOOL CheckAutoCenter();  
    46.   
    47. protected:  
    48.     UINT m_nIDHelp;                 // Help ID (0 for none, see HID_BASE_RESOURCE)  
    49.     LPCTSTR m_lpszTemplateName;     // name or MAKEINTRESOURCE  
    50.     HGLOBAL m_hDialogTemplate;      // indirect (m_lpDialogTemplate == NULL)  
    51.     LPCDLGTEMPLATE m_lpDialogTemplate;  // indirect if (m_lpszTemplateName == NULL)  
    52.     void* m_lpDialogInit;           // DLGINIT resource data  
    53.     CWnd* m_pParentWnd;             // parent/owner window  
    54.     HWND m_hWndTop;                 // top level parent window (may be disabled)  
    55.   
    56.         virtual void PreInitDialog();  
    57.     HWND PreModal();  
    58.     void PostModal();  
    59. };  

    2.模式对话框的实现
       在Win32 SDK编程下的模式对话框使用了Windows提供给对话框窗口的窗口过程和自己的对话框过程,对话框过程将被窗口过程调用。但在MFC下,所有的窗口类都使用了同一个窗口过程,

    CDialog也不例外。CDialog对象在创建Windows对话框时,采用了类似于CWnd的创建函数过程,采用子类化的手段将Windows提供给对话框的窗口过程取代为AfxWndProc或者AfxBaseWndProc,

    同时提供了对话框过程AfxDlgProc。那么,这些“过程”是如何实现或者协调的呢?下文将予以分析。

    I.MFC对话框过程
    //MFC对话框过程AfxDlgProc的原型和实现如下:

    1. BOOL CALLBACK AfxDlgProc(HWND hWnd,UINT message, PARAM, LPARAM)  
    2. {  
    3.     if (message == WM_INITDIALOG)  
    4.     {  
    5.          //处理WM_INITDIALOG消息  
    6.          CDialog* pDlg = DYNAMIC_DOWNCAST(CDialog,CWnd::FromHandlePermanent(hWnd));  
    7.          if (pDlg != NULL)  
    8.              return pDlg->OnInitDialog();  
    9.          else  
    10.              return 1;  
    11.     }  
    12.   
    13.     return 0;  
    14. }  

       由上可以看出,MFC的对话框函数AfxDlgProc仅处理消息WM_INITDIALOG,其他都留给对话框窗口过程处理。因此,它不同于SDK编程的对话框过程。程序员在SDK的对话框过程处理消息和事

    件,实现自己的对话框功能。AfxDlgProc处理WM_INITDIALOG消息时调用虚拟函数OnInitDialog,给程序员一个机会处理对话框的初始化。

     

    II.模式对话框窗口过程
       AfxWndProc是所有的MFC窗口类使用的窗口过程,它取代了模式对话框原来的窗口过程(Windows提供),那么,MFC如何完成Win32下对话框窗口的功能呢?考查模式对话框的创建过程。

    CDialog::DoModal用来创建模式对话框窗口并执行有关任务,和DoModal相关的是MFC内部使用的成员函数CDialog::PreModal和CDialog::PostModal。下面分别讨论它们的实现。

    1. HWND CDialog::PreModal()  
    2. {  
    3.     ASSERT(m_hWnd == NULL);  
    4.   
    5.     // allow OLE servers to disable themselves  
    6.     AfxGetApp()->EnableModeless(FALSE);  
    7.   
    8.     // 得到父窗口  
    9.     CWnd* pWnd = CWnd::GetSafeOwner(m_pParentWnd, &m_hWndTop);  
    10.   
    11.     // 如同CWnd处理其他窗口的创建,设置一个窗口创建HOOK  
    12.     AfxHookWindowCreate(this);  
    13.   
    14.     //返回父窗口的句柄  
    15.     return pWnd->GetSafeHwnd();  
    16. }  
    17.   
    18. void CDialog::PostModal()  
    19. {  
    20.     //取消窗口创建前链接的HOOK  
    21.     AfxUnhookWindowCreate(); // just in case  
    22.   
    23.     //MFC对话框对象和对应的Windows对话框窗口分离  
    24.     Detach(); // just in case  
    25.   
    26.     // m_hWndTop是当前对话框的父窗口或所属窗口,则恢复它  
    27.     if (::IsWindow(m_hWndTop))  
    28.         ::EnableWindow(m_hWndTop, TRUE);  
    29.     m_hWndTop = NULL;  
    30.     AfxGetApp()->EnableModeless(TRUE);  
    31. }  
    32.   
    33. int CDialog::DoModal()  
    34. {  
    35.     ASSERT(m_lpszTemplateName != NULL || m_hDialogTemplate != NULL || m_lpDialogTemplate != NULL);  
    36.   
    37.     //加载对话框资源  
    38.     LPCDLGTEMPLATE lpDialogTemplate = m_lpDialogTemplate;  
    39.     HGLOBAL hDialogTemplate = m_hDialogTemplate;  
    40.     HINSTANCE hInst = AfxGetResourceHandle();  
    41.     if (m_lpszTemplateName != NULL)  
    42.     {  
    43.         hInst = AfxFindResourceHandle(m_lpszTemplateName, RT_DIALOG);  
    44.         HRSRC hResource =::FindResource(hInst, m_lpszTemplateName, RT_DIALOG);  
    45.         hDialogTemplate = LoadResource(hInst, hResource);  
    46.     }  
    47.   
    48.     //锁定加载的资源  
    49.     if (hDialogTemplate != NULL)  
    50.         lpDialogTemplate = (LPCDLGTEMPLATE)LockResource(hDialogTemplate);  
    51.     if (lpDialogTemplate == NULL)  
    52.         return -1;  
    53.   
    54.     //创建对话框前禁止父窗口,为此要调用PreModal得到父窗口句柄  
    55.     HWND hWndParent = PreModal();  
    56.     AfxUnhookWindowCreate();  
    57.     CWnd* pParentWnd = CWnd::FromHandle(hWndParent);  
    58.     BOOL bEnableParent = FALSE;  
    59.     if (hWndParent != NULL && ::IsWindowEnabled(hWndParent))  
    60.     {  
    61.         ::EnableWindow(hWndParent, FALSE);  
    62.         bEnableParent = TRUE;  
    63.     }  
    64.   
    65.     //创建对话框,注意是无模式对话框  
    66.     TRY  
    67.     {  
    68.         //链接一个HOOK到HOOK链以处理窗口创建,  
    69.         AfxHookWindowCreate(this);  
    70.   
    71.         //CreateDlgIndirect间接调用::CreateDlgIndirect,最终调用了::CreateWindowEX来创建对话框窗口。  
    72.         //HOOK过程_AfxCbtFilterHook用子类化的方法取代原来的窗口过程为AfxWndProc。  
    73.         if (CreateDlgIndirect(lpDialogTemplate, CWnd::FromHandle(hWndParent), hInst))  
    74.         {  
    75.             if (m_nFlags & WF_CONTINUEMODAL)  
    76.             {  
    77.                 // enter modal loop  
    78.                 DWORD dwFlags = MLF_SHOWONIDLE;  
    79.                 //RunModalLoop接管整个应用程序的消息处理  
    80.                 if (GetStyle() & DS_NOIDLEMSG)  
    81.                     dwFlags |= MLF_NOIDLEMSG;  
    82.                 VERIFY(RunModalLoop(dwFlags) == m_nModalResult);  
    83.             }  
    84.   
    85.             // hide the window before enabling the parent, etc.  
    86.             if (m_hWnd != NULL)  
    87.                 SetWindowPos(NULL, 0, 0, 0, 0, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);  
    88.         }  
    89.     }  
    90.   
    91.     CATCH_ALL(e)  
    92.     {  
    93.         DELETE_EXCEPTION(e);  
    94.         m_nModalResult = -1;  
    95.     }  
    96.     END_CATCH_ALL  
    97.   
    98.     //Enable并且激活父窗口  
    99.     if (bEnableParent)  
    100.         ::EnableWindow(hWndParent, TRUE);  
    101.     if (hWndParent != NULL && ::GetActiveWindow() == m_hWnd)  
    102.         ::SetActiveWindow(hWndParent);  
    103.     //::EndDialog仅仅关闭了窗口,现在销毁窗口  
    104.     DestroyWindow();  
    105.     PostModal();  
    106.   
    107.     // 必要的话,解锁/释放资源  
    108.     if (m_lpszTemplateName != NULL || m_hDialogTemplate != NULL)  
    109.         UnlockResource(hDialogTemplate);  
    110.     if (m_lpszTemplateName != NULL)  
    111.         FreeResource(hDialogTemplate);  
    112.   
    113.     return m_nModalResult;  
    114. }  

    III.使用原对话框窗口过程作消息的缺省处理
       对话框的消息处理过程和其他窗口并没有什么不同。这里主要分析的是如何把一些消息传递给对话框原窗口过程处理。下面,通过解释MFC对WM_INITDIALOG消息的处理来解释MFC窗口过程

    和原对话框窗口过程的关系及其协调作用。MFC提供了WM_INITDIALOG消息的处理函数CDialog::HandleInitDialog,WM_INITDIALOG消息按照标准Windows的处理送给HandleInitDialog处理。

    HandleInitDialog调用缺省处理过程Default,导致CWnd的Default函数被调用。CWnd::Default的实现如下:

    1. LRESULT CWnd::Default()  
    2. {  
    3. // call DefWindowProc with the last message  
    4. _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();  
    5. return DefWindowProc(pThreadState->m_lastSentMsg.message,pThreadState->m_lastSentMsg.wParam,pThreadState->m_lastSentMsg.lParam);  
    6. }  

    顺便指出,从Default的实现可以看出线程状态的一个用途:它把本线程最新收到和处理的消息记录在成员变量m_lastSentMsg中。在Default的实现中,CWnd的DefWindowsProc被调用,其

    实现如下:

    1. LRESULT CWnd::DefWindowProc(UINT nMsg,WPARAM wParam, LPARAM lParam)  
    2. {  
    3.     //若“窗口超类(SuperClass)”的窗口过程m_pfnSuper非空,则调用它  
    4.     if (m_pfnSuper != NULL)  
    5.         return ::CallWindowProc(m_pfnSuper, m_hWnd, nMsg, wParam, lParam);  
    6.   
    7.     //在MFC中,GetSuperWndProcAddr的作用就是返回m_pfnSuper,为什么还  
    8.     //要再次调用呢?因为虽然该函数现在是Obsolete,但原来曾经是有用的。  
    9.     //如果返回非空,就调用该窗口过程进行处理,否则,由Windows进行缺省处理。  
    10.     WNDPROC pfnWndProc;  
    11.     if ((pfnWndProc = *GetSuperWndProcAddr()) == NULL)  
    12.         return ::DefWindowProc(m_hWnd, nMsg, wParam, lParam);  
    13.     else  
    14.         return ::CallWindowProc(pfnWndProc, m_hWnd, nMsg, wParam, lParam);  
    15. }  

     

    综合上述分析,HandleInitDialog最终调用了窗口过程m_pfnSuper,即Windows提供给“对话框窗口类”的窗口过程,于是该窗口过程调用了对话框过程AfxDlgProc,导致虚拟函数

    OnInitDialog被调用。OnInitDialog的MFC缺省实现主要完成三件事情:
    调用ExecInitDialog初始化对话框中的控制;调用UpdateData初始化对话框控制中的数据;确定是否显示帮助按钮。所以,程序员覆盖该函数时,一定要调用基类的实现。
    MFC采用子类化的方法取代了对话框的窗口过程,原来SDK下对话框过程要处理的东西大部分转移给MFC窗口过程处理,如处理控制窗口的控制通知消息等。如果不能处理或者必须借助于原来的

    窗口过程的,则通过缺省处理函数Default传递给原来的窗口过程处理,如同这里对WM_INITDIALOG的处理一样。

     

    IV.Dialog命令消息和控制通知消息的处理
    通过覆盖CWnd的命令消息发送函数OnCmdMsg,CDialog实现了自己的命令消息发送路径

    1. BOOL CDialog::OnCmdMsg(UINT nID, int nCode, void* pExtra,AFX_CMDHANDLERINFO* pHandlerInfo)  
    2. {  
    3.     //首先,让对话框窗口自己或者基类处理  
    4.     if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))  
    5.         return TRUE;  
    6.   
    7.     //如果还未处理,且是控制通知消息或者状态更新消息或者系统命令则停止进一步的发送  
    8.     if ((nCode != CN_COMMAND && nCode != CN_UPDATE_COMMAND_UI) ||!IS_COMMAND_ID(nID) || nID >= 0xf000)  
    9.         return FALSE; // not routed any further  
    10.   
    11.     //尝试给父窗口处理  
    12.     CWnd* pOwner = GetParent();  
    13.     if (pOwner != NULL)  
    14.     {  
    15.         ASSERT(pOwner != this);  
    16.         if (pOwner->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))  
    17.         return TRUE;  
    18.     }  
    19.   
    20.     // 最后,给当前线程对象处理  
    21.     CWinThread* pThread = AfxGetThread();  
    22.     if (pThread != NULL)  
    23.     {  
    24.         if (pThread->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))  
    25.         return TRUE;  
    26.     }  
    27.   
    28.     return FALSE;  
    29. }  

    从上述实现可以看出,CDialog处理命令消息遵循如下顺序:对话框自身→父窗口→线程对象
    例如,模式对话框产生的WM_ENTERIDLE消息就发送给父窗口处理。
    CDialog::OnCmdMsg不仅适用于模式对话框,也适用于无模式对话框。

     

    V.消息预处理和Dialog消息
       另外,对话框窗口的消息处理还有一个特点,就是增加了对Dialog消息的处理,如同在介绍::IsDialogMessage函数时所述。如果是 Dialog消息,MFC框架将不会让它进入下一步的消息循

    环。为此,MFC覆盖了CDialog的虚拟函数PreTranslateMessage,该函数的实现如下:

    1. BOOL CDialog::PreTranslateMessage(MSG* pMsg)  
    2. {  
    3.     ASSERT(m_hWnd != NULL);  
    4.   
    5.     //过滤tooltip messages  
    6.     if (CWnd::PreTranslateMessage(pMsg))  
    7.         return TRUE;  
    8.   
    9.     //在Shift+F1帮助模式下,不转换Dialog messages  
    10.     CFrameWnd* pFrameWnd = GetTopLevelFrame();  
    11.     if (pFrameWnd != NULL && pFrameWnd->m_bHelpMode)  
    12.         return FALSE;  
    13.   
    14.     //处理Escape键按下的消息  
    15.     if (pMsg->message == WM_KEYDOWN &&(pMsg->wParam == VK_ESCAPE || pMsg->wParam == VK_CANCEL) &&  
    16.     (::GetWindowLong(pMsg->hwnd, GWL_STYLE) & ES_MULTILINE) &&_AfxCompareClassName(pMsg->hwnd, _T("Edit")))  
    17.     {  
    18.         HWND hItem = ::GetDlgItem(m_hWnd, IDCANCEL);  
    19.         if (hItem == NULL || ::IsWindowEnabled(hItem))  
    20.         {  
    21.             SendMessage(WM_COMMAND, IDCANCEL, 0);  
    22.             return TRUE;  
    23.         }  
    24.   
    25.     }  
    26.   
    27.     // 过滤来自控制该对话框子窗口的送给该对话框的Dialog消息  
    28.     return PreTranslateInput(pMsg);  
    29.   
    30. }  

    用到的函数:

    1. BOOL CWnd::PreTranslateMessage(MSG* pMsg)  
    2. {  
    3.     // handle tooltip messages (some messages cancel, some may cause it to popup)  
    4.     AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE();  
    5.     if (pModuleState->m_pfnFilterToolTipMessage != NULL)  
    6.         (*pModuleState->m_pfnFilterToolTipMessage)(pMsg, this);  
    7.   
    8.     // no default processing  
    9.     return FALSE;  
    10. }  
    11.   
    12. BOOL CWnd::PreTranslateInput(LPMSG lpMsg)  
    13. {  
    14.     ASSERT(::IsWindow(m_hWnd));  
    15.   
    16.     // don't translate non-input events  
    17.     if ((lpMsg->message < WM_KEYFIRST || lpMsg->message > WM_KEYLAST) &&  
    18.         (lpMsg->message < WM_MOUSEFIRST || lpMsg->message > AFX_WM_MOUSELAST))  
    19.         return FALSE;  
    20.   
    21.     return IsDialogMessage(lpMsg);  
    22. }  
    23.   
    24. BOOL CWnd::IsDialogMessage(LPMSG lpMsg)  
    25. {  
    26.     ASSERT(::IsWindow(m_hWnd));  
    27.   
    28.     if (m_nFlags & WF_OLECTLCONTAINER)  
    29.         return afxOccManager->IsDialogMessage(this, lpMsg);  
    30.     else  
    31.         return ::IsDialogMessage(m_hWnd, lpMsg);  
    32. }  

    根据IsDialogMessage(),对话框无法处理WM_KEYUP等消息,我们可以重载PreTranslateMessage()。PreTranslateMessage的实现不仅用于模式对话框,而且用于无模式对话框。

     

    VI.模式对话框的消息循环
        从DoModal的实现可以看出,DoModal调用CreateDlgIndirect创建的是无模式对话框,MFC如何来接管和控制应用程序的消息队列,实现一个模式对话框的功能呢?
    CDialog调用了RunModalLoop来实现模式窗口的消息循环。RunModalLoop是CWnd的成员函数,它和相关函数的实现如下:

    1. int CWnd::RunModalLoop(DWORD dwFlags)  
    2. {  
    3.   
    4.     ASSERT(::IsWindow(m_hWnd));   
    5.     BOOL bIdle = TRUE;  
    6.     LONG lIdleCount = 0;  
    7.     BOOL bShowIdle = (dwFlags & MLF_SHOWONIDLE) &&!(GetStyle() & WS_VISIBLE);  
    8.     HWND hWndParent = ::GetParent(m_hWnd);  
    9.     m_nFlags |= (WF_MODALLOOP|WF_CONTINUEMODAL);  
    10.     MSG* pMsg = &AfxGetThread()->m_msgCur;  
    11.   
    12.     //获取和派发消息直到模式状态结束  
    13.     for (;;)  
    14.     {  
    15.         ASSERT(ContinueModal());/  
    16.         //第一阶段,判断是否可以进行Idle处理  
    17.         while (bIdle &&!::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE))  
    18.         {  
    19.             ASSERT(ContinueModal());  
    20.   
    21.             //必要的话,当Idle时显示对话框窗口  
    22.             if (bShowIdle)  
    23.             {  
    24.                 ShowWindow(SW_SHOWNORMAL);  
    25.                 UpdateWindow();  
    26.                 bShowIdle = FALSE;  
    27.             }  
    28.   
    29.             //进行Idle处理,必要的话发送WM_ENTERIDLE消息给父窗口  
    30.             if (!(dwFlags & MLF_NOIDLEMSG) &&hWndParent != NULL && lIdleCount == 0)  
    31.             {  
    32.                 ::SendMessage(hWndParent, WM_ENTERIDLE,  
    33.                 MSGF_DIALOGBOX, (LPARAM)m_hWnd);  
    34.             }  
    35.   
    36.             //必要的话发送WM_KICKIDLE消息给父窗口  
    37.             if ((dwFlags & MLF_NOKICKIDLE) ||!SendMessage(WM_KICKIDLE, MSGF_DIALOGBOX, lIdleCount++))  
    38.             {  
    39.                 //终止Idle处理  
    40.                 bIdle = FALSE;  
    41.             }  
    42.         }  
    43.   
    44.         //第二阶段,发送消息  
    45.         do  
    46.         {  
    47.             ASSERT(ContinueModal());  
    48.             // 若是WM_QUIT消息,则发送该消息到消息队列,返回;否则发送消息。  
    49.             if (!AfxGetThread()->PumpMessage())  
    50.             {  
    51.                 AfxPostQuitMessage(0);  
    52.                 return -1;  
    53.             }  
    54.   
    55.             //必要的话,显示对话框窗口  
    56.             if (bShowIdle &&(pMsg->message == 0x118 || pMsg->message == WM_SYSKEYDOWN))  
    57.             {  
    58.                 ShowWindow(SW_SHOWNORMAL);  
    59.                 UpdateWindow();  
    60.                 bShowIdle = FALSE;  
    61.             }  
    62.   
    63.             if (!ContinueModal())  
    64.             goto ExitModal;  
    65.   
    66.             //在派发了“正常 ”消息后,重新开始Idle处理  
    67.             if (AfxGetThread()->IsIdleMessage(pMsg))  
    68.             {  
    69.                 bIdle = TRUE;  
    70.                 lIdleCount = 0;  
    71.             }  
    72.         } while (::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE));  
    73.     }  
    74.   
    75.     ExitModal:  
    76.     m_nFlags &= ~(WF_MODALLOOP|WF_CONTINUEMODAL);  
    77.     return m_nModalResult;  
    78. }  

         和CWinThread::Run的处理过程比较,RunModalLoop也分两个阶段进行处理。这里不同于Run的Idle处理,RunModalLoop是给父窗口发送WM_ENTERIDLE消息(如果需要的话);另外,当前对

    话框的父窗口被Disabled,是不接收用户消息的。RunModalLoop是一个实现自己的消息循环的示例,消息循环的条件是模式化状态没有结束。实现线程自己的消息循环见8.5.6节。当用户按下

    按钮“取消”、“确定”时,将导致RunModalLoop退出消息循环,结束对话框模式状态,并调用::EndDialog关闭窗口。
    OnOk、OnCancle、EndDialog都可以用来关闭对话框窗口。其中:
    OnOk首先进行数据交换,获取对话框中各个控制子窗口的数据,然后调用EndDialog结束对话框。
    OnCancle直接EndDialog结束对话框。

     

    三.数据交换
       对话框数据交换指以下两种动作,或者是把内存数据写入对应的控制窗口,或者是从控制窗口读取数据并保存到内存变量中。MFC为了简化这些操作,以CDataExchange类和一些数据交换函

    数为基础,提供了一套数据交换和校验的机制。
    相应的DoDataExchange的实现如下:

    1. void CExDialog::DoDataExchange(CDataExchange* pDX)  
    2. {  
    3.     CDialog::DoDataExchange(pDX);  
    4.     DDX_Control(pDX, IDC_NAME, m_name);  
    5.     DDX_Text(pDX, IDC_AGE, m_nAge);  
    6.     DDV_MinMaxInt(pDX, m_nAge, 1, 100);  
    7. }  

    DDX_ Control表示把IDC_NAME子窗口的内容传输到窗口m_name,或者相反。
    DDX_ Text表示把IDC_AGE子窗口的内容按整数类型保存到m_nAge,或者相反。
    DDV_MinMaxInt表示m_nAge应该在1和100之间取值

     

    1.CDataExchange类

    1. class CDataExchange  
    2. {  
    3. public:  
    4.   
    5.     BOOL m_bSaveAndValidate; // TRUE 则 保存和验证数据  
    6.     CWnd* m_pDlgWnd; // 指向一个对话框  
    7.   
    8.     HWND PrepareCtrl(int nIDC); //返回指定ID的控制窗口的句柄  
    9.     HWND PrepareEditCtrl(int nIDC); //返回指定ID的编辑控制窗口句柄  
    10.     void Fail(); // 用来扔出例外  
    11.   
    12.     CDataExchange(CWnd* pDlgWnd, BOOL bSaveAndValidate);  
    13.     HWND m_hWndLastControl; // last control used (for validation)  
    14.     BOOL m_bEditLastControl; // last control was an edit item  
    15. };  

    DoDataExchange类似于Serialize函数,CDataExchange类似于 CArchive。CDataExchange使用成员变量m_pDlgWnd保存要进行数据交换的对话框,使用成员变量 m_bSaveAndValidate指示数

    据传输的方向,如果该变量真,则从控制窗口读取数据到成员变量,如果假,则从成员变量写数据到控制窗口。
    在构造一个CDataExchange对象时,将保存有关信息在对象的成员变量中。构造函数如下:

    1. CDataExchange::CDataExchange(CWnd* pDlgWnd, BOOL bSaveAndValidate)  
    2. {  
    3.     ASSERT_VALID(pDlgWnd);  
    4.     m_bSaveAndValidate = bSaveAndValidate;  
    5.     m_pDlgWnd = pDlgWnd;  
    6.     m_hWndLastControl = NULL;  
    7. }  

     

    2.数据交换和验证函数
    在进行数据交换或者验证时,首先使用PrePareCtrl或者PrePareEditCtrl得到控制窗口的句柄,然后使用::GetWindowsText从控制窗口读取数据,或者使用::SetWindowsText写入数据到控制

    窗口。下面讨论几个例子:

    1. static void AFX_CDECL DDX_TextWithFormat(CDataExchange* pDX,int nIDC,LPCTSTR lpszFormat, UINT nIDPrompt, ...)  
    2. {  
    3.     va_list pData; //用来处理个数可以变化的参数  
    4.     va_start(pData, nIDPrompt);//得到参数  
    5.   
    6.     //得到编辑框的句柄  
    7.     HWND hWndCtrl = pDX->PrepareEditCtrl(nIDC);  
    8.     TCHAR szT[32];  
    9.     if (pDX->m_bSaveAndValidate) //TRUE,从编辑框读出数据  
    10.     {  
    11.         //从编辑框得到内容  
    12.         ::GetWindowText(hWndCtrl, szT, _countof(szT));  
    13.         //转换编辑框内容为指定的格式,支持“ %d, %u, %ld, %lu”  
    14.         if (!AfxSimpleScanf(szT, lpszFormat, pData))  
    15.         {  
    16.             AfxMessageBox(nIDPrompt);  
    17.             pDX->Fail(); //数据交换失败  
    18.         }  
    19.     }  
    20.     else //FALSE,写入数据到编辑框  
    21.     {  
    22.         //把要写的内容转换成指定格式,不支持浮点运算  
    23.         wvsprintf(szT, lpszFormat, pData);  
    24.         //设置编辑框的内容  
    25.         AfxSetWindowText(hWndCtrl, szT);  
    26.     }  
    27.   
    28.     va_end(pData);//结束参数分析  
    29. }  
    30.   
    31. void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, long& value)  
    32. {  
    33.     if (pDX->m_bSaveAndValidate)  
    34.         DDX_TextWithFormat(pDX, nIDC, _T("%ld"), AFX_IDP_PARSE_INT, &value);  
    35.     else  
    36.         DDX_TextWithFormat(pDX, nIDC, _T("%ld"), AFX_IDP_PARSE_INT, value);  
    37. }  

     

    3.UpdateData函数

    1. BOOL CWnd::UpdateData(BOOL bSaveAndValidate)  
    2. {  
    3.     ASSERT(::IsWindow(m_hWnd));   
    4.   
    5.     //创建CDataChange对象  
    6.     CDataExchange dx(this, bSaveAndValidate);  
    7.     DoDataExchange(&dx);  
    8.     //.................  
    9.     return bOK;  
    10. }  

     

    四,模式对话框的使用
    CFormView是MFC使用无模式对话框的一个典型例子。CFormView是基于对话框模板创建的视,它的直接基类是CSrcollView,CSrcollView的直接基类才是CView。
    这样不做详细介绍了

     

    展开全文
  • MFC对话框

    2013-06-30 10:19:32
    1、对话框基本要点和生命周期 MFC对话框支持“所见即所得”编程模式。其类型分为模式对话框和非模式对话框对话框由一个rc资源文件描述外观,通过ID与一个CPP类相连接,对话框内的控件使用基于ID的变量映射通讯...
  • Android 开发过程中经常需要用户自定义的对话框。这种对话框需要支持在对话框里面输入的内容在对话框关闭后传递给当前所在的Activity。...用户自定义对话框的例子包含已下几个部分:    1)Act
  • symbian对话框

    2010-08-04 14:55:00
    <br />Symbian对话框   Symbian OS的对话框首先需要在资源文件中定义(对话框所容纳的控件也需要在资源文件中定义...对话框构架管理器负责管理对话框的所有行为,包括布局,外观,以及用户输入。典型的
  • Symbian对话框

    千次阅读 2009-11-05 15:03:00
    Symbian对话框Symbian OS的对话框首先需要在资源文件中定义(对话框所容纳的控件也需要在资源文件中定义),然后在程序中建立对话框对象并使用资源文件中的对话框id进行初始化,最后执行对话框并显示相关内容。...
  • AlertDialog对话框

    千次阅读 2014-02-18 22:37:45
    ②区域2那里是AlertDialog对话框的content部分,在这里我们可以设置一些message信息,或者是定义一组选择框,还可以定义我们自己的布局弹出框。 ③区域3那里使我们的Action Buttons部分,这里我们可以定义我们...
  • 选项卡对话框的例子展示了如何使用QTabWidget类构建一个选项卡对话框。...选项卡对话框的例子由一个TabDialog类和两个普通的按钮组成。TabDialog类提供了3个选项卡和每个特定文件的包含信息;两个按钮用于接收或拒...
  • MFC对话框整合

    千次阅读 2016-11-24 19:20:34
    创建对话框主要分两大步,第一,创建对话框资源,主要包括创建新的对话框模板、设置对话框属性和为对话框添加各种控件;第二,生成对话框类,主要包括新建对话框类、添加控件变量和控件的消息处理函数等。 创建...
  • javascript对话框

    2017-11-22 20:14:00
    使用对话框对用户的体验进行引导是任何用户界面的一个重要组成部分(对对话框的过度使用会使人分心,所以请明智地使用对话框)。这里提供的支持使您可以不受所使用的技术的限制构建希望的用户界面。我还希望我已经...
  • Android 对话框相关总结

    千次阅读 2017-08-22 21:56:52
    1.纯粹的 AlertDialog以及设置位置2. ProgressDialog和自定义对话框3. 自定义对话框(考虑wrap_content时的大小问题)4.单选列表对话框5.多选列表对话框6.全屏的对话框
  • 第12章 对话框

    2018-08-22 20:22:38
    对话框的一般形式是包含多种子窗口控件的弹出式窗口,这些控件的大小和位置在程序资源描述文件的「对话框模板」中指定。虽然程序写作者能够「手工」定义对话框模板,但是现在通常是在Visual C++ Developer Studio中...
  • 模态对话框和非模态对话框

    千次阅读 2007-11-22 22:03:00
    对话框分为模态对话框和非模态对话框,创建模态对话框用DialogBox(),创建非模态对话框用CreateDialog(),一个对话框通常都应当有一个父窗口,对话框的定位以父窗口的坐标系统为基准,父窗口关闭对话框也随即关闭。...
  • android dialog对话框

    2015-10-09 10:10:20
    在Android开发当中,在界面上弹出一个Dialog对话框使我们经常需要做的,本篇随笔将详细的讲解Dialog对话框这个概念,包括定义不同样式的对话框。 一、Dialog 我们首先来看看android官方文档对Dialog的介绍 A dialog...
  • 首先观察Filter属性的组成部分:“Word文件|*.doc”,前面的“Word文件”成为标签,是一个可读的字符串,可以自定定义,“|*.doc”是筛选器,表示筛选文件夹中后缀名为.doc的文件,“*”表示匹配Word文件名称的字符串...
  • wix 自定义对话框

    千次阅读 2012-09-14 10:36:18
     开始之前,我们先看看标准的WixUI_Mondo UI包含哪些对话框: 1: BrowseDlg 2: CustomizeDlg 3: DiskCostDlg 4: LicenseAgreementDlg 5: SetupTypeDlg 6: WelcomeDlg  这些标准对话框...
  • 文件对话框 文件对话框是一个从文件中选择文件的界面,也就是弹出一保存或者打开文件的对话框,想要真正打开或保存文件需要用到输入输出流 JFileChoose类可以创建文件对话框//创建的是有模式不可见得对话框//调用下面...
  • Swing 之 对话框

    万次阅读 2012-01-14 14:45:42
     大多数对话框目前的错误消息或警告用户,但对话框可以展示图像,目录树,或几乎任何兼容主要的Swing应用程序,管理。 为方便起见,几个Swing组件类可以直接实例化并显示对话框 。 要创建简单,标准的对话框,您...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 51,482
精华内容 20,592
关键字:

对话框的组成部分包括