我创建了一个基于 MFC 对话框的应用程序来研究选项卡控件。在选项卡控件中,可以为每个选项卡设置特定于应用程序的数据。我试图了解如何设置/检索选项卡控件的各个选项卡的数据。
这是我正在创建的示例应用程序。控件的每个选项卡都应该存储一些 GPU 信息。
据我了解,添加应用程序特定数据有 3 个步骤。
创建一个用户定义的结构,其第一个成员应该是 type
TCITEMHEADER
。struct GPU { std::wstring name; int busid; }; struct tabData { TCITEMHEADER tabItemHeader; GPU gpu; };
告诉选项卡控件额外的字节,用户定义的结构将采用。这是我正在做的
DoDataExchange()
。int extraBytes = sizeof(tabData) - sizeof(TCITEMHEADER); auto status = tabCtrl1.SetItemExtra(extraBytes);
在添加选项卡时设置用户定义的数据。
static int tabCtr = 0; tabData td; td.tabItemHeader.pszText = _T("TabX"); td.tabItemHeader.mask = TCIF_TEXT; td.gpu.name = L"AMD NVIDIA"; td.gpu.busid = 101; TabCtrl_InsertItem(tabCtrl1.GetSafeHwnd(), tabCtr, &td);
现在要获取数据,我们只需调用TabCtrl_GetItem()
.
tabData td2;
td2.tabItemHeader.pszText = new TCHAR[20];
td2.tabItemHeader.cchTextMax = 20;
td2.tabItemHeader.mask = TCIF_TEXT;
td2.gpu.busid = 0;
TabCtrl_GetItem(tabCtrl1.GetSafeHwnd(), 0, &td2);
但正如我们在下图中看到的那样。我确实得到了选项卡文本(pszText 成员 - 图像中的数据项 1),但没有获得我之前与之关联的额外数据(图像中的数据项 2 和 3)。
我错过了哪一步?
为什么与应用程序定义的数据相关的结构没有被填充?
附加信息
这是应用程序的完整代码。
CPP 文件:
// tabCtrlStackOverflowDlg.cpp : implementation file // #include "stdafx.h" #include "tabCtrlStackOverflow.h" #include "tabCtrlStackOverflowDlg.h" #include "afxdialogex.h" #include <string> #ifdef _DEBUG #define new DEBUG_NEW #endif struct GPU { std::wstring name; int busid; }; struct tabData { TCITEMHEADER tabItemHeader; GPU gpu; }; CtabCtrlStackOverflowDlg::CtabCtrlStackOverflowDlg(CWnd* pParent /*=NULL*/) : CDialogEx(IDD_TABCTRLSTACKOVERFLOW_DIALOG, pParent) { m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } void CtabCtrlStackOverflowDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_TAB1, tabCtrl1); int extraBytes = sizeof(tabData) - sizeof(TCITEMHEADER); auto status = tabCtrl1.SetItemExtra(extraBytes); wchar_t *t = status ? L"SetItemExtra() success" : L"SetItemExtra() fail"; GetDlgItem(IDC_STATUSTEXT)->SetWindowTextW(t); } BEGIN_MESSAGE_MAP(CtabCtrlStackOverflowDlg, CDialogEx) ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDADDTAB, &CtabCtrlStackOverflowDlg::OnBnClickedAddtab) ON_BN_CLICKED(IDC_GETITEM0, &CtabCtrlStackOverflowDlg::OnBnClickedGetitem0) ON_BN_CLICKED(IDCLOSE, &CtabCtrlStackOverflowDlg::OnBnClickedClose) END_MESSAGE_MAP() // CtabCtrlStackOverflowDlg message handlers BOOL CtabCtrlStackOverflowDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here return TRUE; // return TRUE unless you set the focus to a control } // If you add a minimize button to your dialog, you will need the code below // to draw the icon. For MFC applications using the document/view model, // this is automatically done for you by the framework. void CtabCtrlStackOverflowDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // device context for painting SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); // Center icon in client rectangle int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // Draw the icon dc.DrawIcon(x, y, m_hIcon); } else { CDialogEx::OnPaint(); } } // The system calls this function to obtain the cursor to display while the user drags // the minimized window. HCURSOR CtabCtrlStackOverflowDlg::OnQueryDragIcon() { return static_cast<HCURSOR>(m_hIcon); } void CtabCtrlStackOverflowDlg::OnBnClickedAddtab() { static int tabCtr = 0; tabData td; td.tabItemHeader.pszText = _T("TabX"); td.tabItemHeader.mask = TCIF_TEXT; td.gpu.name = L"AMD NVIDIA"; td.gpu.busid = 101; int status = TabCtrl_InsertItem(tabCtrl1.GetSafeHwnd(), tabCtr, &td); wchar_t *t = L""; if (status == -1) { t = L"TabCtrl_InsertItem() Fail"; } else { t = L"TabCtrl_InsertItem() success"; } GetDlgItem(IDC_STATUSTEXT)->SetWindowTextW(t); tabCtr++; } void CtabCtrlStackOverflowDlg::OnBnClickedGetitem0() { tabData td2; td2.tabItemHeader.pszText = new TCHAR[20]; td2.tabItemHeader.cchTextMax = 20; td2.tabItemHeader.mask = TCIF_TEXT; td2.gpu.busid = 0; if (TabCtrl_GetItem(tabCtrl1.GetSafeHwnd(), 0, &td2) == TRUE) { std::wstring text = td2.tabItemHeader.pszText; text += std::wstring(L" ") + td2.gpu.name; GetDlgItem(IDC_STATUSTEXT)->SetWindowTextW(text.c_str()); } else { GetDlgItem(IDC_STATUSTEXT)->SetWindowTextW(_T("TabCtrl_GetItem() error")); } } void CtabCtrlStackOverflowDlg::OnBnClickedClose() { CDialog::OnCancel(); }
头文件:
// tabCtrlStackOverflowDlg.h : header file // #pragma once #include "afxcmn.h" // CtabCtrlStackOverflowDlg dialog class CtabCtrlStackOverflowDlg : public CDialogEx { // Construction public: CtabCtrlStackOverflowDlg(CWnd* pParent = NULL); // standard constructor // Dialog Data #ifdef AFX_DESIGN_TIME enum { IDD = IDD_TABCTRLSTACKOVERFLOW_DIALOG }; #endif protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support // Implementation protected: HICON m_hIcon; // Generated message map functions virtual BOOL OnInitDialog(); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() public: CTabCtrl tabCtrl1; afx_msg void OnBnClickedAddtab(); afx_msg void OnBnClickedGetitem0(); afx_msg void OnBnClickedClose(); };
解决方案总结
从Barmak Shemirani 的回答来看,这是我的代码无法正常工作的 3 个原因。必须阅读他的答案才能更好地理解。
TCIF_PARAM
必须在掩码中设置,同时执行TCM_INSERTITEM
和TCM_GETITEM
。- 我正在使用在堆栈上创建的局部变量(tabData td2;对象)。一旦超出范围,对该变量的引用就会变得无效。
- 在用于的结构中使用 std::wstring
TCM_INSERTITEM
。最好使用可以准确确定大小的数据类型(如普通的旧数据类型。)。
正如 Barmak Shemirani 在评论中指出的那样,文档TCITEMHEADER
很少。他的回答提供了详尽的解释。