当前位置: 首页 > news >正文

WTL学习之对话框和控件

作者:朱金灿

来源:http://blog.csdn.net/clever101

 

            继续学习WTL程序中的对话框程序的使用。程序的主要设置如下图:

                             

          

 

        在对话框和控件学习中我们主要解决下面几个问题。

 

一.变量如何关联控件。

 

      和MFC的通过增加控件成员变量或者数据成员变量不同,WTL的变量关联控件的方法有四种。一个值得注意的地方是不同的控件绑定方法处理控件的消息各不相同。下面会逐一进行介绍。

 

  1.绑定一个CWindow

 

    最简单的方法是声明一个CWindow 或者其他的窗口接口类,然后调用其 Attach() 方法。你也可以使用CWindow 的构造函数或者赋值操作符把变量关联到控件的 HWND。

下面的代码演示了把变量关联到列表控件的全部三种方法:

HWND hwndList = GetDlgItem(IDC_LIST);
CListViewCtrl wndList1 (hwndList);  // use constructor
CListViewCtrl wndList2, wndList3;

  wndList2.Attach ( hwndList );     // use Attach method
  wndList3 = hwndList;              // use assignment operator

       记住,CWindow析构函数并不销毁窗口,所以在变量离开作用域前并不需要将之与控件脱离。如果愿意,你也可以对成员变量使用此方法 -你可以在 OnInitDialog() 处理器中关联变量。

 

   不过这种绑定控件的方法经过我研究,似乎只可以处理WM_COMMAND、WM_NOTIFY或者其他通知消息。要处理更多的消息,需要用到下面两种绑定方法。       

 

2.使用CContainedWindow类

         CContainedWindow 是使用CWindow 和 CWindowImpl 的一个折中。它允许你子类化一个控件,并在其父窗口中处理控件的消息。这就允许你把所有的消息处理器置于对话框类里,而不必再为每个控件写独立的CWindowImpl 类。注意,不要使用 CContainedWindow 来处理 WM_COMMAND、WM_NOTIFY 或者其他通知消息,因为这些消息总是发送给控件的父窗口。就是说这种绑定方法可以处理专门发给控件自身窗口的消息。

 

        具体的做法是:

a. 使用CContainedWindow或CContainedWindowT类定义一个变量。其中CContainedWindow为一个基类窗口,可以用它来处理一般发给控件的消息,如WM_SETCURSOR消息,如果要实现更使用更具体的窗口的用法,需要CContainedWindowT类来包装WTL已有的控件类。CContainedWindowT为一个模板类,如需定义一个列表控件类,CContainedWindowT<CListViewCtrl>。


b. 在对话框的初始化函数里使用接口SubclassWindow关联控件,例子如下:

 m_wndAboutBtn.SubclassWindow ( GetDlgItem(ID_APP_ABOUT) );

c.使用ALT_MSG_MAP来指定控件消息处理函数。假设你定义了一个

CContainedWindow m_wndOKBtn;

      然后在消息处理宏添加ALT_MSG_MAP节:

BEGIN_MSG_MAP_EX(CMainDlg)
        MESSAGE_HANDLER(WM_INITDIALOG,OnInitDialog)
        COMMAND_ID_HANDLER_EX(ID_APP_ABOUT,OnAppAbout)
        COMMAND_ID_HANDLER_EX(IDOK,OnOK)
        COMMAND_ID_HANDLER_EX(IDCANCEL,OnCancel)
   ALT_MSG_MAP(1)
        MSG_WM_SETCURSOR(OnSetCursor_OK)
    END_MSG_MAP()

        注意这里你必须使用BEGIN_MSG_MAP_EX代替BEGIN_MSG_MAP,具体原因请参考MFC 程序员的 WTL 教程(五)。

最后在对话框的构造函数里指定控件使用的消息处理段,如

CMainDlg::CMainDlg() : m_wndOKBtn(this, 1)//这里对应ALT_MSG_MAP(1)中的1
{
}

 

3.子类化

       这种方法实际上是定义一个控件派生类,和第二种方法有些类似,不同的是消息处理部分不在对话框类的实现里,而是在控件派生类的实现部分。

       具体做法我举一个例子来说明。比如创建一个CWindowImpl 派生类并使用它来子类化控件。这与方法 2 相似,但是消息处理器在CWindowImpl 类中而不是在对话框类中。

      ControlMania1 使用此方法来子类化主对话框中的 About 按钮。下面是 CButtonImpl 类,派生于 CWindowImpl 并处理了 WM_SETCURSOR消息:

class CButtonImpl : public CWindowImpl<CButtonImpl, CButton>
{
    BEGIN_MSG_MAP_EX(CButtonImpl)
        MSG_WM_SETCURSOR(OnSetCursor)
    END_MSG_MAP()

    LRESULT OnSetCursor(HWND hwndCtrl, UINT uHitTest, UINT uMouseMsg)
    {
    static HCURSOR hcur = LoadCursor ( NULL, IDC_SIZEALL );

        if ( NULL != hcur )
            {
            SetCursor ( hcur );
            return TRUE;
            }
        else
            {
            SetMsgHandled(false);
            return FALSE;
            }
    }
};

