2

我正在编写一个 wxWidgets GUI,它涉及通过按钮在 gridsizer 中动态添加和删除控件行。

每行控件都有一个“删除”按钮,用于触发以下事件:

  • 从它们的 sizer 中删除控件
  • 隐藏控件
  • 解除绑定删除按钮事件
  • 将可重用控件池中的控件标记为可用(我正在重用控件,因为 wxWidgets 不喜欢在运行时删除内容)

现在我不认为在事件处理程序本身中取消绑定事件是一件好事。有没有更好的方法来实现这种行为?

这就是我动态创建控件的方式

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);
}

这是事件处理程序:

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);
}

顺便说一下,这个 GUI 的目的是为加载的 XML 文件中的属性添加搜索过滤器。

4

1 回答 1

2

将事件与其处理程序解除绑定很好,我真的没有看到任何问题,即使是潜在的,这样做。

此外,wxWidgets 在运行时删除控件并没有任何特殊问题,一般来说,使用隐藏控件池似乎比根据需要删除和重新创建它们更麻烦。但是,从该控件生成的事件的处理程序中删除该控件确实是一个坏主意,并且可能导致崩溃。对此的规范解决方案是立即隐藏控件并“稍晚”将其删除,即在下一次事件循环迭代期间。这可以通过wxEVT_IDLE在所有版本的 wxWidgets 中显式使用来完成,或者更方便的是,通过在 wxWidgets 2.9.5 或更高版本中使用CallAfter()来完成。

于 2013-09-29T10:46:11.147 回答