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






4.33/5 (3投票s)
演示了 MFC 应用程序文档模板的非参数化构造函数的参数化可能性。
引言
在上一篇文章中,我们展示了显示在 CFormView 派生类中的 CListCtrl 类修改版本。然而,在不同的视图中,控件显示得完全相同。因此,我们希望在对话框的视图中拥有 CListCtrl 类的不同派生变体。例如,如图 1 所示。
为了实现这一点,可以提供几种解决方案。
- 编写自定义的 CMultiDocTemplate 版本,使用所需类的参数化构造函数。但这种方法似乎并非必需,可以留待紧急情况使用。
- 创建 CMultiDocTemplate 中使用的类的几个派生类,这些类仅在内部参数化上有所不同。此方法更简单,但更臃肿。如果我们想在程序中使用数百个表格,例如在俄罗斯会计程序 **“1C”** 的 MFC 版本 7.7 中,这种方法就不再适用了。
- 根据一些程序员的建议,切换到 Qt。但个人认为这不适合我。
- 尝试以一种不明显的方式将参数传递给非参数化构造函数。我们这样假设,在 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 函数的其他类,可以进行类似的程序操作。
这种方法完全可行,如果不是因为另一种简单明了但不知为何不立即出现的非参数化构造函数方法,它本可以被采用。
- 通过应用程序的全局变量向类模板传递参数。但我们不使用 `UINT nTable` 变量,而是利用在 Main.h 中定义的枚举,以便于操作大量表格
/////////////////////////////////////////////////////////////////////////////
// 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 日:首次发布