然后在主对话框里声明一个 CButtonImpl 成员变量:

class CMainDlg : public CDialogImpl<CMainDlg>
{

// ...
protected:
    CContainedWindow m_wndOKBtn, m_wndExitBtn;
    CButtonImpl m_wndAboutBtn;
};

最后在 OnInitDialog() 里子类化按钮:

LRESULT CMainDlg::OnInitDialog(...)
{
// ...
    // Attach CContainedWindows to OK and Exit buttons
    m_wndOKBtn.SubclassWindow ( GetDlgItem(IDOK) );
    m_wndExitBtn.SubclassWindow ( GetDlgItem(IDCANCEL) );
    // CButtonImpl: subclass the About button
    m_wndAboutBtn.SubclassWindow ( GetDlgItem(ID_APP_ABOUT) );
    return TRUE;
}
       

       使用这种方法进行控件消息处理的话,需要涉及到一个反射通知的问题。在 WTL 中处理通知与API 级编程类似。控件以 WM_COMMAND 或者WM_NOTIFY 消息的形式向其父窗口发送通知,其父窗口负责处理。另外还有几个消息也可以视作为通知,例如WM_DRAWITEM,该消息在属主绘制控件需要绘制的时候发送。父窗口既可以自己处理通知消息,也可将消息反射回控件。反射像在MFC 中一样工作 - 控件可以自己处理通知,使得代码具有自包容的形态,易于移到其他的工程中。那什么叫反射通知呢?简单来说就是有一个消息发给控件,你想在控件的实现部分处理这个消息而不是在对话框类处理这个消息。这就意味着你要通知对话框类:这个消息你不用处理,留给控件自身处理吧。具体的做法是向对话框的消息映射中添加一个宏REFLECT_NOTIFICATIONS()。 

 

4.使用DDX_CONTROL

    这种做法和MFC程序的做法一样,就是使用DDX_CONTROL宏绑定一个控件派生类。值得注意的是你不能在DDX_CONTROL宏使用WTL的原生类CListViewCtrl、CContainedWindow、以及CContainedWindowT,你需要使用一个CWindowImpl 的派生类。  

       为了向CMainDlg 添加 DDX 支持,把CWinDataExchange 加入到继承列表中:

class CMainDlg : public CDialogImpl<CMainDlg>, 
                 public CWinDataExchange<CMainDlg>
{
//...
};

          接下来在类中创建一个DDX 映射,它与 MFC 应用中 ClassWizard 生成的 DoDataExchange() 函数相似。针对不同类型的数据,存在着好多个DDX_* 宏,我们在这儿要使用的是 DDX_CONTROL,以把变量连接到控件上。这一次,当你在控件上右击时,我们用CEditImpl 处理 WM_CONTEXTMENU 消息来做一些事情。

class CEditImpl : public CWindowImpl<CEditImpl, CEdit>
{
    BEGIN_MSG_MAP_EX(CEditImpl)
        MSG_WM_CONTEXTMENU(OnContextMenu)
    END_MSG_MAP()

    void OnContextMenu ( HWND hwndCtrl, CPoint ptClick )
    {
        MessageBox("Edit control handled WM_CONTEXTMENU");
    }
};

class CMainDlg : public CDialogImpl<CMainDlg>,
                 public CWinDataExchange<CMainDlg>,                
{
//...
    BEGIN_DDX_MAP(CMainDlg)
        DDX_CONTROL(IDC_EDIT, m_wndEdit)
    END_DDX_MAP()

protected:
    CContainedWindow m_wndOKBtn, m_wndExitBtn;
    CButtonImpl m_wndAboutBtn;
    CEditImpl   m_wndEdit;
};

        最后,在OnInitDialog() 中,我们调用继承于 CWinDataExchange 的 DoDataExchange() 函数。在DoDataExchange() 被第一次调用的时候,它会按需子类化控件。在本例中,DoDataExchange()会子类化 ID 为 IDC_EDIT 的控件,并把它连接到变量 m_wndEdit 上。

LRESULT CMainDlg::OnInitDialog(...)
{
// ...
    // Attach CContainedWindows to OK and Exit buttons
    m_wndOKBtn.SubclassWindow ( GetDlgItem(IDOK) );
    m_wndExitBtn.SubclassWindow ( GetDlgItem(IDCANCEL) );
    // CButtonImpl: subclass the About button
    m_wndAboutBtn.SubclassWindow ( GetDlgItem(ID_APP_ABOUT) );
    // First DDX call, hooks up variables to controls.
    DoDataExchange(FALSE);
    return TRUE;
}

    DoDataExchange() 的参数和 MFC UpdateData() 函数的参数具有相同的含义。我们在下一节中讨论其更多细节。

    如前所述,这种方法我们仍然需要REFLECT_NOTIFICATIONS()进行反射通知。

 

一.数据交换问题

    我们知道在MFC程序中控件资源除了绑定上面的提到的控件变量,也可以绑定数据成员,这样就可以在对话框类中进行数据交换。在WTL程序中也可以这样做。具体做法以一个简单的例子说明:

class CMainDlg : public ...
{

//...

