对话框/表单视图上的控件数组






4.58/5 (9投票s)
2005年3月1日
6分钟阅读

79210

1832
在VC++中使用控件数组。
目录
引言
我从 The Code Project 社区获得了巨大的帮助。我觉得我的贡献是向所有人表示感谢的一种方式。这是一个修改后的项目版本,也适用于FormView应用程序。非常感谢Joe的指出。
Visual Basic 具有处理控件数组的功能。
当您有多个相同控件的子窗口时,使用它们的优点如下:
- 当您想修改或检查控件的属性时,代码行数更少(即循环)。
- 您希望对其中一些控件执行(上述第1点)(即前五个或最后五个,每隔一个或每隔三个)。
- 当Windows捕获到滑块或微调控件被滚动时,您希望知道是哪个滑块或微调控件。
- 通过索引直接修改其他控件的便捷性(无需使用
CWnd::UpdateData()
)。 - 指针算术和比较变得可用(减少了
CWnd::GetDlgItem()
的使用)。
正如我所说,这可以在VB中完成,VC++中没有理由不能。你可以做到!只是做法不同。本教程展示了一个如何利用它的示例。
入门
让我们从最合适的地方开始。创建一个MFC EXE应用程序。为简单起见,我将使用基于对话框的应用程序(如果您选择单文档或多文档,请按照AppWizard的步骤6,在基类(左下角)中选择 CFormView
)。
完成这些后,我们现在有了资源编辑器。
我们可以在对话框(或FormView)上放置控件了。
此演示所需的控件包括以下各项:
- 编辑框
- 微调按钮控件
- 滑块控件
逐一选择它们并相应地放置。将它们放置在对话框上后,我们必须将滑块设置为具有刻度线。因此,只选择滑块,然后右键单击鼠标。然后选择“属性”。
在滑块属性中选择“样式”选项卡。选中“刻度线”和“自动刻度”。此外,对于点,选择“顶部/左侧”。然后关闭。这样做是为了您不必为另外两个编辑属性。
然后按住Shift键选择另外两个控件。复制(CTRL-C)并粘贴(CTRL-V)。将粘贴的控件移动到其他位置并重复粘贴。对齐它们以使其看起来美观。
完成的对话框
完成这些后,我们现在进入任务中更棘手的部分。
类向导(下一步)
这部分可能最容易出问题。因为向导要么失去了它的锥形帽子,要么魔杖未能处理控件数组。因此,我们必须自己施展魔法来解决这个问题。
现在,我们调用类向导,保留消息映射。我们想选择以下消息:
我们希望将消息映射到所有微调控件和编辑控件。滑块我们稍后处理。
确保选项卡位于“消息映射”上。类名为 CControlArrayDlg
。选择消息 WM_VSCROLL
。这会自动调用函数 OnVScroll
。这适用于所有垂直滚动器(微调按钮)。请注意,FormView应用程序也选择 WM_HSCROLL
。这会自动调用函数 OnHScroll
来处理滑块。
选择控件 IDC_EDIT1
。选择消息 EN_UPDATE
。接受建议的函数名。由于 EN
消息仅作用于每个编辑控件,请对 IDC_EDIT2
和 IDC_EDIT3
重复此操作。跳过编辑更改。
选择“成员变量”选项卡。仅将变量映射到 IDC_EDIT1
、IDC_SLIDER1
和 IDC_SPIN1
(参见注释1)。其他将在下一节中处理。将滑块和编辑控件设置为控制类别。这是微调控件的默认设置。
变量列表
现在我们已经完成了类向导。点击“确定”。
项目编码
小节
现在进入精彩部分。
消息映射滑块(仅适用于基于对话框的应用程序)
现在,我们首先映射滑块(参见注释2),这需要相当多的编码。在 ControlArrayDlg
中选择“类视图”选项卡,然后在弹出菜单中选择“添加成员函数”。右键单击类型为 LRESULT
的函数返回,并将其声明为 OnHScrollSlider(WPARAM wParam, LPARAM lParam)
。最后,将其可访问性设置为 protected
。
如何添加函数的视觉描述
ControlArrayDemo1Dlg.cpp 找到以下文本
BEGIN_MESSAGE_MAP(CControlArraysDemo1Dlg, CDialog)
//{{AFX_MSG_MAP(CControlArraysDemo1Dlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_VSCROLL()
ON_EN_CHANGE(IDC_EDIT1, OnChangeEdit1)
ON_EN_CHANGE(IDC_EDIT2, OnChangeEdit2)
ON_EN_CHANGE(IDC_EDIT3, OnChangeEdit3)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
在 //}}AFX_MSG_MAP
和 END_MESSAGE_MAP()
之间插入 ON_MESSAGE(WM_HSCROLL, OnHScrollSlider)
,使其看起来像这样:
BEGIN_MESSAGE_MAP(CControlArraysDemo1Dlg, CDialog) //{{AFX_MSG_MAP(CControlArraysDemo1Dlg) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_WM_VSCROLL() ON_EN_CHANGE(IDC_EDIT1, OnChangeEdit1) ON_EN_CHANGE(IDC_EDIT2, OnChangeEdit2) ON_EN_CHANGE(IDC_EDIT3, OnChangeEdit3) //}}AFX_MSG_MAP // This is declared outside the AFX_MSG_MAP scope // if later you decided to add more messags if will be deleted ON_MESSAGE(WM_HSCROLL, OnHScrollSlider) END_MESSAGE_MAP()
afx_msg LRESULT OnHScrollSlider(WPARAM wParam, LPARAM lParam);
现在转到文件 ControlArraysDemo1Dlg.h 并找到以下文本
protected:
LRESULT OnHScrollSlider(WPARAM wParam, LPARAM lParam);
HICON m_hIcon;
将 LRESULT OnHScrollSlider(WPARAM wParam, LPARAM lParam);
拖放或剪切粘贴到 //}}AFX_MSG
和 DECLARE_MESSAGE_MAP()
之间,使其看起来像这样:
protected: HICON m_hIcon; // Generated message map functions //{{AFX_MSG(CControlArraysDemo1Dlg) virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); afx_msg void OnChangeEdit1(); afx_msg void OnChangeEdit2(); afx_msg void OnChangeEdit3(); //}}AFX_MSG // This is declared outside the AFX_MSG scope // if later you decided to add more messags if will be deleted LRESULT OnHScrollSlider(WPARAM wParam, LPARAM lParam); DECLARE_MESSAGE_MAP()
保持在头文件中,我们将处理一些属性。
声明控件数组,将变量连接到控件
在 ControlArraysDemo1Dlg.h 或 ControlArraysDemo2View.h 中找到以下代码片段
// Dialog Data //{{AFX_DATA(CControlArraysDemo1Dlg) enum { IDD = IDD_CONTROLARRAYS_DIALOG }; CSpinButtonCtrl m_spin; CSliderCtrl m_slider; CEdit m_edit; //}}AFX_DATA // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CControlArraysDemo1Dlg)
将 m_spin
声明为 CSpinButtonCtrl
类型的数组,m_slider
声明为 CSliderCtrl
类型的数组,m_edit
声明为 CEdit
类型的数组,每个数组包含 3 个元素,使其看起来像这样:
// Dialog Data //{{AFX_DATA(CControlArraysDemo1Dlg) enum { IDD = IDD_CONTROLARRAYS_DIALOG }; CSpinButtonCtrl m_spin[3]; CSliderCtrl m_slider[3]; CEdit m_edit[3]; //}}AFX_DATA // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CControlArraysDemo1Dlg) protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //}}AFX_VIRTUAL
完成后,我们可以开始“认真”的编码了。
最后一部分,方法编码
变量与控件连接方式表
控件 ID | 连接变量 |
IDC_EDIT1 |
m_edit[0] |
IDC_EDIT2 |
m_edit[1] |
IDC_EDIT3 |
m_edit[2] |
IDC_SLIDER1 |
m_slider[0] |
IDC_SLIDER2 |
m_slider[1] |
3IDC_SLIDER1 |
m_slider[2] |
IDC_SPIN1 |
m_spin[0] |
IDC_SPIN2 |
m_spin[1] |
IDC_SPIN3 |
m_spin[2] |
让我们转到文件 ControlArrayDlg.cpp 并使用上表转到函数 DoDataExchange
。修改以下代码:
CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CControlArraysDemo1Dlg) DDX_Control(pDX, IDC_SPIN1, m_spin); DDX_Control(pDX, IDC_SLIDER1, m_slider); DDX_Control(pDX, IDC_EDIT1, m_edit); //}}AFX_DATA_MAP
to
CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CControlArraysDemo1Dlg) DDX_Control(pDX, IDC_EDIT1, m_edit[0]); DDX_Control(pDX, IDC_EDIT2, m_edit[1]); DDX_Control(pDX, IDC_EDIT3, m_edit[2]); DDX_Control(pDX, IDC_SLIDER1, m_slider[0]); DDX_Control(pDX, IDC_SLIDER2, m_slider[1]); DDX_Control(pDX, IDC_SLIDER3, m_slider[2]); DDX_Control(pDX, IDC_SPIN1, m_spin[0]); DDX_Control(pDX, IDC_SPIN2, m_spin[1]); DDX_Control(pDX, IDC_SPIN3, m_spin[2]); //}}AFX_DATA_MAP
现在开始编码
创建一个声明为:BOOL StringToNumber(LPCTSTR szText, int &nValue)
的保护函数,用于将 CString
转换为数字,位于以下代码下方:
BOOL CControlArraysDemo1Dlg::StringToNumber(LPCTSTR pszText, int &nValue) { if(!pszText) return FALSE; while(*pszText) { nValue *= 10; if((*pszText < 48)||( *pszText > 57)) { nValue = 0; return FALSE; } nValue += *pszText - 48; pszText++; } return TRUE; }
处理微调控件的代码
void CControlArraysDemo1Dlg::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { // nSBCode is NOT needed because the scroller in the class is spin // First convert the pointer to one of type CSpinButtonCtrl CSpinButtonCtrl *pSpin = reinterpret_cast<CSPINBUTTONCTRL *>(pScrollBar); int nIndex = pSpin - &m_spin[0]; // Get the actual index of spin control CString szValue; // The string for the edit box szValue.Format("%d",nPos); // Format it that number is a string m_bChangedByCtrl = TRUE; //Edit box is updated a control Flag it TRUE m_edit[nIndex].SetWindowText(szValue); //Self explanatory m_slider[nIndex].SetPos(nPos); //ditto CDialog::OnVScroll(nSBCode, nPos, pScrollBar); }
处理滑块控件的变通方法(仅限基于对话框的应用程序)
LRESULT CControlArraysDemo1Dlg::OnHScrollSlider(WPARAM wParam, LPARAM lParam) { // is wParam NOT needed because message is called by a control HWND hwnd; // Interrogator of the slider's handle hwnd = (HWND)lParam; //Casting to a window hadnd CString szValue; // The string for the edit box int nIndex; // For obtaining index of slider control int nPos; //to obtain the slider's position //Is it m_slider[0]? if(m_slider[0].m_hWnd == hwnd) { nIndex = 0; // Yes! then index is 0 } // No, see if it's m_slider[1]? else if(m_slider[1].m_hWnd == hwnd) { nIndex = 1; // Yes! then index is 1 } else { nIndex = 2; // Obviously, the index is 2 } nPos = m_slider[nIndex].GetPos(); // Self explanatory szValue.Format("%d",nPos); // Format it that number is a string m_edit[nIndex].SetWindowText(szValue); // Again self explanatory m_spin[nIndex].SetPos(nPos); //ditto return 0; }
或者在FormView (CControlArrayDemo2View
)中
void CControlArraysDemo2View::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { // Let's ascertain whether or not the slider was slidden if(pScrollBar) { CSliderCtrl *pSlider = reinterpret_cast<CSLIDERCTRL *>(pScrollBar); int nIndex = pSlider - &m_slider[0]; int nValue = m_slider[nIndex].GetPos(); CString szValue; szValue.Format("%d",nValue); m_spin[nIndex].SetPos(nValue); m_edit[nIndex].SetWindowText(szValue); } else { // Your code for handling the conventional scroll bar } CFormView::OnHScroll(nSBCode, nPos, pScrollBar); }
处理 IDC_EDIT1
的更新
void CControlArraysDemo1Dlg::OnUpdateEdit1() { // Check to see if the control is focused // if so modify the other controls at index 0 // if not no further action is taken if(GetFocus() == &m_edit[0]) { CString szText; int nValue = 0; m_edit[0].GetWindowText(szText); StringToNumber(szText,nValue); m_slider[0].SetPos(nValue); m_spin[0].SetPos(nValue); } }
OnUpdateEdit2 (IDC_EDIT2)
和 OnUpdateEdit3 (IDC_EDIT3)
的代码非常相似。区别在于其他控件的索引。
最后是初始化代码,它从 OnInitDialog
调用
void CControlArraysDemo1Dlg::Initialisation() { int nCounter; for(nCounter = 0; nCounter < 3; nCounter++) { m_edit[nCounter].ModifyStyle(0,ES_NUMBER); m_edit[nCounter].SetWindowText(_T("0")); m_edit[nCounter].SetLimitText(2); m_slider[nCounter].SetRange(0,99); m_slider[nCounter].SetTicFreq(10); m_slider[nCounter].SetPos(0); m_spin[nCounter].SetRange(0,99); m_spin[nCounter].SetPos(0); } }
瞧!您有了一个工作的基于对话框的应用程序,它在对话框上使用了控件数组。
注意事项
以下是您在使用控件数组时会遇到的问题:
- 当调用类向导时,它会发出消息:“解析错误,”;。输入行 CSpinButtonCtrl m_spin[3]”(在此示例中)。一个可能的变通方法是使用“全部替换”将“[”替换为双下划线,将“]”替换为乱码字符,并注释掉部分代码。
- 我不知道其背后的原因,是不是对父类的调用是罪魁祸首?带有滑块的消息
WM_HSCROLL
倾向于重置控件。尽管它被滑动到了不同的位置。
结论
我不知道你是否同意我的观点。我发现数组可以使编码变得非常简单。虽然这个演示只使用了三个控件,但它可以扩展到其他控件。你可能会看到我在介绍中说明的原因。
历史
- 版本 1. (2005年2月) 原始应用程序。
- 版本 2. (2005年8月) 错误修复,解决了在FormView上运行代码的问题。使用了
EN_UPDATE
而不是EN_CHANGE
。