スポンサーリンク

GridSizerを使用してリストを作成する

一行にパネルを配置することでリストを作成する。

ウィンドウサイズが変わるたびに、

① 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:
};

コメントを残す

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

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


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