如何正确删除控件行并在该位置动态创建一个新控件?

时间:2019-06-05 07:04:21

标签: c++ mfc

我的目标是每次(在运行时)单击Add button时都创建一行动态控件,如下所示:

|             Combo box             |             |Add Button|

|Static Ctrl| |Edit Ctrl| |Edit Ctrl| |Edit Ctrl| |Delete Button|
|Static Ctrl| |Edit Ctrl| |Edit Ctrl| |Edit Ctrl| |Delete Button|(*)
|Static Ctrl| |Edit Ctrl| |Edit Ctrl| |Edit Ctrl| |Delete Button|

例如,如果我单击Delete Button(*),它将删除整行(包括该行Delete button)。然后,当再次单击Add时,新创建的行将出现在同一位置,或者如果我可以使下面的所有行向上移动,甚至会更好,则新添加的行将出现在底部。

这是我写的一些代码:

int CSettingDlg::Getid() // increase the id by 1 each time it was called
{
    id = id + 1; // int id = 4000 in the '.h' file
    return id;
}
int CSettingDlg::AddControlSet() // Add a row of control
{
    int index = 0;
    indexStr.Format(_T("%d"), index + 1);

    GetDlgItem(IDC_TEST1)->GetWindowRect(&rcCtrl);
    ScreenToClient(&rcCtrl);

    for (;;)
    {
        rcCtrl.top = rcCtrl.top + index * 35;
        rcCtrl.bottom = rcCtrl.bottom + index * 35;

        StaticText = new CStatic;
        EditBox = new CEdit;
        EditBox2 = new CEdit;
        EditBox3 = new CEdit;
        Delete = new CButton;

        StaticText->Create((indexStr), WS_CHILD | WS_VISIBLE | ES_READONLY | SS_NOTIFY, CRect(rcCtrl.left -= 163, rcCtrl.top += 5, rcCtrl.right -= 270, rcCtrl.bottom), this, Getid());

        EditBox->Create(WS_CHILD | WS_VISIBLE | WS_BORDER, CRect(rcCtrl.left += 28, rcCtrl.top -= 5, rcCtrl.right += 134, rcCtrl.bottom), this, Getid());

        EditBox2->Create(WS_CHILD | WS_VISIBLE | WS_BORDER | ES_READONLY, CRect(rcCtrl.left += 135, rcCtrl.top, rcCtrl.right += 136, rcCtrl.bottom), this, Getid());

        EditBox3->Create(WS_CHILD | WS_VISIBLE | WS_BORDER, CRect(rcCtrl.left += 135, rcCtrl.top, rcCtrl.right += 172, rcCtrl.bottom), this, Getid());

        Delete->Create(_T("Del"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, CRect(rcCtrl.left += 191, rcCtrl.top, rcCtrl.right += 101, rcCtrl.bottom), this, Getid());

        index++;
        return TRUE;
        nCount++;
    }
}

这是删除行的基本思路:

void CSettingDlg::OnBnClickedDeleteSettingDlg(UINT nID)
{
     nID == Getid();
     switch (nID)
     {
          case 3005: //3005 is the 1st Delete Button's ID
               for (; nID > 3000; nID--)
                    GetDlgItem(nID)->DestroyWindow(); //destroy all controls that have ID from 3001 to 3005
           nCount--; //This variable is not relevant
           break;

          case 3010: //The 2nd Delete Button's ID
          ...
}

我知道我的代码很糟糕,所以如果有人有解决方案,提示我的问题或至少知道使我的代码更好一点,我将非常感激。

1 个答案:

答案 0 :(得分:2)

您可以使用向量来动态添加/删除按钮。请注意,每个new CWnd指针都需要一个相应的delete以避免资源泄漏,因此请确保在删除按钮时进行清理。为简单起见,您可以将每一行的控件放入一个结构中,如下例所示。添加或删除行时,还必须重新定位控件。

struct control_set
{
    CStatic st;
    CEdit e1, e2, e3;
    CButton bn;
};
std::vector<control_set*> vec;

CSettingDlg::~CSettingDlg()
{
    //for(auto &e : vec) delete e; <- remove this
}

void CSettingDlg::PostNcDestroy() // <- add this
{
    CDialog::PostNcDestroy();
    for(auto &e : vec)
        delete e;
    vec.clear();
}

void CSettingDlg::AddControlSet()
{
    vec.push_back(new control_set);
    vec.back()->st.Create(_T("text"), WS_CHILD | WS_VISIBLE, CRect(0, 0, 0, 0), this, 0);
    vec.back()->e1.Create(WS_CHILD | WS_VISIBLE | WS_BORDER, CRect(0, 0, 0, 0), this, 0);
    vec.back()->e2.Create(WS_CHILD | WS_VISIBLE | WS_BORDER, CRect(0, 0, 0, 0), this, 0);
    vec.back()->e3.Create(WS_CHILD | WS_VISIBLE | WS_BORDER, CRect(0, 0, 0, 0), this, 0);
    vec.back()->bn.Create(_T("del"), WS_CHILD | WS_VISIBLE, CRect(0, 0, 0, 0), this, 0);
    resize_controls();
}

void CSettingDlg::OnBnClickedDeleteSettingDlg(UINT id)
{
    UINT row = id / 100;
    //add more checks to make sure this is the ID from delete buttons
    if(row < 0 || row >= vec.size()) return;
    delete vec[row];
    vec.erase(vec.begin() + row);
    resize_controls();
}

void CSettingDlg::resize_controls()
{
    CRect rc(10, 20, 10 + 50, 20 + 14);
    MapDialogRect(&rc);
    for(size_t i = 0; i < vec.size(); i++)
    {
        std::vector<CWnd*> tmp{
          &vec[i]->st, &vec[i]->e1, &vec[i]->e2, &vec[i]->e3, &vec[i]->bn};
        CRect r = rc;
        for(size_t j = 0; j < tmp.size(); j++)
        {
            tmp[j]->MoveWindow(r);
            tmp[j]->SetDlgCtrlID(i * 100 + j);
            tmp[j]->SetFont(GetFont());
            r.OffsetRect(rc.Width() + 2, 0);
        }
        rc.OffsetRect(0, rc.Height() + 2);
    }
}

您要为删除按钮添加消息处理程序:

ON_COMMAND_RANGE(4, 904, OnBnClickedDeleteSettingDlg)


或者,您可以如下设置控件的位置。首先,将5个虚拟静态控件放入对话框资源中,它们具有以下ID:

| IDC_REF_STATIC | IDC_REF_EDIT1 | IDC_REF_EDIT2 | IDC_REF_EDIT3 | IDC_REF_BUTTON |

这些控件可以隐藏。

然后使用这些虚拟控件的坐标来放置新控件。您可以将矩形偏移到下一行。

void CSettingDlg::resize_controls()
{
    CWnd *st = GetDlgItem(IDC_REF_STATIC);
    CWnd *e1 = GetDlgItem(IDC_REF_EDIT1);
    CWnd *e2 = GetDlgItem(IDC_REF_EDIT2);
    CWnd *e3 = GetDlgItem(IDC_REF_EDIT3);
    CWnd *bn = GetDlgItem(IDC_REF_BUTTON);
    ASSERT(st && e1 && e2 && e3 && bn);

    CRect r, rc;
    st->GetWindowRect(&rc);
    ScreenToClient(&rc);

    for(size_t i = 0; i < vec.size(); i++)
    {
        //reposition the static control
        st->GetWindowRect(&r);
        ScreenToClient(&r);
        r.MoveToY(rc.top);
        vec[i]->st.MoveWindow(r);

        //edit1
        e1->GetWindowRect(&r);
        ScreenToClient(&r);
        r.MoveToY(rc.top);
        vec[i]->e1.MoveWindow(r);

        //edit2
        e2->GetWindowRect(&r);
        ScreenToClient(&r);
        r.MoveToY(rc.top);
        vec[i]->e2.MoveWindow(r);

        //edit3
        e3->GetWindowRect(&r);
        ScreenToClient(&r);
        r.MoveToY(rc.top);
        vec[i]->e3.MoveWindow(r);

        //button
        bn->GetWindowRect(&r);
        ScreenToClient(&r);
        r.MoveToY(rc.top);
        vec[i]->bn.MoveWindow(r);

        //move rc down by one row
        rc.OffsetRect(0, rc.Height() + 2);

        //Set the font for each control
        //Also set id for each control, based on the row
        vec[i]->st.SetDlgCtrlID(i * 100 + 1);
        vec[i]->st.SetFont(GetFont());

        vec[i]->e1.SetDlgCtrlID(i * 100 + 2);
        vec[i]->e1.SetFont(GetFont());

        vec[i]->e2.SetDlgCtrlID(i * 100 + 3);
        vec[i]->e2.SetFont(GetFont());

        vec[i]->e3.SetDlgCtrlID(i * 100 + 4);
        vec[i]->e3.SetFont(GetFont());

        vec[i]->bn.SetDlgCtrlID(i * 100 + 5);
        vec[i]->bn.SetFont(GetFont());
    }
}