4

プロパティをボタンとして設定したり、プロパティにボタンを追加したりする方法はありますか?グリッドサンプルを見ると、次のようなことができることに気づきました。

wxPGEditor* editorButton = wxPropertyGrid::RegisterEditorClass(someButton);
propertyGrid->SetPropertyEditor(wxT("Name"), editorButton );

ただし、1つのタイプだけでなく、これがサポートするさまざまなボタンが必要です。

編集:さまざまなボタンとは、エディターの使用から便乗する必要のないボタンを意味します。私がやりたいのは、wxButtonの場合と同じように、onClickコールバック関数を設定することですが、これをプロパティとして設定します。

EDIT2:明確にするために、プロパティフィールド自体をボタンのみにします。

4

1 に答える 1

3

プロパティに保存されているデータに基づいてボタンイベントを中継するカスタム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;
}

これは、(複数のwxPGEditorを作成する代わりに)wxPGPropertyがコールバックを格納する方法を示すための単なる例です。std::functionコールバックストレージ(ここではButtonData)は、またはboost::function代わりに(C ++ 11またはブーストが必要)を格納するように変更できます。

別のアプローチは、コールバック情報をwxPGEditorインスタンスに格納することです。ただし、これには、異なるコールバック関数ごとに1つずつ、複数のエディターインスタンスが必要になります。


アップデート

文字列フィールドをクリックするまで、実際の「ボタン」は表示されません

プロパティが選択されるまで、エディターはアクティブ化されません。wxPGPropertyプロパティの表現を制御するために、カスタムを返すカスタムを作成する必要がwxPGCellRendererあります(編集されていない間)。残念ながら、これは幻想を生み出し、ユーザーがボタンを2回クリックする必要があります。最初にプロパティ(およびエディター)をアクティブにし、次に実際のボタンをアクティブにします。頭に浮かぶ解決策の1つは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;
    }
};

この方法では、プロパティエディタは1つしか存在できないようです。

合計を意味する場合:wxPropertyGrid::RegisterEditorClass単純にエディターをプロパティグリッドに登録します(グリッドがエディターの所有権を取得し、プロパティグリッドが破棄されると自動的に削除されます)。エディターはいくつでも登録できます。プロパティグリッド全体ではなく、特定のプロパティにエディタを設定します。

同時に意味する場合:はい、残念ながら、一度にアクティブにできるプロパティエディタは1つだけです。


ハック

私が前に述べたハックでこれを終えさせてください。

まず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 ...
}
于 2012-11-21T02:17:52.303 に答える