您需要创建一个自定义的wxPGEditor,根据存储在属性中的数据中继按钮事件。
wxPGProperty可以存储用户定义的数据(wxClientData
),可用于存储回调函数。
首先,您必须定义处理程序的函数签名,该处理程序将在按钮事件上调用。
typedef bool (wxEvtHandler::*ButtonEventMethod)(wxPGProperty*);
处理程序接收附加属性作为参数,如果属性已被修改,则应返回true。
现在需要基于wxClientData的类来存储回调函数。
struct ButtonData : wxClientData {
ButtonData(ButtonEventMethod method, wxEvtHandler* handler)
: _method(method), _handler(handler) {}
bool call(wxPGProperty* property) {
return (*_handler.*_method)(property);
}
private:
ButtonEventMethod _method;
wxEvtHandler* _handler;
};
这个类仅存储方法的地址和所属类。编辑器将从属性中提取这个对象,并执行 call()
方法,进而执行回调函数:
class ButtonEventEditor : public wxPGTextCtrlAndButtonEditor {
protected:
virtual bool OnEvent(wxPropertyGrid* propgrid, wxPGProperty* property,
wxWindow* wnd_primary, wxEvent& event) const {
if( event.GetEventType() == wxEVT_COMMAND_BUTTON_CLICKED )
if( ButtonData* btn = dynamic_cast<ButtonData*>(property->GetClientObject()) )
return btn->call(property);
return wxPGTextCtrlAndButtonEditor::OnEvent(propgrid,property,wnd_primary,event);
}
};
总之,可以编写一个将方法“绑定”到属性的函数:
void BindButton(wxPGProperty* property, ButtonEventMethod method, wxEvtHandler* handler) {
property->SetClientObject(new ButtonData(method,handler));
}
还可以创建一个宏,使其变得更加简单:
property->SetEditor(editor)
BindButton(property,static_cast<ButtonEventMethod>(method),handler)
现在,您可以将成员函数绑定到属性上:
wxPGEditor* editor = propertyGrid->RegisterEditorClass(new ButtonEventEditor());
wxPGProperty* prop = propertyGrid->Append(new wxStringProperty("StringProperty"));
BIND_BUTTON(prop,&Frame::foo,this,editor);
方法foo
可能长成这样:
bool Frame::foo(wxPGProperty* p) {
p->SetValue("foo");
return true;
}
这只是一个示例,演示了 wxPGProperty 如何存储回调函数(而不是创建多个 wxPGEditor)。回调函数存储(这里是 ButtonData)可以修改为存储一个 std::function
或 boost::function
(这需要 C++11 或 boost)。
另一种方法是将回调信息存储在 wxPGEditor 实例中。但是,这将需要多个编辑器实例,每个实例对应一个不同的回调函数。
更新
实际的“按钮”直到点击字符串字段才会显示出来
编辑器在属性被选择之前不会激活。
您需要创建一个自定义的 wxPGProperty
并返回一个自定义的 wxPGCellRenderer
,以控制属性的表示方式(在未进行编辑时)。
不幸的是,这会创建一种假象,并要求用户点击两次按钮:首先激活属性(和编辑器),然后再单击实际的按钮。
一个解决方案是在单元格中显示像 点击以编辑
这样的文本或属性值的简短摘要。
虽然可以让自定义属性创建按钮并忽略编辑器系统,但是属性网格不是设计成那样工作的,这种“黑客”方式可能会导致一些问题。但是,我在最后添加了这个 hack。
当我只想让按钮占用整个属性时,有一个字符串字段
这只是使用示例基类 wxTextCtrl 的副作用,很容易更改。由于不需要 wxTextCtrl,因此可以直接基于 wxPGEditor
创建编辑器并自己创建控件(一个单独的按钮):
class ButtonEventEditor : public wxPGEditor {
protected:
virtual wxPGWindowList CreateControls(wxPropertyGrid* propgrid,
wxPGProperty* property, const wxPoint& pos, const wxSize& size) const {
return wxPGWindowList(new wxButton(propgrid,wxPG_SUBID1,"Edit",pos,size));
}
virtual void UpdateControl(wxPGProperty* property, wxWindow* ctrl) const {}
virtual bool OnEvent(wxPropertyGrid* propgrid, wxPGProperty* property,
wxWindow* wnd_primary, wxEvent& event) const {
if( event.GetEventType() == wxEVT_COMMAND_BUTTON_CLICKED )
if( ButtonData* btn = dynamic_cast<ButtonData*>(property->GetClientObject()) )
return btn->call(property);
return false;
}
};
用这种方法,似乎只能有一个属性编辑器。
如果您的意思是总数:wxPropertyGrid :: RegisterEditorClass只是在属性网格中注册一个编辑器(网格将拥有编辑器并在销毁属性网格时自动删除它)。您可以注册任意数量的编辑器。您将编辑器设置为特定属性,而不是整个属性网格。
如果您的意思是同时:是的,遗憾的是,在任何给定时间只能有一个属性编辑器处于活动状态。
黑客方法
让我以前提到的黑客方式完成。
首先,我们需要一个自定义的wxPGCellRenderer来定位属性网格上的按钮:
class ButtonMover : public wxPGCellRenderer {
public:
ButtonMover(wxButton* btn) : _btn(btn) {}
protected:
virtual bool Render(wxDC &dc, const wxRect &rect,
const wxPropertyGrid *propertyGrid, wxPGProperty *property,
int column, int item, int flags) const {
if( column == 0 ) {
wxRect rc(rect);
rc.SetWidth(propertyGrid->GetClientRect().width-rect.GetX());
_btn->SetSize(rc);
_btn->Show();
}
return true;
}
private:
wxButton* _btn;
};
现在我们可以创建自定义属性:
class ButtonProperty : public wxPGProperty {
public:
ButtonProperty(wxWindow* parent, wxObjectEventFunction func,
const wxString& button, const wxString& label=wxPG_LABEL,
const wxString& name=wxPG_LABEL)
: wxPGProperty(label,name), _btn(new wxButton(parent,wxID_ANY,button)),
_renderer(_btn) {
_btn->Connect(wxEVT_COMMAND_BUTTON_CLICKED,func);
_btn->Hide();
}
protected:
virtual wxPGCellRenderer* GetCellRenderer(int column) const {
return &_renderer;
}
virtual const wxPGEditor* DoGetEditorClass () const {
return 0;
}
private:
wxButton* _btn;
mutable ButtonMover _renderer;
};
无需使用编辑器,现在可以直接将事件处理程序附加到属性:
propertyGrid->Append( new ButtonProperty(
propertyGrid,
wxCommandEventHandler(Frame::OnClick1),
"Click me!")
);
propertyGrid->Append( new ButtonProperty(
propertyGrid,
wxCommandEventHandler(Frame::OnClick2),
"Edit this!")
);
处理程序应该如下所示:
void OnClick1(wxCommandEvent& event) {
//TODO ...
}