wxWidgetsでは、wxWindowなどを継承してカスタムコントロールを作れる。それを.xrcファイルに定義した場合、.xrcを読み込む wxXmlResource がカスタムコントロールを知らないため、専用のハンドラを定義しなければいけない。
以下のようなカスタムコントロール 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)); } };
以下のように.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>
// .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)
読み込むときは、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); } };