本文介绍了wxWidgets:解除绑定事件作为其触发的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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:

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

现在我不认为在事件处理程序内部解除绑定事件是一件好事.有没有更好的方法来实现这种行为?

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

这是事件处理程序:

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 文件中的属性添加搜索过滤器.

By the way, the purpose of this GUI is adding search filters for properties in a loaded XML file.

推荐答案

解除事件与其处理程序的绑定很好,我真的不认为这样做有任何问题,甚至是潜在的问题.

It's fine to unbind the event from its handler, I really don't see any problem, even potentially, with doing this.

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

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.

这篇关于wxWidgets:解除绑定事件作为其触发的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-26 19:48