スポンサーリンク

wxWidgetsで.xrcファイルからデザインを読み込んで適用(3)カスタムコントロールを読み込み

wxWidgetsでは、wxWindowなどを継承してカスタムコントロールを作れる。それを.xrcファイルに定義した場合、.xrcを読み込む wxXmlResource がカスタムコントロールを知らないため、専用のハンドラを定義しなければいけない。

CMyControl カスタムコントロールのクラス

以下のようなカスタムコントロール CMyControl を定義する

// ボタンとテキストを持つコントロールをパネルで作成
class CMyControl : public wxWindow
{

public:

    // 今回はMyControlXmlHandler側でXRC_MAKE_INSTANCEでインスタンス生成をする
    // MyControlXmlHandlerはデフォルトコンストラクタを呼び出すので定義する
    CMyControl() :wxWindow() {
    }

    // デフォルトコンストラクタでは最小限の初期化しか行わないので
    // 初期化用のCreate関数を定義する必要が出てくる
    bool Create(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name) {
        if (!wxWindow::Create(parent, id, pos, size, style, name)) {
            return false;
        }

        my_create();

        return true;
    }

    // parentを指定するコンストラクタ
    CMyControl(wxWindow* parent) :wxWindow(parent, wxID_ANY) {

        my_create();
    }

    // ウィジェットの初期化関数
    void my_create() {

        // ボタンの作成
        wxButton* myButton = new wxButton(this, wxID_ANY, "ボタン", wxPoint(10, 10), wxSize(150, 50));
		// テキストの作成
        wxTextCtrl* myText = new wxTextCtrl(this, wxID_ANY, "テキスト", wxPoint(10, 70), wxSize(150, 50));

        this->SetBackgroundColour(wxColour(255, 0, 0));

    }

};

myctrl.xrc

以下のように.xrcファイルを定義する

<?xml version="1.0" ?>
<resource version="2.3.0.1">

    <!-- トップレベルはコントロールである必要がある。パネルを作成。 -->
    <object class="wxPanel" name="myContainerPanel">
        <bg>#00FF00</bg>
        
        <!-- レイアウト用のサイザーを作成 -->
        <object class="wxBoxSizer" name="myBoxSizer">
            <orient>wxVERTICAL</orient> <!-- レイアウト方法の指定 -->
            <!-- コントロールは sizeritem に入れる -->
            <object class="sizeritem">
                <!-- 周囲に15pxの余白を入れる -->
                <flag>wxALL | wxEXPAND</flag>
                <border>15</border>
                
                <!-- このコントロールはサイザーのサイズ変更で大きさが変わる -->
                <option>1</option>

                <!-- カスタムコントロールを追加 -->
                <object class="MyControl" name="nameMyControl" />

            </object>

        </object>
    </object>
</resource>

CMyControl 用のハンドラを作成

// .xrcから読み込むには、自作ウィジェットをwxXmlResourceが読み込むための
// ハンドラを作成して登録する必要がある
class MyControlXmlHandler : public wxXmlResourceHandler
{
public:
    MyControlXmlHandler() : wxXmlResourceHandler()
    {
        XRC_ADD_STYLE(wxTAB_TRAVERSAL);
        XRC_ADD_STYLE(wxNO_BORDER);
        XRC_ADD_STYLE(wxDOUBLE_BORDER);
        AddWindowStyles();
    }

    // これが必要
    // wxXmlResource::LoadObject() が呼び出されたときに呼び出される関数
    virtual wxObject* DoCreateResource() override
    {
        // 概ねやっていることは以下と同じ:
        // MyControl* control = new MyControl; 
        // 注意として、このマクロはMyControlのデフォルトコンストラクタを呼び出すので
        // 引数なしのコンストラクタが必要
        // 別にXRC_MAKE_INSTANCEを使わなくても、普通にnewしてもいい。
        XRC_MAKE_INSTANCE(control, CMyControl);

        // デフォルトコンストラクタでは最小限の初期化しかされないので
        // Create()を呼び出して、実際の初期化を行う
        // 従ってMyControl::Create()を定義しておく
        control->Create(
            m_parentAsWindow, // 親ウィンドウ
            GetID(),
            GetPosition(),
            GetSize(),
            GetStyle(),
            GetName()
        );


        SetupWindow(control);

        return control;
    }
    
    
    // このハンドラが、xrc内に定義された"MyControl"というウィジェットを作成できることを示すために必要
    virtual bool CanHandle(wxXmlNode* node) override
    {
        return IsOfClass(node, "MyControl");
    }
    
    
    // wxCreateObject() ,GetClassInfo() ,ms_classInfo,wxCreateObject()が定義される
    wxDECLARE_DYNAMIC_CLASS(MyControlXmlHandler);
};

// 自作ウィジェットのハンドラを登録
IMPLEMENT_DYNAMIC_CLASS(MyControlXmlHandler, wxXmlResourceHandler)

.xrcの読み込み

読み込むときは、AddHandlerで、作成したハンドラのインスタンスをwxXmlResourceに渡す。

// ウィンドウ作成
class MyFrame : public wxFrame
{
public:
    void PostCreate() {


        wxXmlResource::Get()->InitAllHandlers();// 初期化

        // カスタムコントロールのハンドラを登録
        wxXmlResource::Get()->AddHandler(new MyControlXmlHandler);

        wxXmlResource::Get()->Load(R"(C:\test\myctrl.xrc)"); // ファイル読み込み
        wxXmlResource::Get()->LoadPanel(this, "myContainerPanel"); // GUIの生成

        this->Layout(); // レイアウトの更新

    }

    MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
        : wxFrame(NULL, wxID_ANY, title, pos, size)
    {       
        // CallAfter : 現在処理中のイベントが終わったらPostCreateを実行
        // コンストラクタはウィンドウ生成イベント扱い
        CallAfter(&MyFrame::PostCreate);
    }

};

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)


この記事のトラックバックURL: