一行にパネルを配置することでリストを作成する。
ウィンドウサイズが変わるたびに、
① wxGridSizer::Clear(true);で現在のパネルをすべて削除し
② GridSizerを置いているwxBoxSizerから既存のwxGridSizerをwxBoxSizer::Detachで除外し、
③ 新しいwxGridSizerを作成し、この時に新しい行数を指定
④ 新しいwxGridSizerをGoxSizerにAddする
ということをやる。どうやらwxGridSizerの分割数を更新する方法がないらしく、仕方がないので毎回作り直している。
// 自作リストコントロール class MyList : public wxWindow { std::vector<int> _itemlist; // データリスト int row; int col = 1; int itemcount() { return _itemlist.size(); } int _itemHeight; wxScrollBar* _scroll; wxGridSizer* _gridSizer; wxBoxSizer* _boxSizer; public:
// 縦に配置するアイテム一覧用のGridSizerを作成 wxGridSizer* NewGridSizer() { int vgap = 1;// 垂直方向の間隔 int hgap = 1;// 水平方向の間隔 wxGridSizer* newGridSizer = new wxGridSizer(row, col, vgap, hgap); for (int i = 0; i < row * col; ++i) { //// 新しいパネルを作成 wxPanel* panel = new wxPanel(this, wxID_ANY); //// パネルの背景色を設定 panel->SetBackgroundColour(wxColour(200, 200, 100 * (i + 1) % 256)); //// パネルをgridSizer追加 newGridSizer->Add(panel, 1, wxEXPAND); // パネルにラベルを追加 wxStaticText* label = new wxStaticText(panel, wxID_ANY, ""); wxBoxSizer* labelboxSizer = new wxBoxSizer(wxHORIZONTAL); labelboxSizer->Add(label, 1, wxALIGN_CENTER); panel->SetSizer(labelboxSizer); } return newGridSizer; }
// 水平分割のSizerを作成 // 左側 ... GridSizer アイテム一覧 // 右側 ... スクロールバー wxBoxSizer* NewBoxSizer(wxGridSizer* gridsizer) { // 水平分割のSizer作成 auto boxSizer = new wxBoxSizer(wxHORIZONTAL); boxSizer->Add(gridsizer, 1, wxEXPAND); if (_scroll == nullptr) { //スクロールバー作成 _scroll = new wxScrollBar(this, wxID_ANY, wxDefaultPosition, wxSize(20, -1), wxSB_VERTICAL); boxSizer->Add(_scroll, 0, wxEXPAND); _scroll->Bind(wxEVT_SCROLL_CHANGED, &MyList::OnScroll, this); _scroll->Bind(wxEVT_SCROLL_LINEUP, &MyList::OnScroll, this); _scroll->Bind(wxEVT_SCROLL_LINEDOWN, &MyList::OnScroll, this); _scroll->Bind(wxEVT_SCROLL_THUMBTRACK, &MyList::OnScroll, this); } return boxSizer; }
// 表示行数を変更する void ResetRow() { wxGridSizer* newGridSizer = NewGridSizer(); int scrollpos = 0; // リストを表示しているpanelをすべて削除 if (_gridSizer != nullptr) { _gridSizer->Clear(true); } if (_boxSizer == nullptr) { _boxSizer = NewBoxSizer(newGridSizer); } else { // 既存の_gridSizerをboxSizerから削除 _boxSizer->Detach(_gridSizer); _boxSizer->Detach(_scroll); // _gridSizerを削除 delete _gridSizer; _gridSizer = nullptr; _boxSizer->Add(newGridSizer, 1, wxEXPAND); _boxSizer->Add(_scroll, 0, wxEXPAND); } _gridSizer = newGridSizer; scrollpos = _scroll->GetThumbPosition(); SetSizer(_boxSizer); _scroll->SetScrollbar( scrollpos, // position スクロールバーの位置 row, // thumbSize つまみのサイズ。これは一度に画面上に表示する行数と等しいことが望ましい itemcount(), // range 総行数 2, // pageSize ページ単位でのスクロールを行ったときに移動する量 true // refresh trueにするとスクロールバーの位置を更新する ); // 表示を行う panel_update(); }
MyList( int ItemHeight, std::vector<int>& ITEM_LIST, wxWindow* parent, wxWindowID WINID, wxPoint point, wxSize size, long style, wxString name ) : wxWindow(parent, WINID,point,size,style,name) { _gridSizer = nullptr; _scroll = nullptr; _boxSizer = nullptr; _itemlist = ITEM_LIST; // アイテムの高さとウィンドウの高さから、一回に表示する件数を計算 // この値はgridSizerの行数になる // ウィンドウの高さは変化するので、アイテム高さは完全に固定しないで、 // ItemHeight以上、ItemHeight×2未満の範囲で調整する _itemHeight = ItemHeight; // ウィンドウサイズ変更時のイベントをBind Bind(wxEVT_SIZE, &MyList::OnSize, this); Centre();// ウィンドウを画面中央に表示 }
// ウィンドウサイズ変更時に呼び出されるイベントハンドラ void OnSize(wxSizeEvent& event) { // ウィンドウの高さから、一回に表示する件数を計算 int newrow = event.GetSize().GetHeight() / _itemHeight; if (newrow < 1) newrow = 1; // 表示行数が変わったら、表示をリセット if (newrow != row) { row = newrow; ResetRow(); } // 再配置 _boxSizer->Layout(); event.Skip(); }
// wxScrollBarをクリックしたときに呼び出されるイベントハンドラ void OnScroll(wxScrollEvent& event) { // スクロールバーの位置を取得 int pos = event.GetPosition(); // pos をデバッグ出力 wxLogDebug(wxString::Format("pos = %d", pos)); panel_update(); }
// panelの内容を更新する void panel_update() { // _scroll の位置を取得 int pos = _scroll->GetThumbPosition(); // offset = つまみの位置 = pos = 一番上に表示するアイテム番号 // 表示アイテム = offset + i int offset = pos * col; // リストの上からi番目にアイテムを表示 for (int i = 0; i < row * col; ++i) { // _gridSizerに登録されているpanelを取得 wxPanel* panel = (wxPanel*)_gridSizer->GetItem(i)->GetWindow(); // panel上にあるlabelを取得 wxStaticText* label = (wxStaticText*)panel->GetChildren()[0]; if ((offset + i) >= itemcount()) { // アイテム数を超えたら *** を表示 label->SetLabelText("***"); } else { if (offset + i == itemcount() - 1) { // 最後のアイテムなら (last item) を表示 label->SetLabelText(wxString::Format("Panel %d (last item)", _itemlist[offset + i])); } else { // それ以外はアイテム番号を表示 label->SetLabelText(wxString::Format("Panel %d", _itemlist[offset + i])); } } } }
};
class MyFrame : public wxFrame { public: void PostCreate() { // セットするデータ std::vector<int> ImageList; for (size_t i = 0; i < 20; i++) { ImageList.push_back(i); } // リスト作成 MyList* list = new MyList(20, ImageList,this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_SIMPLE, "MyList"); this->Layout(); // レイアウトの更新 } MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size) { // CallAfter : 現在処理中のイベントが終わったらPostCreateを実行 // コンストラクタはウィンドウ生成イベント扱い CallAfter(&MyFrame::PostCreate); } private: };