興味をおぼえましたので、調べてみました。
そもそも何故表示が崩れるのか?→最初から崩れないようにする方法はありますか?
初回の描画で、2つ目のアイテムから高さを変えられてしまっているようです。それ以後はその高さが適用されるので、初回の描画が崩れているように見えます。
表示が崩れるのが仕方ない場合,上記よりももっと(何らかの意味で)楽な対処方法はありませんか?
若干面倒ですが、CCheckBox::AddString
の実行後に高さを明示的にセットすることで対処できました。以下、ご説明します。
どうもCCheckListBox
の初回の描画で、2行目(Item[1])以降の項目の高さが低くセットされてしまっているようです。その為、2回目以降の描画ではその低い高さのまま、全項目(リストボックスのスタイルはLBS_OWNERDRAWFIXED
なので)に適用されてしまっている様子です。
確認の為、CCheckListBox
を派生させたCCheckListBox2
クラスを定義し、CCheckListBox::DrawItem
が呼び出されるようにしてみました。
C++
1// CCheckListBox2.h
2#pragma once
3#include <afxwin.h>
4class CCheckListBox2 :
5 public CCheckListBox
6{
7public:
8 CCheckListBox2();
9 virtual ~CCheckListBox2();
10 virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
11 virtual void MeasureItem(LPMEASUREITEMSTRUCT ps);
12};
13
14// CCheckListBox2.cpp
15#include "stdafx.h"
16#include "CCheckListBox2.h"
17
18CCheckListBox2::CCheckListBox2()
19{
20}
21
22CCheckListBox2::~CCheckListBox2()
23{
24}
25
26static int n = 0;
27void CCheckListBox2::DrawItem(LPDRAWITEMSTRUCT ps)
28{
29 int h = ps->rcItem.bottom - ps->rcItem.top;
30 TRACE(_T("DrawItem item:[%d], h=%d\n"), ps->itemID, h);
31 CCheckListBox::DrawItem(ps);
32}
33
34// LBS_OWNERDRAWFIXEDだから、呼ばれない
35void CCheckListBox2::MeasureItem(LPMEASUREITEMSTRUCT ps)
36{
37 TRACE(_T("Measuretem item:[%d], h=%d\n"), ps->itemID, ps->itemHeight);
38 CCheckListBox::MeasureItem(ps);
39}
上記CCheckListBox2
をメンバー変数m_CLB
として使うよう、差し替えます。
C++
1 //CCheckListBox m_CLB;
2 CCheckListBox2 m_CLB;
これで試すと、仮想関数CCheckListBox2::DrawItem
の呼び出し(WM_DRAWITEM
のメッセージハンドラのはず)で、描画しようとしている項目の高さが分かります。
# デバッグ端末のTRACE出力
# 初回のDraw - item[0]だけ高さ18で、Item[1]以降は14と、小さくなっている。
cchecklistbox2.cpp(18) : atlTraceGeneral - DrawItem item:[0], h=18
cchecklistbox2.cpp(18) : atlTraceGeneral - DrawItem item:[1], h=14
cchecklistbox2.cpp(18) : atlTraceGeneral - DrawItem item:[2], h=14
cchecklistbox2.cpp(18) : atlTraceGeneral - DrawItem item:[3], h=14
cchecklistbox2.cpp(18) : atlTraceGeneral - DrawItem item:[4], h=14
# 2回目以降のDraw
# 高さ14のまま
cchecklistbox2.cpp(18) : atlTraceGeneral - DrawItem item:[0], h=14
cchecklistbox2.cpp(18) : atlTraceGeneral - DrawItem item:[1], h=14
cchecklistbox2.cpp(18) : atlTraceGeneral - DrawItem item:[2], h=14
cchecklistbox2.cpp(18) : atlTraceGeneral - DrawItem item:[3], h=14
cchecklistbox2.cpp(18) : atlTraceGeneral - DrawItem item:[4], h=14
これで分かるように、初回のDrawItem
呼び出しでItem[1]のときに高さが変わっています。あくまで私の頼りない推測ですが、CCheckListBox
の実装でチェックボックスの存在などを加味した高さの計算とセットがなされてしまうようです。
>>>>追記 2019/09/28 01:51
尚、コード例は示しませんが、OnInitDialog
内でのAddString
実行直後にCDC::GetTextExtent
で高さを確認すると全ての項目で高さ18であったものが、初回の描画、Item[1]以降で14になってしまったことから「高さを変えられている」と判断しました。
<<<<追記ここまで
そこで、対処としてSubclassDlgItem
~AddString
の実行後にあえて明示的にCListBox::SetItemHeight
でItem[0]の高さを指定してみました。
リストボックスのスタイルはLBS_OWNERDRAWFIXED
なので、ひとつセットすれば他の項目も同じになるだろう、との意図です。
一応、これで最初の描画から固定の高さになることは確認できました。
C++
1 // ...OnInitDialogの実装
2
3 // TODO: 初期化をここに追加します。
4 m_CLB.SubclassDlgItem(IDC_LIST1, this);
5 m_CLB.AddString(_T("Ragamuffin"));
6 m_CLB.AddString(_T("Australian Mist"));
7 m_CLB.AddString(_T("Maine Coon"));
8 m_CLB.AddString(_T("Japanese Bobtail"));
9 m_CLB.AddString(_T("Sphynx"));
10
11 // デバッグ用スイッチ
12 bool sw1 = true;
13 if (sw1) {
14 /*
15 https://docs.microsoft.com/ja-jp/cpp/mfc/reference/clistbox-class?view=vs-2019
16 を参考に、項目にセットした文字列の高さを基に、明示的にセットする。
17 */
18
19 CString str;
20 CSize sz;
21 CDC *pDC = m_CLB.GetDC();
22
23 // LBS_OWNERDRAWFIXED だから、ひとつセットすれば充分(?)
24 m_CLB.GetText(0, str);
25 sz = pDC->GetTextExtent(str);
26 TRACE(_T("i=%d, sz=%d\n"), 0, sz);
27 m_CLB.SetItemHeight(0, sz.cy);
28 m_CLB.ReleaseDC(pDC);
29 }
30
31 return TRUE; // フォーカスをコントロールに設定した場合を除き、TRUE を返します。
32
MFC内部のソースまで追っていませんので、推測の域を出ない意見でしたが、「一応、対処できた」ということでご参考まで。
なお、Windows 7(x64)上で、Visual Studio 2015、2017でのビルドとも同じ結果でした。