CPropertySheet和CPropertyPage中数据交换时的两种处理手段

来源:互联网 发布:照片底色修改软件 编辑:程序博客网 时间:2024/05/22 17:40

        第一种方式MFC中有一篇专门的文章介绍了它,先把它列出来:

        在大多数对话框中,属性表(CPropertySheet)和应用程序之间的数据交互是属性表最重要的功能之一。这篇文章介绍了怎样实现这个任务。

        和属性表交互数据实际上是和属性表中的属性页(CPropertyPage)交互数据,和属性页交互数据的过程和和对话框交互数据的过程是一样的,因为一个CPropertyPage对象是一种特殊的CDialog对象。数据交互过程也使用了框架的DDX(Dialog Data Exchange)机制,这种机制实现了在对话框中的控件和对话框的成员变量中交互数据。

        和属性表交互数据与和普通的对话框交互数据有一个很大的不同点是属性表经常会含有多个属性页,因此你必须和属性表中的所有属性页交互数据。

       下面的例子显示了如何在一个CView对象和含有两个属性页的属性表中交互数据:

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;        GetDocument()->SetModifiedFlag();      GetDocument()->UpdateAllViews(NULL);   }}
        上面是MFC中对属性表和属性页中数据交互的介绍,DoModalPropertySheet()函数是一个事件响应函数,可能是一个按钮的响应函数,点击这个按钮后会进行响应,这个函数首先创建了含有两个属性页(这两个属性页都定义了各自单独的类而没有使用MFC中提供的CPropertyPage,这是因为属性页中存在需要传递给应用程序的数据)的属性表,这两个属性页和视图类中各自都定义了一些成员变量,分别如下:
CMyFirstPage 类:
m_nMember1 m_nMember2 
CMySecondPage 类:
m_nMember1 m_nMember2 
CMyView类:
m_nMember1m_nMember2m_strMember3m_strMember4 
       程序接下来做的事情是将View类中的成员变量的值赋给属性页中成员变量的值,这样最开始显示属性页时属性页中的控件会默认显示View类中定义的值。接下来将属性页添加到属性表中。

       接下来这行代码是本文的重点:
 propsheet.DoModal() == IDOK    
       这行代码表示当你点击属性表中的确定按钮时会进行接下来的操作,就是将属性页中新的数据传回应用程序。

        但是如果你不是直接使用MFC中的CPropertySheet类,而是使用它的继承类,因为在里面有比较复杂的业务逻辑要处理。这样也不方便将事件响应函数放在CView类中了,而是应该放置在CPropertySheet的派生类中。问题就来了,首先CPropertySheet是没有对应的对话框资源的,所以无法添加新按钮,并且在CPropertySheet类中也找不到可以重载的OnOk()函数,这就是现在说的第二种方式。

        经过测试发现可以手动添加OnOk()和OnCancel()函数,
首先在CMyPropertySheet.h中添加:
afx_msg void OnOk();afx_msg void OnCancel();
然后在CMyPropertySheet.cpp的消息映射中添加:
BEGIN_MESSAGE_MAP(CMyPropertySheet, CPropertySheet)ON_BN_CLICKED(IDOK,OnOk)ON_BN_CLICKED(IDCANCEL,OnCancel)END_MESSAGE_MAP()
最后在CMyPropertySheet.cpp中实现它:
void CMyPropertySheet::OnOk(){//write your code hereCPropertySheet::EndDialog(IDOK);}void  CMyPropertySheet::OnCancel(){//write your code hereCPropertySheet::OnClose();}

       在上面注释的地方加上你的处理代码,需要注意的是CPropertySheet::EndDialog(IDOK)和CPropertySheet::OnClose()这两行代码必不可少,不然点击确定和取消后不会有什么反应。并且CPropertySheet不是继承自CDialog类,所以它的处理方式和一般的对话框也有些不同。

       在属性页和属性表中还有一个小问题,就是如果在上面的OnOk函数中如果有从属性页中获取数据的代码但是这个属性表出现时你没有点击这个属性页,那么实际上属性页这个资源是不会创建的,那么就使用了未创建的对话框资源,程序会报错。


      解决的办法就是判断这个属性页是否实际创建了,如果它没有创建就不要尝试从这个属性页中获取数据,这时使用到了m_hWnd这个数据成员。


       这时OnOk()函数应该是这个样子:

void CMyPropertySheet::OnOk(){if(m_propertyPage1.m_hWnd!=NULL){//get the member value for m_propertyPage1 and do something}if(m_propertyPage2.m_hWnd!=NULL){//get the member value for m_propertyPage2 and do something}CPropertySheet::EndDialog(IDOK);}

        就是说从属性页中获取数据并处理的代码需要放在if语句里,只有实际创建了这个属性页时才可以从这个属性页中获取数据,不然程序会报错。