我的目标是每次(在运行时)单击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
...
}
我知道我的代码很糟糕,所以如果有人有解决方案,提示我的问题或至少知道使我的代码更好一点,我将非常感激。
答案 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)
| 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());
}
}