    BEGIN_DDX_MAP(CMainDlg)
        DDX_CONTROL(IDC_EDIT, m_wndEdit)
        DDX_TEXT(IDC_EDIT, m_sEditContents)
        DDX_INT(IDC_EDIT, m_nEditNumber)
    END_DDX_MAP()

protected:

    // DDX variables
    CString m_sEditContents;
    int     m_nEditNumber;
};

      在 OK 按钮的处理器中,我们首先调用 DoDataExchange() 将数据从编辑框中传输到我们刚刚添加的两个变量中,然后再把结果显示到列表控件中。

LRESULT CMainDlg::OnOK ( UINT uCode, int nID, HWND hWndCtl )
{
CString str;
    // Transfer data from the controls to member variables.
    if ( !DoDataExchange(true) )
        return;
    m_wndList.DeleteAllItems();
    m_wndList.InsertItem ( 0, _T("DDX_TEXT") );
    m_wndList.SetItemText ( 0, 1, m_sEditContents );
    str.Format ( _T("%d"), m_nEditNumber );
    m_wndList.InsertItem ( 1, _T("DDX_INT") );
    m_wndList.SetItemText ( 1, 1, str );
}

      在这里你可以看到WTL的DoDataExchange函数实际上和MFC的UpdateData函数的作用是一样的。

     如果你在编辑框中输入了非数字文本,DDX_INT 就会失败,并调用OnDataExchangeError()。CMainDlg 覆盖了OnDataExchangeError() 以显示一个消息框:

 

void CMainDlg::OnDataExchangeError ( UINT nCtrlID, BOOL bSave )
{
    CString str;
    str.Format ( _T("DDX error during exchange with control: %u"), nCtrlID );
    MessageBox ( str, _T("ControlMania1"), MB_ICONWARNING );
    ::SetFocus ( GetDlgItem(nCtrlID) );
}
 

       

       这其实已带有数据校验(DDV)的色彩了。关于DDV我们以后还会提到,本次学习就到这里。

参考文献:

1.《WTL for MFC Programmers》,作者迈克尔.敦 (Michael Dunn), 珠穆朗玛 译




    



转载于:https://www.cnblogs.com/lanzhi/archive/2012/06/24/6470769.html

相关文章:

  • 人的一生需要看淡的几个方面
  • Smack 结合 Openfire服务器,建立IM通信,发送聊天消息
  • 数据字典存储事件实例
  • [转]MYSQL常用命令
  • Windows 下硬盘安装 fedora(Live CD)
  • SQL几个常用函数略解
  • b/s结构和c/s结构
  • IBM x3650 m3 raid0
  • Linux内核中的双向循环链表学习
  • .net中生成excel后调整宽度
  • maven 自动生成运行脚本插件appassembler-maven-plugin
  • Spring配置数据源的三种方式
  • 数据库的四种类型的完整性约束
  • 由struct 和class 想到的浅度复制和深度复制 c#
  • 深入理解C#之 参数传递 ref out params
  • 【跃迁之路】【477天】刻意练习系列236(2018.05.28)
  • 2017 年终总结 —— 在路上
  • Hibernate【inverse和cascade属性】知识要点
  • javascript 总结(常用工具类的封装)
  • Java比较器对数组,集合排序
  • Java超时控制的实现
  • java中的hashCode
  • leetcode388. Longest Absolute File Path
  • puppeteer stop redirect 的正确姿势及 net::ERR_FAILED 的解决
  • spring + angular 实现导出excel
  • Vue.js-Day01
  • Vue小说阅读器(仿追书神器)
  • Vultr 教程目录
  • webgl (原生)基础入门指南【一】
  • webpack+react项目初体验——记录我的webpack环境配置
  • 纯 javascript 半自动式下滑一定高度,导航栏固定
  • 从重复到重用
  • 动手做个聊天室,前端工程师百无聊赖的人生
  • 理解在java “”i=i++;”所发生的事情
  • 前端存储 - localStorage
  • 深度解析利用ES6进行Promise封装总结
  • 世界上最简单的无等待算法(getAndIncrement)
  • 【云吞铺子】性能抖动剖析(二)
  • #NOIP 2014#Day.2 T3 解方程
  • #大学#套接字
  • $refs 、$nextTic、动态组件、name的使用
  • (06)金属布线——为半导体注入生命的连接
  • (16)UiBot:智能化软件机器人(以头歌抓取课程数据为例)
  • (4)事件处理——(7)简单事件(Simple events)
  • (bean配置类的注解开发)学习Spring的第十三天
  • (Java数据结构)ArrayList
  • (MATLAB)第五章-矩阵运算
  • (大众金融)SQL server面试题(1)-总销售量最少的3个型号的车及其总销售量
  • (二)Eureka服务搭建,服务注册,服务发现
  • (二)springcloud实战之config配置中心
  • (六)什么是Vite——热更新时vite、webpack做了什么
  • (一)C语言之入门:使用Visual Studio Community 2022运行hello world
  • (一)硬件制作--从零开始自制linux掌上电脑(F1C200S) <嵌入式项目>
  • (转)创业的注意事项
  • (转)关于pipe()的详细解析