Reputation: 482
I'm coding a wxWidgets GUI that involves dynamically adding and removing rows of controls inside a gridsizer through buttons.
Each row of controls has a "remove" button that triggers an event that:
Now I don't think unbinding the event inside the event handler itself is a good thing. Is there a better way to achieve this behaviour?
this is how I dynamically create my controls
bool filtermanager::add()
{
if (!grid || !box || !form || !bsizer)
return false;
dbgcode(log->d(tag, "add: adding a new filter row"));
// add and index filter in the filter map and refresh layout
filter *flt = new filter(this, box, grid);
filters[flt->removebutton()->GetId()] = flt;
refreshlayout();
return true;
}
filtermanager::filter::filter(filtermanager *parent,
wxStaticBox *box, wxGridSizer *grid)
: parent(parent), grid(grid)
{
controlpool *ctl = parent->ctl;
// initialize filter row elements
property = ctl->makeComboBox(box, "property");
value = ctl->makeTextCtrl(box, "value");
button = ctl->makeButton(box, "Remove");
// add filter row to the grid sizer
grid->SetRows(grid->GetRows() + 1);
grid->Add(property, 0, wxALL | wxEXPAND, 0);
grid->Add(value, 0, wxALL | wxEXPAND, 0);
grid->Add(button, 0, wxALL | wxEXPAND, 0);
// bind remove button
button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &filtermanager::OnRemoveClicked, parent);
}
this is the event handler:
void filtermanager::OnRemoveClicked(wxCommandEvent &e)
{
wxButton *b = dynamic_cast<wxButton *>(e.GetEventObject());
filter *flt = filters[b->GetId()];
dbgcode(log->d(tag,
strfmt() << "OnRemoveClicked: remove button event caught" <<
" property=" << flt->propertycombo() <<
" value=" << flt->valuetext() <<
" button=" << flt->removebutton())
);
removebyflt(flt);
}
void filtermanager::removebyflt(filter *flt)
{
int id = flt->id();
// dealloc filter from the map
delete filters[id];
filters[id] = NULL;
filters.erase(id);
}
filtermanager::filter::~filter()
{
controlpool *ctl = parent->ctl;
// unbind button
button->Unbind(wxEVT_COMMAND_BUTTON_CLICKED, &filtermanager::OnRemoveClicked, parent);
// remove the filter from the gui
if (!grid->Detach(property))
parent->log->e(tag, "~filter: failed to remove property from sizer");
if (!grid->Detach(value))
parent->log->e(tag, "~filter: failed to remove value from sizer");
if (!grid->Detach(button))
parent->log->e(tag, "~filter: failed to remove button from sizer");
grid->SetRows(grid->GetRows() - 1);
// refresh panels as usual
parent->refreshlayout();
ctl->free(property);
ctl->free(value);
ctl->free(button);
}
By the way, the purpose of this GUI is adding search filters for properties in a loaded XML file.
Upvotes: 2
Views: 1653
Reputation: 22688
It's fine to unbind the event from its handler, I really don't see any problem, even potentially, with doing this.
Also, wxWidgets doesn't have any special problems with deleting the controls during run-time, generally speaking, and using a pool of hidden controls seems more trouble than just deleting and recreating them as needed. However, deleting a control from a handler for the event generated by this control is indeed a bad idea and may result in crashes. The canonical solution for this is to hide the control immediately and delete it "slightly later", i.e. during the next event loop iteration. This can be done by explicitly using wxEVT_IDLE
in all version of wxWidgets or, more conveniently, by using CallAfter() in wxWidgets 2.9.5 or later.
Upvotes: 3