【C++/MFC】リストビューの基本的な使い方

 一覧表を表示するコントロールであるリストビュー(CListCtrlクラス)について、最低限の基本的な使い方についてメモ。

開発環境Visual Studio 2022
言語C++
フレームワークMFC
動作確認Windows 11
Windows 10
Windows 7
リストビュー

前提条件

  • ツールボックスより、「List Control」をダイアログに貼る
  • プロパティの「ビュー」は、「レポート」を設定
  • ダイアログクラスにメンバー変数追加
  • 
        CListCtrl m_list;
    
    
  • DoDataExchangeに、IDとメンバー変数の結びつきを設定
  • 
        DDX_Control(pDX, IDC_LIST1, m_list);
    
    

拡張スタイルの設定

 OnInitDialogの「// TODO: 初期化をここに追加します。」より後に記述。 設定したいスタイルをOR条件で指定する。


    m_list.SetExtendedStyle(m_list.GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);

  • GetExtendedStyleは、現状の設定の取得
  • LVS_EX_FULLROWSELECTは、テキスト選択時、1行まるごと反転する。 これを設定しない場合、一番左側の列のみ反転になる
  • 行選択
  • LVS_EX_GRIDLINESは、グリッド線の設定
  • グリッド線
  • リストビューからフォーカスが外れた場合でも選択状態を維持するには、プロパティの「選択を常に表示」をTrueに設定する
  • 選択を常に表示

列の挿入

 最初に列を挿入してから、次に行を挿入する。


    m_list.InsertColumn(列のインデックス, ヘッダーのテキスト, 配置, 列幅の初期値);

  • 「列のインデックス」は、ゼロ開始(以下、インデックスは行・列ともにゼロ開始)
  • 「ヘッダーのテキスト」の文字コードは、UTF-16
  • 「配置」は、LVCFMT_LEFT(左寄せ)、LVCFMT_RIGHT(右寄せ)、LVCFMT_CENTER(中央寄せ)
  • 「列幅の初期値」の単位は、ピクセル
  • 型はリファレンスを参照のこと

 上記の方法では、LVCFMT_RIGHTまたはLVCFMT_CENTERを指定した場合でも、一番左側の列は左寄せになる。 一番左側の列を右寄せ、または、中央寄せにする方法は、 「リストビューの列の属性を変更する」を参照のこと。

テキストの設定


// iLine    行のインデックス(ゼロ開始)
// iCol     列のインデックス(ゼロ開始)
// data     表示するテキスト(UTF-16)
void CXxxxDlg::setItemListView(const int iLine, const int iCol, wchar_t *data)
{
    LVITEM lvItem{};
    lvItem.mask = LVIF_TEXT;
    lvItem.iItem = iLine;
    lvItem.iSubItem = iCol;
    lvItem.pszText = data;

    if (iCol == 0)
    {
        // 行の挿入と、最初の列にテキスト設定
        m_list.InsertItem(&lvItem);
    }
    else
    {
        // 最初以外の列にテキスト設定
        m_list.SetItem(&lvItem);
    }
}

列幅の自動調整


    // ヘッダーのテキストの幅に合わせる(列の挿入後に記述)
    m_list.SetColumnWidth(列のインデックス, LVSCW_AUTOSIZE_USEHEADER);


    // ヘッダー以外のテキストの最大幅に合わせる(テキスト設定後に記述)
    m_list.SetColumnWidth(列のインデックス, LVSCW_AUTOSIZE);

テキストのフォーカスと選択

テキストのフォーカスと選択

 フォーカスは、1行のみで、テキストの周りが点線で囲まれる。 選択は、複数行可能で、テキストの背景の色が反転する。 複数行選択した場合、最後に選択した行にフォーカスが移動する。


    // フォーカスと選択を同時に設定する例
    m_list.SetItemState(行のインデックス, LVIS_FOCUSED | LVIS_SELECTED,
        LVIS_FOCUSED | LVIS_SELECTED);

  • 2番目の引数は設定値(ビット)、3番目の引数はどのビットを設定するかのマスク
  • フォーカスはLVIS_FOCUSED、選択はLVIS_SELECTED

テキストのフォーカスと選択を解除

 逆にゼロを設定する(ビットを落とす)とよい。


    // 全行のフォーカスと選択を解除する例
    for (int i = 0; i < m_list.GetItemCount(); i++)
    {
        m_list.SetItemState(i, 0, LVIS_FOCUSED | LVIS_SELECTED);
    }

フォーカスがある行のインデックスを取得

(1) ダイアログクラスにメンバー関数を追加。(関数名は任意)


    void OnItemChanged(NMHDR *pNMHDR, LRESULT *pResult);

(2) メッセージマップに、以下を追加。


    ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST1, OnItemChanged)

(3) メンバー関数を記述。 リストビューにメッセージが飛ぶ(マウスや矢印キーでテキスト選択)度に、この関数が呼ばれる。


void CXxxxDlg::OnItemChanged(NMHDR *pNMHDR, LRESULT *pResult)
{
    NMLISTVIEW *pListView = reinterpret_cast<NMLISTVIEW *>(pNMHDR);

    if (pListView->uChanged & LVIF_STATE)
    {
        if (pListView->uNewState & LVIS_FOCUSED)
        {
            // pListView->iItemに、フォーカスがある行のインデックスが入っている
            //
            //
            //
        }
    }

    *pResult = 0;
}

選択行のインデックスを取得


    // 最初の選択行の位置を取得
    POSITION pos = m_list.GetFirstSelectedItemPosition();

    if (pos == nullptr)
    {
        // 選択行無し
    }
    else
    {
        while(pos)
        {
            // 現在の選択行のインデックスと、次の選択行の位置を取得
            int nLine = m_list.GetNextSelectedItem(pos);

            // nLineに選択行のインデックスが入っている
            //
            //
            //
        }
    }

全行削除


    m_list.DeleteAllItems();

全列削除

 DeleteColumnの引数はインデックスなので、ゼロを列数分だけ削除すると全列削除になる。 (削除した列より後ろのインデックスは1個前にずれるため)


    // 列数の取得
    int nColumnCount = m_list.GetHeaderCtrl()->GetItemCount();

    // 削除
    for (int i = 0; i < nColumnCount; i++)
    {
        m_list.DeleteColumn(0);
    }

リファレンス

CListCtrl Class
https://learn.microsoft.com/en-us/cpp/mfc/reference/clistctrl-class

(おまけ)サンプルデータ


    const std::vector<std::vector<std::wstring>> data
    {
        {L"茨城県", L"水戸市", L"笠原町"},
        {L"栃木県", L"宇都宮市", L"塙田"},
        {L"群馬県", L"前橋市", L"大手町"},
        {L"埼玉県", L"さいたま市", L"浦和区"},
        {L"千葉県", L"千葉市", L"中央区"},
        {L"東京都", L"新宿区", L"西新宿"},
        {L"神奈川県", L"横浜市", L"中区"}
    };