65.9K
CodeProject 正在变化。 阅读更多。
Home

MDI 模板 CMultiDocTemplate 函数中 CListCtrl 后代类的参数化

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.33/5 (3投票s)

2009 年 7 月 2 日

CPOL

3分钟阅读

viewsIcon

21496

downloadIcon

545

演示了 MFC 应用程序文档模板的非参数化构造函数的参数化可能性。

引言

上一篇文章中,我们展示了显示在 CFormView 派生类中的 CListCtrl 类修改版本。然而,在不同的视图中,控件显示得完全相同。因此,我们希望在对话框的视图中拥有 CListCtrl 类的不同派生变体。例如,如图 1 所示。

Tables.jpg - Click to enlarge image

图 1. 对话框视图中 CListCtrl 类的不同派生变体。

为了实现这一点,可以提供几种解决方案。

  1. 编写自定义的 CMultiDocTemplate 版本,使用所需类的参数化构造函数。但这种方法似乎并非必需,可以留待紧急情况使用。
  2. 创建 CMultiDocTemplate 中使用的类的几个派生类,这些类仅在内部参数化上有所不同。此方法更简单,但更臃肿。如果我们想在程序中使用数百个表格,例如在俄罗斯会计程序 **“1C”** 的 MFC 版本 7.7 中,这种方法就不再适用了。
  3. 根据一些程序员的建议,切换到 Qt。但个人认为这不适合我。
  4. 尝试以一种不明显的方式将参数传递给非参数化构造函数。我们这样假设,在 CMainApp 类的代码中,我们有
    CMultiDocTemplate *pDocTemplate;
    
    pDocTemplate = new CMultiDocTemplate(
        IDR_MAINTYPE,
        RUNTIME_CLASS(CMainDoc),
        RUNTIME_CLASS(CTableFrame),   // Custom MDI child frame
        RUNTIME_CLASS(CMainView)
        );
    
    AddDocTemplate(pDocTemplate);

    我们想将参数 `UINT nTable`(表格编号)传递给 CTableFrame 类。显然这样做是不可能的,因为即使存在参数化构造函数,CMultiDocTemplate 函数也会调用非参数化构造函数 CTableFrame::CTableFrame()。但如果你仔细观察 `RUNTIME_CLASS` 宏,你会发现

    #define RUNTIME_CLASS(class_name) 
    	((CRuntimeClass*)(&class_name::class##class_name))

    在 CTableFrame::classCTableFrame 结构体中,为了我们的目的,可以利用成员变量 `classCTableFrame.m_pNextClass`,前提是我们不使用 `AddDocTemplate(pDocTemplate)` 函数通过列表来管理模板,而是直接定义模板数组。这样,我们就可以传递所需的参数 `nTable`

    CTableFrame::classCTableFrame.m_pNextClass = 
    		reinterpret_cast<CRuntimeClass *>(nTable);

    这行代码应该放在该行之前

    CMultiDocTemplate *pDocTemplate;

    将此变量从 CTableFrame 类中“偷出”将执行反向操作

    /////////////////////////////////////////////////////////////////////////////
    // CTableFrame construction
    /////////////////////////////////////////////////////////////////////////////
    CTableFrame::CTableFrame() {
      m_nTable = (UINT) reinterpret_cast<DWORD>(classCTableFrame.m_pNextClass);
    }

    通过这种方式,我们得到了一个准参数化的构造函数 CTableFrame::CTableFrame()。对于 CMultiDocTemplate 函数的其他类,可以进行类似的程序操作。

    这种方法完全可行,如果不是因为另一种简单明了但不知为何不立即出现的非参数化构造函数方法,它本可以被采用。

  5. 通过应用程序的全局变量向类模板传递参数。但我们不使用 `UINT nTable` 变量,而是利用在 Main.h 中定义的枚举,以便于操作大量表格
  6. /////////////////////////////////////////////////////////////////////////////
    // ETABLE
    /////////////////////////////////////////////////////////////////////////////
    enum ETABLE {
        e_NULL = 0,  // Empty Form Index
        e_First,
        e_Second,
        e_Third,
        e_MAX
    };

    等等。这足以满足我们的演示目的。稍后,我们在 Main.h 中定义一个全局变量 `ETABLE m_eTable`,其默认值为 `e_NULL`。作为示例,我们将利用功能处理程序

    /////////////////////////////////////////////////////////////////////////////
    // OnFileNew
    /////////////////////////////////////////////////////////////////////////////
    void CMainApp::OnFileNew() {
      m_eTable = (ETABLE) ((UINT) m_eTable + 1);
    
      if(m_eTable >= e_MAX){
        _M("No data for new table!");
        return;
      }
    
      CWinApp::OnFileNew();
    }  // OnFileNew

    访问此变量并不复杂。例如,在 CMainView 类中

    /////////////////////////////////////////////////////////////////////////////
    // CMainView construction
    /////////////////////////////////////////////////////////////////////////////
    CMainView::CMainView() : CFormView(CMainView::IDD){
      //*** Main Application Pointer
      CMainApp *pMainApp = reinterpret_cast<CMainApp *>(AfxGetApp());
    
      if(!pMainApp){
        _M("CMainView: Empty object of the CMainApp class!");
        return;
      }
    
      //*** Table Id
      m_eTable = pMainApp->m_eTable;
    
      //*** The Meta Table Structure
      m_MetaTable = pMainApp->m_aMetaTable[m_eTable];
    }  // CMainView

    此处参数 `ETABLE m_eTable` 和 `META_TABLE m_MetaTable` 定义在 `MainView.h` 中。`META_TABLE` 结构体定义在 `StdAfx.h` 中

    //*** The Meta Table Structure
    typedef struct {
        TCHAR *szTblName;  	// Table name
        DWORD dwStyle;  	// Table style
        DWORD dwExStyle;  	// Extended table style
        RECT *pFrmRect;  	// Frame rectangle pointer
        RECT *pViewRect;  	// View rectangle pointer
        CFont *pHdrFont;  	// Table header font pointer
        CFont *pListFont;  	// Table list font pointer
        UINT nHdrHeight;  	// Table header height
        UINT nListHeight;  	// Table list height
        UINT nColCount;  	// Table header columns count
        UINT nRowCount;  	// Table list row count
        TCHAR **apRowText;  	// Table rows text array
        META_HEADER *apMetaHeader;  // Meta table header pointer
    } META_TABLE;

    `META_HEADER` 结构体也定义在此处

    //*** The Meta Table Header Structure
    typedef struct {
        TCHAR *szHdrName;  	// Column name
        // TCHAR *szFormat;  	// Table list data format
        DWORD nAdjust;  	// Text formatting
        UINT nWidth;  		// Column width
    } META_HEADER;

    这些参数的使用示例如下代码所示

    /////////////////////////////////////////////////////////////////////////////
    // OnCreate
    /////////////////////////////////////////////////////////////////////////////
    int CMainView::OnCreate(LPCREATESTRUCT pCS){
      if(CFormView::OnCreate(pCS) == -1)
          return -1;
    
      //*** Create table
      CListCtrlEx *pTable = new CListCtrlEx;
        
      if(!pTable){
        _M("Empty and CListCtrlEx object!");
        return -1;
      }
    
      //*** CListCtrlEx initialization
      if(!pTable->Create(m_MetaTable.dwStyle, *m_MetaTable.pViewRect, this, m_eTable)){
        _M("Failed to create and CListCtrlEx object!");
        return - 1;
      }
    
      //*** Sets extended table style
      pTable->SetExtendedStyle(m_MetaTable.dwExStyle);
    
      //*** Creates and table header
      CHeaderCtrlEx *pHeader = new CHeaderCtrlEx;
        
      if(!pHeader){
        _M("Empty CHeaderCtrlEx object!");
        return -1;
      }
        
      //*** The CHeaderCtrlEx handle
      HWND hHeader = pHeader->m_hWnd;
      //HWND hHeader = pHeader->GetSafeHwnd();
    
      CHeaderCtrl *pOldHeader = pTable->GetHeaderCtrl();
    
      if(!pOldHeader){
        _M("Empty CHeaderCtrl object!");
        return -1;
      }
        
      //*** The CHeaderCtrl handle
      HWND hOldHeader = pOldHeader->m_hWnd; 
      //HWND hOldHeader = pOldHeader->GetSafeHwnd();
    
      //*** The table header subclassing
      if(!pHeader->SubclassWindow(hOldHeader)){
        _M("Failed to Subclass and table header!");
        return -1;
      }
    
      //*** The structure of and table header cell
      HDITEM HDItem = {0};
    
      HDItem.mask |= HDI_FORMAT;  	// The fmt member is valid
      HDItem.mask |= HDI_TEXT;  	// The pszText and cchTextMax members are valid
      HDItem.mask |= HDI_WIDTH;  	// The cxy member is valid and specifies 
    				// the item's width
    
      HDItem.cchTextMax = MAXITEMTEXT;
    
      //*** Creates table columns
      for(UINT i = 0; i < m_MetaTable.nColCount; i++){
        META_HEADER *apMetaHeader = m_MetaTable.apMetaHeader;
    
        HDItem.pszText = (LPTSTR) apMetaHeader[i].szHdrName;
        HDItem.fmt = apMetaHeader[i].nAdjust;
        HDItem.cxy = apMetaHeader[i].nWidth;
            
        //*** Calls CHeaderCtrlEx::DrawItem
        HDItem.fmt |= HDF_OWNERDRAW;
    
        //*** Sends too message HDM_LAYOUT
        pTable->InsertColumn(
            i, 
            HDItem.pszText,    
            HDItem.fmt, 
            HDItem.cxy
        );
        
        //*** Reset the first column
        if(i == 0)
            pHeader->SetItem(i, &HDItem);
      }
    
      //*** Sets the table rows count in the virtual mode (LVS_OWNERDATA)
      //*** Send messages LVN_GETDISPINFOW & HDM_LAYOUT
      //*** Cals the CListCtrlEx::DrawItem
      pTable->SetItemCount(2*m_MetaTable.nRowCount);  	// REALLY MUST BE 
    						// m_MetaTable.nRowCount
    
      return 0;
    }  // OnCreate

程序显示了三个由同一类以不同方式设计的列表控件。所有这些控件都是通过按 **CTRL-N** 触发的。这些演示列表的信息作为 `static` 变量存在于 CMainApp 类中。在实际应用程序中,信息将从某个数据库获取。

历史

  • 2009 年 7 月 2 日:首次发布
© . All rights reserved.