Как правильно удалить строку Control и динамически создать новую в этой позиции? - PullRequest
0 голосов
/ 05 июня 2019

Моя цель - создавать ряд динамических элементов управления каждый раз, когда я нажимаю 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 Ответ

2 голосов
/ 05 июня 2019

Вы можете использовать вектор для динамического добавления / удаления кнопок.Обратите внимание, что каждому указателю 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 фиктивных статических элементов управления в ресурс диалогового окна со следующими идентификаторами:
| 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());
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...