您必須創建一個自定義的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 {
// handle the button event
if(event.GetEventType() == wxEVT_COMMAND_BUTTON_CLICKED)
// extract the client data from the property
if(ButtonData* btn = dynamic_cast<ButtonData*>(property->GetClientObject()))
// call the method
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));
}
和宏,使其更簡單:
#define BIND_BUTTON(property,method,handler,editor) \
property->SetEditor(editor); \
BindButton(property,static_cast<ButtonEventMethod>(method),handler)
現在可以綁定成員函數的性質:
// register the editor
wxPGEditor* editor = propertyGrid->RegisterEditorClass(new ButtonEventEditor());
// create a property
wxPGProperty* prop = propertyGrid->Append(new wxStringProperty("StringProperty"));
// bind method foo to the property
BIND_BUTTON(prop,&Frame::foo,this,editor);
而且可能看起來像這樣foo
方法:
bool Frame::foo(wxPGProperty* p) {
p->SetValue("foo");
return true;
}
這只是證明wxPGProperty如何存儲回調(而不是創建多個wxPGEditors)的例子。回調存儲(此處爲ButtonData)可以修改爲存儲std::function
或boost::function
而不是(這將需要C++ 11或boost)。
另一種方法是將回調信息存儲在wxPGEditor實例中。但是,這需要多個編輯器實例,每個不同的回調函數都有一個。
UPDATE
實際的「按鈕」不顯示,直到您在串場
編輯,直到選中屬性單擊未激活。 您必須創建一個自定義wxPGProperty
,返回一個自定義wxPGCellRenderer
以控制屬性的表示形式(而不是正在編輯)。 不幸的是,這會產生幻覺,並要求用戶點擊兩次按鈕:首先激活屬性(和編輯器),然後是實際的按鈕。 想到的一種解決方案是在單元格中顯示文本Click to edit
,或者顯示屬性值的簡短摘要。
雖然可以讓自定義屬性創建一個按鈕並忽略編輯器系統,但屬性網格並不是以這種方式工作的,這種「黑客」可能會導致一些問題。不過,我在最後加上了黑客。
有一個字符串場,當時我只想按鈕佔據整個財產
這是我用來作爲例如基類的編輯器只是一個副作用,很容易改變。由於不需要在wxTextCtrl,只要立足於wxPGEditor
直接 編輯和創建控件(一個按鈕)自己:
class ButtonEventEditor : public wxPGEditor {
protected:
virtual wxPGWindowList CreateControls(wxPropertyGrid* propgrid,
wxPGProperty* property, const wxPoint& pos, const wxSize& size) const {
// create and return a single button to be used as editor
// size and pos represent the entire value cell: use that to position the button
return wxPGWindowList(new wxButton(propgrid,wxPG_SUBID1,"Edit",pos,size));
}
// since the editor does not need to change the primary control (the button)
// to reflect changes, UpdateControl is just a no-op
virtual void UpdateControl(wxPGProperty* property, wxWindow* ctrl) const {}
// and here we remove the call to the base class because it is abstract
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:
// pointer to the button from the property
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) { // 0 = label, 1 = value
// instead of actually drawing the cell,
// move the button to the cell position:
wxRect rc(rect);
// calculate the full property width
rc.SetWidth(propertyGrid->GetClientRect().width-rect.GetX());
_btn->SetSize(rc); // move button
_btn->Show(); // initially hidden, show once 'rendered' (moved)
}
return true;
}
private:
wxButton* _btn;
};
現在,我們可以創建自定義屬性:
class ButtonProperty : public wxPGProperty {
public:
// [parent] should be the property grid
// [func] is the event handler
// [button] is the button label
// [label] is the property display name (sort name with autosort)
// [name] is the internal property name
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) {
// connect the handler to the button
_btn->Connect(wxEVT_COMMAND_BUTTON_CLICKED,func);
_btn->Hide(); // when it's off the grid, it's not rendered
// (thus not moved properly)
}
protected:
virtual wxPGCellRenderer* GetCellRenderer(int column) const {
return &_renderer; // return button mover
}
virtual const wxPGEditor* DoGetEditorClass() const {
return 0; // not using an editor
}
private:
wxButton* _btn; // the button attached to the property
mutable ButtonMover _renderer; // the button mover
};
已經取消了編輯的需要,你現在可以將事件處理程序直接附加到屬性:
propertyGrid->Append(new ButtonProperty(
propertyGrid, // parent window
wxCommandEventHandler(Frame::OnClick1), // event handler
"Click me!") // button label
);
propertyGrid->Append(new ButtonProperty(
propertyGrid,
wxCommandEventHandler(Frame::OnClick2),
"Edit this!")
);
和處理程序看起來像這樣:
void OnClick1(wxCommandEvent& event) {
//TODO ...
}
請解釋您的意思是'不同的按鈕'。 – ravenspoint