MFC 对话框与控件的使用

一、实验目的:

1、 熟悉 Windows 对话框应用程序开发的基本过程;

2、 学习标准控件的使用;

3、 学些 CommonDialog ActiveX 控件的使用;

4、 练习较复杂的交互式操作的控制流程;

5、 练习文件输入/输出流的使用。

二、实验内容

实现一个具有一定实用价值的通讯录程序设计。界面如图所示

三、 实验主要原理说明

①界面的基本设计

  1. 开发windows应用程序,往往是从界面开发开始。在资源视图中把对话框调出,把对话框中自带的控件删掉,放入自己所需要的控件。

  2. 先设立两个组合框Group Box(Group Box组合框会将组合框内的控件标识为一组控件来使用:只能选择其中的一个控件),将两个组合框属性中的描述文字分别改为:联系人、地址簿,便于用户区分。在联系人组合框中放入两组Static Text和Eidt Control分别表示姓名和电话号码,并再放一组Static Text和Combo Box(组合框控件:由一个文本输入控件和一个下拉菜单组成。用户可以从一个预先定义的列表里选择一个选项,同时也可以直接在文本框里输入文本。)表示用户可以选择的群组。Ctrl选中联系人里面的控件进行位置调整。

  3. 在对话框中放入六个Button控件,Ctrl选中这六个控件进行位置调整,并在属性中对这六个按钮的描述文字依次改成载入、保存、退出、新建、更新、删除,供用户进行使用。

  4. 在地址簿这个组合框中放入List Control控件(相当于一个表格),调整大小,在该控件的属性栏中,单选设置为True(表示用户只能选择该列表的一个元素)、视图设为Report(以报表的形式呈现给用户)、文本右对齐设为True、始终显示选定的内容设置为True、

  5. 为所有用户需要的控件进行ID修改,以便后面类向导给控件绑定变量时进行区分:

    控件ID
    姓名Edit ControlIDC_EDITNAME
    号码Edit ControlIDC_EDITNUMBER
    群组Combo BoxIDC_COMBOGROUP
    载入ButtonIDC_BUTTONLOAD
    保存 ButtonIDC_BUTTONSAVE
    退出 ButtonIDC_BUTTONEXIT
    新建 ButtonIDC_BUTTONNEW
    更新 ButtonIDC_BUTTONUPDATA
    删除 ButtonIDC_BUTTONDELETE
    地址簿List ControlIDC_LISTCONTROL
  6. 插入ActiveX控件:Microsoft Common Dialog Control version6.0(这是一个共用控件,可以调用字体格式选择对话框,打开文件对话框,打印对话框,保存对话框,颜色选择框),该对话框对于用户来说是不可见的。在属性一栏中,将Filter设置为:文本文件(.txt)|.txt|所有文件(.)|.* 。下表是通用对话框的使用方法:

    *方法**所显示的对话框*
    *ShowOpen*显示“打开”对话框
    *ShowSave*显示“另存为”对话框
    *ShowColor*显示“颜色”对话框
    *ShowFont*显示“字体”对话框
    *ShowPrinter*显示“打印”或“打印选项”对话框
    *ShowHelp*调用 Windows 帮助引擎
  7. 打开类向导,添加成员变量

    控件ID控件类型类别名称访问变量类型
    IDC_COMBOGROUPCOMBOBOXm_sGrouppublicCString
    IDC_COMBOGROUPCOMBOBOX控件m_ComboGrouppublicCComboBox
    IDC_COMMONDIALOG1OCX控件FileDialogpublicCOMMONDIALOG1
    IDC_EDITNAMEEDITm_sNamepubilcCString
    IDC_EDITNAMEEDIT控件m_EditNamepublicCEdit
    IDC_EDITNUMBEREDITm_sNumberpublicCString
    IDC_LISTCONTROLSysListView32控件m_ListControlpublicCListCtrl

    CString、CComboBox、COMMONDIALOG1、CEdit、CString、CListCtrl这些变量类型都是MFC已经封装好的类,里面有对应的方法可以使用、查看。添加完这些成员变量之后,我们的项目中会有三个类被创建:COMMONDIALOG1、CExperiment3App、CExperiment3Dlg。界面的设计基本已经完成

