Natural Intelligence による記事要点まとめ
・横スクロールバーを有効にするため、まずプロパティを設定する。
・横スクロールバーを表示するため、設定を行う。
・適切なサイズでスクロールさせるため、内容のサイズを計算する。
Visual C++ でダイアログを作成し、リストボックスを設置しても、プロパティ設定だけでは
SetHorizontalExtent 関数 で横幅を指定することで横スクロールバーを有効にすることはできますが、 設定された文字列の長さに合わせて変更されることはなく、 設定する値によってはスクロールが不足してテキストの末尾まで見えなかったり、 あるいはスクロールが多すぎて右側に広い空白ができてしまったりします。
ここではこの問題を解決する方法について検討しています。
リストボックスの文字サイズやフォントを変える
Visual C++ では、リストボックスの文字サイズをプロパティで変更することはできません。
ここでは自由な文字サイズに変更したり、別のフォントにしたりしています。
コードは Windows 10、Visual Studio 2022 で作成、確認したものです。
なお、本サイトのご利用に際しては、必ずプライバシーポリシー(免責事項等)をご参照ください。
投稿 December 17, 2022
とりあえず横スクロールバーを表示できるまで進めてから検討に入ります。
テストのために、ダイアログベースの MFC アプリを作成しました。 プロジェクト名は LBTextExtent としています。 以降、この文字列は関数名等ではなくプロジェクト名ですので、ご注意ください。
作成したダイアログに、リストボックスを配置します。
横スクロールバーを有効にするには、リストボックスのプロパティ 「水平スクロール」を True に設定します。
このあとリストボックスにデータを追加するため、エディットコントロール IDC_TEXT も追加しておきました。
実行するにあたり、毎回たくさんデータを追加するのは面倒ですから、 ダイアログの初期化関数 OnInitDialog() で、適当に初期データを設定しておきます。 OnInitDialog 関数は、ダイアログクラス内に、 BOOL CLBTextExtentDlg::OnInitDialog() のように実装されています。
// リストボックスに初期データを入れておきます。 CListBox* pList = (CListBox*)GetDlgItem(IDC_LIST); pList->ResetContent(); pList->AddString(_T("アリゾナ・カージナルス")); pList->AddString(_T("シアトル・シーホークス")); pList->AddString(_T("サンフランシスコ・フォーティーナイナーズ")); pList->AddString(_T("ロサンゼルス(セントルイス)・ラムズ")); pList->AddString(_T("ラスベガス・レイダース")); pList->AddString(_T("ニューイングランド・ペイトリオッツ")); SetDlgItemText(IDC_TEXT, _T("ロサンゼルス(サンディエゴ)・チャージャース"));
実行すると、このようになりました。
収まりきらないテキストがありますが、横スクロールバーは表示されていません。 「水平スクロール」のプロパティをセットしているのに、です。 ちなみにはみでているのは上から 2 つ目の「サンフランシスコ・フォーティーナイナーズ」です。
リストボックスのプロパティで「スクロールなしの無効化」を True にする必要がありました。
大変わかりにくい日本語訳になっていますから、だったら英語のままで良かったんですけど。
縦スクロールバーも同時に表示されてしまいましたが、横スクロールバーは使えません。
投稿 December 17, 2022
横スクロールバーを有効にするために、 ダイアログの初期化 OnInitDialog() で、リストボックスの横方向のピクセル数を指定します。
一連の pList-AddString 呼び出し の次に、 SetHorizontalExtent 関数 呼び出しを追加します。 なお、ここで与えている値 500 は適当です。 ただじゅうぶんに大きな値を指定したというだけで、何か意味がある数字ではありません。
pList->SetHorizontalExtent(500);
無事に横スクロールできましたが、とてもたくさんスクロールして無駄な感じです。
上のイメージの横スクロールバーにある
500 より小さい値を指定すればだいたい合わせることはできますが、実行環境によって最適な値が異なる可能性も高いです。 しかし、水平スクロールバーのサイズは自動計算されません。
どうにかならないものでしょうか?
投稿 December 17, 2022
「リストボックスの文字サイズやフォントを変える」で、 リストボックスに設定されているフォントを取得できていますので、設定されているテキストのサイズを知ることができそうです。
つまり、リストにある項目を 1 つずつ調べて、一番長いテキストにあわせて SetHorizontalExtent 関数を呼び出します。
ダイアログのヘッダに、関数と変数を追加します。
private: /* リストボックスの横幅を計算して設定 */ bool AdjustListBoxWidth(void); protected: /* 現在のリストボックスの幅 */ int m_nLBWidth;
それぞれの意味は、上記コメントの通りです。
現在のリストボックスの幅 m_nLBWidth は初期状態 0 としたいので、 ダイアログの初期化関数 OnInitDialog() の最後で設定し、 そのあと AdjustListBoxWidth 関数を呼び出すようにします。
先ほど適当な値を設定していた pList->SetHorizontalExtent(500) は、混乱がないように削除、またはコメントアウトします。
// リストボックスの幅を決定します。
m_nLBWidth = 0;
AdjustListBoxWidth();
リストボックスの最適な横幅を設定する関数 AdjustListBoxWidth は、次のようにしました。
bool CLBTextExtentDlg::AdjustListBoxWidth(void) { CListBox* pList = (CListBox*)GetDlgItem(IDC_LIST); int n = pList->GetCount(); // リストボックスのデータ数
ダイアログに配置したリストボックスを取得し、リストボックスにあるデータの数を取得しています。
CFont* pFont = pList->GetFont(); CDC memDC; memDC.CreateCompatibleDC(NULL); CFont* pMemFont = memDC.SelectObject(pFont);
リストボックスに設定されているフォントを取得し、互換性のあるメモリ DC を作成して選択します。 「リストボックスの文字サイズやフォントを変える」で少し詳しく見ています。 なお、CreateCompatibleDC 関数に NULL を渡すと、システムで使用している DC と互換の DC ができる、ということです。
CString str; CSize sz; int nMax = 0; for (int i = 0; i < n; i++) { pList->GetText(i, str); sz = memDC.GetTextExtent(str); nMax = max(nMax, sz.cx); }
リストボックスに設定されているテキストを 1 つずつ取得し、 CDC クラスの GetTextExtent 関数 で、現在の設定で描画される場合の使用領域(サイズ)を計算してもらいます。
計算された横サイズと nMax を比較し、最大の横サイズを nMax に設定することになります。 max マクロは、指定した 2 つの引数のうち、大きいほうの値を返してくれるものです。
memDC.SelectObject(pMemFont);
//memDC.DeleteDC();
使い終わったメモリ DC をもとに戻します。
なお、使い終わったメモリ DC を DeleteDC 関数で削除したいところでしたが、 DeleteDC 関数 の説明に「デストラクタが削除するので通常は呼び出さないように」と書かれていますので、呼び出さないほうがいいようです。
// 今だと横幅が狭すぎる場合 // ※ アイテムを削除して小さくなる場合も考慮が必要です。 if (nMax > m_nLBWidth) { m_nLBWidth = nMax; pList->SetHorizontalExtent(m_nLBWidth); return true; } // 変える必要がありませんでした。 return false; }
現在の幅より広くなった場合のみ、SetHorizontalExtent 関数を呼び出しています。 初期状態で m_nLBWidth が 0 ですので、初回は必ず設定されることになります。
実行すると、右端ぴったりになりました。
ダイアログの OK ボタンで、 エディットコントロール IDC_TEXT に設定された文字列をリストボックスに追加することとし、 AdjustListBoxWidth 関数を呼び出して横幅を合わせてみます。
なお、AdjustListBoxWidth 関数のコメントに書いているように、nMax > m_nLBWidth のときのみ横幅指定をしていますから、 大きくなる方向にのみ対応しています。 リスト項目を削除して小さくなるような場合は、nMax != m_nLBWidth にするとよさそうです。
また、項目数が多くなると無駄が大きくなりますから、追加したり、削除した項目のみを調べて更新するほうが良さそうです。
void CLBTextExtentDlg::OnBnClickedOk()
{
//CDialogEx::OnOK();
CString str;
GetDlgItemText(IDC_TEXT, str);
if (!str.IsEmpty()) {
CListBox* pList = (CListBox*)GetDlgItem(IDC_LIST);
pList->SetCurSel(pList->AddString(str));
AdjustListBoxWidth();
}
}
上記コードを追加して実行し、OK ボタンをクリックすると、すぐ上のエディットコントロールにある文字列がリストに追加されます。 長い文字列を追加すればそれにあわせてスクロールサイズが変わります。
短い文字列の場合は、何も変わりません。
項目をたくさん追加すると、縦スクロールバーもアクティブになります。 こちらは特に設定しなくてもちゃんと動作します。
リストボックス スタイル「スクロールなしの無効化」プロパティは LBS_DISABLENOSCROLL のようです。 この説明には「このスタイルが設定されていないと、項目が少なくてスクロールする必要がない場合、スクロール バーは表示されません。」と書かれています。
正しい横幅を指定している今、これを False にしても問題ないのではないでしょうか?
初期状態で縦スクロールバーは非表示になり、横スクロールバーはちゃんと表示されました。
項目を追加していくと、必要になった時点ではじめて、縦スクロールバーが表示されました。
これで便利に使えそうです。
Visual C++ では、リストボックスの文字サイズをプロパティで変更することはできません。 ここでは自由な文字サイズに変更したり、別のフォントにしたりしています。
Windows PC で 2 台のモニタを接続している場合、2 台接続を検出し、座標を特定するためのコードについて書いています。