Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>You'd have to create a custom wxPGEditor that relays the button event based on data stored in the property. The wxPGProperty can store user defined data (<code>wxClientData</code>) which can be used to store a callback function.</p> <p>First you have to define the function signature of the handler that will be called on the button event.</p> <pre><code>typedef bool (wxEvtHandler::*ButtonEventMethod)(wxPGProperty*); </code></pre> <p>The handler receives the attached property as parameter and should return true if the property was modified.</p> <p>Now a class based on wxClientData is required to store the callback:</p> <pre><code>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; }; </code></pre> <p>This class simply stores the address of the method and the class it belongs to. The editor will extract this object from the property and execute the <code>call()</code> method which in turn executes the callback:</p> <pre><code>class ButtonEventEditor : public wxPGTextCtrlAndButtonEditor { protected: virtual bool OnEvent(wxPropertyGrid* propgrid, wxPGProperty* property, wxWindow* wnd_primary, wxEvent&amp; 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&lt;ButtonData*&gt;(property-&gt;GetClientObject()) ) // call the method return btn-&gt;call(property); return wxPGTextCtrlAndButtonEditor::OnEvent(propgrid,property,wnd_primary,event); } }; </code></pre> <p>To wrap it up, a function to "bind" a method to a property can be written:</p> <pre><code>void BindButton(wxPGProperty* property, ButtonEventMethod method, wxEvtHandler* handler) { property-&gt;SetClientObject(new ButtonData(method,handler)); } </code></pre> <p>And a macro to make it even simpler:</p> <pre><code>#define BIND_BUTTON(property,method,handler,editor) \ property-&gt;SetEditor(editor); \ BindButton(property,static_cast&lt;ButtonEventMethod&gt;(method),handler) </code></pre> <p>Now you can bind member functions to the properties:</p> <pre><code>// register the editor wxPGEditor* editor = propertyGrid-&gt;RegisterEditorClass(new ButtonEventEditor()); // create a property wxPGProperty* prop = propertyGrid-&gt;Append(new wxStringProperty("StringProperty")); // bind method foo to the property BIND_BUTTON(prop,&amp;Frame::foo,this,editor); </code></pre> <p>And the method <code>foo</code> could look like this:</p> <pre><code>bool Frame::foo(wxPGProperty* p) { p-&gt;SetValue("foo"); return true; } </code></pre> <p>This is just an example to demonstrate how the wxPGProperty can store the callback (instead of creating multiple wxPGEditors). The callback storage (here ButtonData) could be modified to store an <a href="http://en.cppreference.com/w/cpp/utility/functional/function" rel="nofollow"><code>std::function</code></a> or <a href="http://www.boost.org/doc/libs/1_52_0/doc/html/function.html" rel="nofollow"><code>boost::function</code></a> instead (which would require C++11 or boost). </p> <p>An alternative approach would be to store the callback information in the wxPGEditor instance. However, that would require multiple editor instances, one for each different callback function.</p> <hr> <p><strong>UPDATE</strong></p> <blockquote> <p>the actual "button" does not show up until you click in the string field</p> </blockquote> <p>The editor is not activated until the property is selected. You would have to create a custom <code>wxPGProperty</code> returning a custom <code>wxPGCellRenderer</code> to take control over the representation of the property (while it is not being edited). Unfortunately, this would create an illusion and require the user to click the button twice: First to activate the property (and the editor) and then the actual button. One solution that comes to mind is either displaying a text like <code>Click to edit</code> in the cell or a short summary of the property value.</p> <p>While it is possible to have the custom property create a button and ignore the editor system, the property grid is not designed to work that way and this 'hack' may cause some problems. However, i added that hack at the end.</p> <blockquote> <p>there is a string field, when I just want the button to take up the entire property</p> </blockquote> <p>That is just a side effect from the editor i used as base class for the example and it's easy to change. Since the wxTextCtrl is not needed, simply base the editor on <code>wxPGEditor</code> directly and create the controls (a single button) yourself:</p> <pre><code>class ButtonEventEditor : public wxPGEditor { protected: virtual wxPGWindowList CreateControls(wxPropertyGrid* propgrid, wxPGProperty* property, const wxPoint&amp; pos, const wxSize&amp; 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&amp; event) const { if( event.GetEventType() == wxEVT_COMMAND_BUTTON_CLICKED ) if( ButtonData* btn = dynamic_cast&lt;ButtonData*&gt;(property-&gt;GetClientObject()) ) return btn-&gt;call(property); return false; } }; </code></pre> <blockquote> <p>with this method, it seems there could only be one property editor</p> </blockquote> <p>If you mean total: <code>wxPropertyGrid::RegisterEditorClass</code> simply registers an editor with the property grid (the grid will take ownership of the editor and automatically delete it when the property grid is destroyed). You can register as many editors as you like. You set the editor to a specific property, not the entire property grid.</p> <p>If you mean at the same time: yes, unfortunately only one property editor can be active at any given time.</p> <hr> <p><strong>The hack</strong></p> <p>Let me finish this up with the hack i previously mentioned.</p> <p>First we need a custom <code>wxPGCellRenderer</code> to position the button on the property grid:</p> <pre><code>class ButtonMover : public wxPGCellRenderer { public: // pointer to the button from the property ButtonMover(wxButton* btn) : _btn(btn) {} protected: virtual bool Render(wxDC &amp;dc, const wxRect &amp;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-&gt;GetClientRect().width-rect.GetX()); _btn-&gt;SetSize(rc); // move button _btn-&gt;Show(); // initially hidden, show once 'rendered' (moved) } return true; } private: wxButton* _btn; }; </code></pre> <p>Now we can create the custom property:</p> <pre><code>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&amp; button, const wxString&amp; label=wxPG_LABEL, const wxString&amp; name=wxPG_LABEL) : wxPGProperty(label,name), _btn(new wxButton(parent,wxID_ANY,button)), _renderer(_btn) { // connect the handler to the button _btn-&gt;Connect(wxEVT_COMMAND_BUTTON_CLICKED,func); _btn-&gt;Hide(); // when it's off the grid, it's not rendered // (thus not moved properly) } protected: virtual wxPGCellRenderer* GetCellRenderer(int column) const { return &amp;_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 }; </code></pre> <p>Having removed the need for an editor, you can now attach the event handler directly to the property:</p> <pre><code>propertyGrid-&gt;Append( new ButtonProperty( propertyGrid, // parent window wxCommandEventHandler(Frame::OnClick1), // event handler "Click me!") // button label ); propertyGrid-&gt;Append( new ButtonProperty( propertyGrid, wxCommandEventHandler(Frame::OnClick2), "Edit this!") ); </code></pre> <p>And the handlers would look like this:</p> <pre><code>void OnClick1(wxCommandEvent&amp; event) { //TODO ... } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload