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

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.58/5 (9投票s)

2005年3月1日

6分钟阅读

viewsIcon

79210

downloadIcon

1832

在VC++中使用控件数组。

目录

引言

我从 The Code Project 社区获得了巨大的帮助。我觉得我的贡献是向所有人表示感谢的一种方式。这是一个修改后的项目版本,也适用于FormView应用程序。非常感谢Joe的指出。

Visual Basic 具有处理控件数组的功能。

当您有多个相同控件的子窗口时,使用它们的优点如下:

  1. 当您想修改或检查控件的属性时,代码行数更少(即循环)。
  2. 您希望对其中一些控件执行(上述第1点)(即前五个或最后五个,每隔一个或每隔三个)。
  3. 当Windows捕获到滑块或微调控件被滚动时,您希望知道是哪个滑块或微调控件。
  4. 通过索引直接修改其他控件的便捷性(无需使用 CWnd::UpdateData())。
  5. 指针算术和比较变得可用(减少了 CWnd::GetDlgItem() 的使用)。

正如我所说,这可以在VB中完成,VC++中没有理由不能。你可以做到!只是做法不同。本教程展示了一个如何利用它的示例。

入门

让我们从最合适的地方开始。创建一个MFC EXE应用程序。为简单起见,我将使用基于对话框的应用程序(如果您选择单文档或多文档,请按照AppWizard的步骤6,在基类(左下角)中选择 CFormView)。

完成这些后,我们现在有了资源编辑器。

我们可以在对话框(或FormView)上放置控件了。

此演示所需的控件包括以下各项:

  1. 编辑框
  2. 微调按钮控件
  3. 滑块控件

逐一选择它们并相应地放置。将它们放置在对话框上后,我们必须将滑块设置为具有刻度线。因此,只选择滑块,然后右键单击鼠标。然后选择“属性”。

在滑块属性中选择“样式”选项卡。选中“刻度线”和“自动刻度”。此外,对于点,选择“顶部/左侧”。然后关闭。这样做是为了您不必为另外两个编辑属性。

Control Arrays

然后按住Shift键选择另外两个控件。复制(CTRL-C)并粘贴(CTRL-V)。将粘贴的控件移动到其他位置并重复粘贴。对齐它们以使其看起来美观。

完成的对话框

完成这些后,我们现在进入任务中更棘手的部分。

类向导(下一步)

这部分可能最容易出问题。因为向导要么失去了它的锥形帽子,要么魔杖未能处理控件数组。因此,我们必须自己施展魔法来解决这个问题。

现在,我们调用类向导,保留消息映射。我们想选择以下消息:

我们希望将消息映射到所有微调控件和编辑控件。滑块我们稍后处理。

确保选项卡位于“消息映射”上。类名为 CControlArrayDlg。选择消息 WM_VSCROLL。这会自动调用函数 OnVScroll。这适用于所有垂直滚动器(微调按钮)。请注意,FormView应用程序也选择 WM_HSCROLL。这会自动调用函数 OnHScroll 来处理滑块

选择控件 IDC_EDIT1。选择消息 EN_UPDATE。接受建议的函数名。由于 EN 消息仅作用于每个编辑控件,请对 IDC_EDIT2IDC_EDIT3 重复此操作。跳过编辑更改。

选择“成员变量”选项卡。将变量映射到 IDC_EDIT1IDC_SLIDER1IDC_SPIN1(参见注释1)。其他将在下一节中处理。将滑块和编辑控件设置为控制类别。这是微调控件的默认设置。

变量列表

现在我们已经完成了类向导。点击“确定”。

项目编码

小节

  1. 消息映射基于对话框的滑块
  2. 声明控件数组,将变量连接到控件
  3. 方法编码

现在进入精彩部分。

消息映射滑块(仅适用于基于对话框的应用程序)

现在,我们首先映射滑块(参见注释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_MAPEND_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_MSGDECLARE_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.hControlArraysDemo2View.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);
        }
    
    }

    瞧!您有了一个工作的基于对话框的应用程序,它在对话框上使用了控件数组。

    注意事项

    以下是您在使用控件数组时会遇到的问题:

    1. 当调用类向导时,它会发出消息:“解析错误,”;。输入行 CSpinButtonCtrl m_spin[3]”(在此示例中)。一个可能的变通方法是使用“全部替换”将“[”替换为双下划线,将“]”替换为乱码字符,并注释掉部分代码。
    2. 我不知道其背后的原因,是不是对父类的调用是罪魁祸首?带有滑块的消息 WM_HSCROLL 倾向于重置控件。尽管它被滑动到了不同的位置。

    结论

    我不知道你是否同意我的观点。我发现数组可以使编码变得非常简单。虽然这个演示只使用了三个控件,但它可以扩展到其他控件。你可能会看到我在介绍中说明的原因。

    历史

    • 版本 1. (2005年2月) 原始应用程序。
    • 版本 2. (2005年8月) 错误修复,解决了在FormView上运行代码的问题。使用了 EN_UPDATE 而不是 EN_CHANGE
    © . All rights reserved.