②类的基本设计

  1. 将实验一中的代码拷贝到现在的项目中,进行复用(在头文件中添加address.h 在源文件中添加addresss.cpp)。

  2. 在Experiment3Dlg.h中添加头文件address.h ,即(#include “address.h”) ,并在CExperiment3Dlg.cpp、addressbook.cpp中添加头文件pch.h。(在MFC项目中所有的源文件前都必须含有pch.h这个头文件)

  3. 在Experiment3Dlg.h中除了之前用类向导添加的变量,另外添加AddressBook的实例book。

  4. 给用户操作的按钮:载入、保存、新建、更新、删除 都需要有对应的方法来对数据进行操作。另外,需要一个辅助函数对数据进行刷新。可以右键类Addressbook,选中添加函数,可快速添加函数。

    方法对应的按钮函数名返回类型访问.cpp文件参数
    载入LoadFilevoidpublicAddressbook.cppCString Filename
    保存SaveFilevoidpublicAddressbook.cppCString Filename
    新建AddContactvoidpublicAddressbook.cppstring
    更新UpdatevoidpublicAddressbook.cppInt nstring namestring numberstring group
    删除DeletevoidpublicAddressbook.cppInt n
    辅助函数RefreshDatavoidpublicAddressbook.cppbool fromDlgToVector

    但这样操作也会产生相对应的问题:

    如果Addressbook中存在名称为Delete的函数,即使我们添加的函数返回值和传入参数不一样,也不能添加名称为Delete的函数。需要自己手动添加。

    Tip:由于涉及到读入文件,记得在源文件前加入fstream文件流(#include “fstream”)

    对应的函数代码如下:

    载入按钮:

    void AddressBook::LoadFile(CString Filename)
    {
        // TODO: 在此处添加实现代码.
        ifstream inputFile;
        string Name, Number, Group;
        try
        {
            inputFile.open(Filename);
            Book.clear();
    ​
            while (!inputFile.eof())
            {
                inputFile >> Name >> Number >> Group;
                AddContact(Name, Number, Group);
            }
        }
        catch (exception& e)
        {
            throw(e);
        }
    }

    保存按钮:

    void AddressBook::SaveFile(CString Filename)
    {
        // TODO: 在此处添加实现代码.
        ofstream outputFile;
        string Name, Number, Group;
        try
        {
            outputFile.open(Filename);
            int Count = Book.size();
            for (int index = 0 ; index < Count; index++)
            {
                if (index > 0)
                    outputFile << endl;
                Book[index].getContact(Name, Number, Group);
                outputFile << Name << " " << Number << " " << Group;
            }
        }
        catch (exception& e)
        {
            throw(e);
        }
    }

    删除按钮:

    void AddressBook::Delete(int n)
    {
        if (n >= 0 && n < Book.size())
        {
            vector<CContact>::iterator pos = Book.begin() +n ;
            Book.erase(pos);
        }
    }
    ​
    更新按钮:
    
    void AddressBook::Update(int n, string name, string number, string group)
    {
        if (n >= 0 && n < Book.size())
        {
            Book[n].setContact(name,number,group);
        }
    }

③事件的基本设计

所谓事件设计,就相当于是界面设计(呈现给用户的操作方式)和类设计(对于数据的操作)中间的桥梁。

  1. 初始化过程

    1). 对于地址簿(List Control)的初始化

    地址簿对应的变量名为m_ListControl,变量类型为CListCtrl.

    (CListCtrl是列表控件类,列表控件的每一行叫做一个item,每一列叫做一个subitem。每一行和每一列都有个ID号,可以确定唯一的单元格。)

    该类中具有两个构建列表的方法:

    m_pListCtrl->InsertColumn(0,"第一列");

    列索引从0开始,InsertColumn函数将在列表中建立一个列,如果数据有三列,就调用三次此函数,索引分别是0,1,2。

    m_pListCtrl->InsertItem(0,"第一行");

    行索引从0开始,InsertColumn函数将在列表中建立一个行,如果数据有三行,就调用三次此函数,索引分别是0,1,2。

    这里我们的初始化代码如下:

        m_ListControl.InsertColumn(0, _T("姓名"), LVCFMT_LEFT, 100);
        m_ListControl.InsertColumn(1, _T("电话号码"), LVCFMT_LEFT, 100);
        m_ListControl.InsertColumn(2, _T("群组"), LVCFMT_LEFT, 100);

    2).对于群组进行初始化

    群组对应的控件变量名为m_ComboGroup,变量类型为CComboBox.

    群组对应的值变量名为m_sGroup,变量类型为CString.

    这里我们的初始化代码如下:

    m_ComboGroup.AddString(_T("朋友"));
    
    m_ComboGroup.AddString(_T("同事"));
    
    m_ComboGroup.AddString(_T("家人"));
    
    m_ComboGroup.AddString(_T("同学"));
    
    m_ComboGroup.AddString(_T("网友"));
    
    m_sGroup = _T("朋友");

    _T宏可以把一个引号引起来的字符串,根据你的环境设置,使得编译器会根据编译目标环境选择合适的(Unicode还是ANSI)字符处理方式

    3).对于文件的初始化

    FileName = "";

    (涉及到文件,在Experiment3Dlg.h中添加访问对象为pubilc,类型为CString,名称为FileName的变量)

    4).UpdateData(FALSE);

    (UpdateData(TRUE)的作用是将文本框界面值跟新到控件变量;

    UpdateData(FALSE)的作用是将控件变量的值更新到界面中)

  2. 为按钮载入、保存、退出、新建、更新、删除添加事件处理程序

    按钮消息类型函数名
    载入CExperiment3DlgBN_CLICKEDOnBnClickedButtonload
    保存CExperiment3DlgBN_CLICKEDOnBnClickedButtonsave
    退出CExperiment3DlgBN_CLICKEDOnBnClickedButtonexit
    新建CExperiment3DlgBN_CLICKEDOnBnClickedButtonnew
    更新CExperiment3DlgBN_CLICKEDOnBnClickedButtonupdata
    删除CExperiment3DlgBN_CLICKEDOnBnClickedButtondelete

    载入按钮的事件处理程序:

    void CExperiment3Dlg::OnBnClickedButtonload()
    {
        // TODO: 在此添加控件通知处理程序代码
        FileDialog.ShowOpen();
        FileName = FileDialog.get_FileName();
        try
        {
            book.LoadFile(FileName);
            RefreshData(false);
        }
        catch (exception& e)
        {
            MessageBox(CString(e.what()),CString("Warning!"));
        }
    }

    为了方便刷新数据,创建一个RefreshData函数:

    void CExperiment3Dlg::RefreshData(bool fromDlgToBook)
    {
        // TODO: 在此处添加实现代码.
        if (fromDlgToBook)
        {
            int Count = m_ListControl.GetItemCount(); //获取列表框的行数
            CString sName, sNumber, sGroup;
            string name, number, group;
            if (Count > 0)
            {
                book.clear();
                for (int index = 0; index < Count; index++)
                {
                    sName = m_ListControl.GetItemText(index, 0);
                    sNumber = m_ListControl.GetItemText(index, 1);
                    sGroup = m_ListControl.GetItemText(index, 2);
                    name = string(CW2A(sName.GetString()));
                    number = string(CW2A(sNumber.GetString()));
                    group = string(CW2A(sGroup.GetString()));
                    book.AddContact(name, number, group);
                }
            }
    ​
        }
        else
        {
            int Count = book.getCount();
            CString sName, sNumber, sGroup;
            string name, number, group;
            if (Count > 0)
            {
                m_ListControl.DeleteAllItems();
                for (int index = 0; index < Count; index++)
                {
                    book[index].getContact(name, number, group);
                    sName = CString(name.c_str());
                    sNumber = CString(number.c_str());
                    sGroup = CString(group.c_str());
                    m_ListControl.InsertItem(index, sName);
                    m_ListControl.SetItemText(index, 1, sNumber);
                    m_ListControl.SetItemText(index, 2, sGroup);
                }
            }
        }
    }

    如果传入值为True,就把列表框中的数据更新到我们的vector中,如果传入值为False,就把vector中的数据更新到列表框中。

    我们定义的这个函数其实有点类似于MFC自带的UpdateData函数(之前在初始化的时候用过),只不过UpdateData函数实现的是对话框中的数据和控件变量数据之间的更新,而我们这里定义的RefreshData函数,实现的是控件变量和vector容器之间的更新。

    Tip: CW2A 将宽字符集(Unicode)转化为多字符集(ASCII)

    CA2W 将多字符集(ASCII)转化为宽字符集(Unicode)

    为列表添加一个事件处理程序,消息类型为NM_CLICK.表示如果选中地址簿中的一行,可以将该行的数据更新到联系人中给用户看:

    void CExperiment3Dlg::OnNMClickListcontrol(NMHDR* pNMHDR, LRESULT* pResult)
    {
        LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
        // TODO: 在此添加控件通知处理程序代码
        POSITION pos;
        pos = m_ListControl.GetFirstSelectedItemPosition();
        int index = m_ListControl.GetNextSelectedItem(pos);
        if (index >= 0)
        {
            string name, number, group;
            book[index].getContact(name, number, group);
            m_sName = CString(name.c_str());
            //m_sNumber
            m_sNumber = CString(number.c_str());
            m_sGroup = CString(group.c_str());
            UpdateData(FALSE);
        }
        *pResult = 0;
    }

    保存按钮的事件处理程序:

    void CExperiment3Dlg::OnBnClickedButtonsave()
    {
        // TODO: 在此添加控件通知处理程序代码
        if (FileName.IsEmpty())
        {
            FileDialog.ShowSave();
            FileName = FileDialog.get_FileName();
        }
        try
        {
            book.SaveFile(FileName);
        }
        catch (exception& e)
        {
            MessageBox(CString(e.what()), CString("Warning!"));
        }
    }

    新建按钮的事件处理程序:

    void CExperiment3Dlg::OnBnClickedButtonnew()
    {
        // TODO: 在此添加控件通知处理程序代码
        UpdateData(TRUE);
        if (!m_sName.IsEmpty())
        {
            int count = m_ListControl.GetItemCount();
            m_ListControl.InsertItem(count, m_sName);
            m_ListControl.SetItemText(count, 1, m_sNumber);
            m_ListControl.SetItemText(count, 2, m_sGroup);
            string name(CW2A(m_sName.GetString()));
            string number(CW2A(m_sNumber.GetString()));
            string group(CW2A(m_sGroup.GetString()));
            book.AddContact(name, number, group);
            m_sName = "";
            m_sNumber = "";
            m_sGroup = "朋友";
            UpdateData(FALSE);
            m_EditName.SetFocus();
        }
    }

    退出按钮的事件处理程序:

    void CExperiment3Dlg::OnBnClickedButtonexit()
    {
        // TODO: 在此添加控件通知处理程序代码
        OnOK();
    }

    更新按钮的事件处理程序:

    void CExperiment3Dlg::OnBnClickedButtonupdate()
    {
        // TODO: 在此添加控件通知处理程序代码
        UpdateData(TRUE);
        int position(m_ListControl.GetSelectionMark());
        book[position].setContact(string(CW2A(m_sName.GetString())), string(CW2A(m_sNumber.GetString())), string(CW2A(m_sGroup.GetString())));
        RefreshData(FALSE);
    }

    删除按钮的事件处理程序:

    void CExperiment3Dlg::OnBnClickedButtondelete()
    {
        // TODO: 在此添加控件通知处理程序代码
        int position(m_ListControl.GetSelectionMark());
        book.Delete(position);
        m_sName = "";
        m_sNumber = "";
        m_sGroup = "朋友";
        UpdateData(FALSE);
        RefreshData(FALSE);
    }

    排序按钮的事件处理程序:

    void CExperiment3Dlg::OnBnClickedButtonsort()
    {
        book.sort();
        RefreshData(FALSE);
    }

到这里,该MFC设计基本就结束了

这本是一篇实验报告,至于为什么产生将它写好并发出来的想法,主要是明白了写实验报告的意义,刚开始跟着老师做完这个实验的时候,对里面的很多东西都是懵的,直到在写实验报告时,才慢慢理解写实验报告的目的其实就是内化这个项目里面所涉及到的知识的过程。也逐渐明白为什么会有那么多人在CSDN上发博客,一方面是内化自己所学的东西,一方面也是为了给那些正在学的人提供帮助。之前也有在学python的Django框架,但学的过程中很多地方不理解,就没有再学下去了。现在也知道以后该怎么去学习做一个项目,等有空一定要把Django拿下,也希望自己的这篇文章能给有需要的人提供力所能及的帮助。

  • 9
    点赞
  • 61
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值