DrawItemでBitmap表示

[戻る]
Nezu 2008/04/15(火) 17:44:49
Nezuです。
お世話になってます。

DrawItemを使って、CListCtrlのサブアイテムにBitmapを表示させたいのですが
情報が少なく、とっかかりが掴めません。

サンプル等の情報を教えていただけたら幸いです。
あつかましいですが、よろしくお願いします。

環境:Vista MFC(ダイアログ) VS2005

Nezu 2008/04/15(火) 19:24:51
すいません、質問を変えます。

現在、CListCtrlの拡張クラスにDrawItemが用意されています。
しかし、必要に応じて(ボタン押下時等)にDrawItemを呼び出すにはどうしたら
よいのでしょう?

PATIO 2008/04/16(水) 12:02:37
CListCtrlの拡張クラスと言っているのは、派生クラスのこと?
派生クラスにしただけでは駄目でオーナードロウにして
特定のメッセージの内部処理できるようにメッセージマップに
追加したりが必要だったと思いますよ。

DrawItemは、アイテムの描画必要なときにその都度呼ばれる物なので
基本的に呼び出すと言う感覚は無いと思います。
アイテムの追加削除とか状態変化が起これば、再描画の必要が
発生するはずなのでそのときは勝手に呼ばれるはず。
どうしても能動的に呼び出したいならCListCtrl::Ivalidateを呼べば、
再描画が走ると思いますけれど。

ちなみにですが、C++言語に関する知識は大丈夫ですか?
あと、Windowsのメッセージ周りの動作に関しては大丈夫ですか?

Nezu 2008/04/16(水) 12:46:17
PATIOさん、いつもお世話になってます。

>CListCtrlの拡張クラスと言っているのは、派生クラスのこと?
そうです、派生クラスです。

DrawItemのコードはすでに書いてあります。
(InsertItem(), SetItemText()を呼び出せばDrawItemで描画されます。)
文字の描画が行えていることは確認済みです。

ただ、リストのサブアイテムに描画する際に、Dlgクラス
でなにをしたらよいのかが分からない状態です。
SetItemを呼び出しても、DrawItemが呼び出されません。
DrawItemを呼び出して、LPDRAWITEMSTRUCTに情報を渡すにはどうしたらよいのでしょう
か?


>ちなみにですが、C++言語に関する知識は大丈夫ですか?
微妙ですかね...
おかしなことを言ってなければよいのですが。

PATIO 2008/04/16(水) 14:03:28
私自身はCListCtrlのオーナードロウはやった事が無いので
何とも言いかねるんですが、現状何処までできているんでしょう?

描画のコードはアイテムの文字列を表示するところだけでしょうか?
画面上は何処まで表示されているんでしょう?
サブアイテムの文字列も描画で来ていますか?

ソースレベルでデバッグが出来る環境があるわけですし、
デバッガーを使ってオーナードロウの時の動きを追いかけて
見ることをお勧めします。
この辺の動きがきちんと理解できていない状態で
オーナードロウのコードを記述するのはちょっと無理があると
思います。

あと、C++言語の勉強とVC++でMFCをつかったプログラミングの勉強を
いっぺんにしようとするのは無謀なので止めた方がいいです。
せめてC++言語の文法に関しては一通りの知識は必要です。
MFCを利用してWindowsアプリのプログラミングをやる為には
C++言語に関しては人に聞かなくてもプログラムが出来る、
もしくは自分で調べて解決できるくらいの知識は必要になります。

Nezu 2008/04/16(水) 15:42:48
PATIOさん、ご忠告ありがとうございます。
おっしゃる通り、C++の勉強からし直したほうがよいですね。

ただ、この問題に関しては、クリアさせてください。

CRect rcIcon;
GetItemRect(nItem, rcIcon, LVIR_ICON);

CImageList* pImageList = m_GetImageList(LVSIL_SMALL);
if (pImageList)
{
   //UINT nOvlImageMask=lvi.state & LVIS_OVERLAYMASK;
   if (rcItem.left<rcItem.right-1)
   {
    ImageList_DrawEx(pImageList->m_hImageList, lvi.iImage,
            pDC->m_hDC,rcIcon.left,rcIcon.top, 16, 16,
                m_clrBkgnd, clrImage, uiFlags | 
                                                           nOvlImageMask);
    }
}

DrawItemの画像表示部分は上記のようになっています。

BitBlt()等で描画させた方がよいのでしょうか?
よろしくお願いします。

Nezu 2008/04/16(水) 15:52:47
間違えました。
>CImageList* pImageList = m_GetImageList(LVSIL_SMALL);
CImageList* pImageList = GetImageList(LVSIL_SMALL);

PATIO 2008/04/16(水) 17:27:02
このソースからではいまひとつ解りません。
ImageList_DrawExに引き渡している一つ一つの引数の内容が
大丈夫なのかなぁとかImageList_DrawExの返却値のチェックが無いなぁ
くらいなら思いつきますけれど。

一つだけ私が体験したCListCtrl絡みの話をば。
VS2005からデフォルトの文字コードがUnicodeになりました。
プロジェクトの作成時にSJISを使うように設定も出来ますが、
これをやるとビジュアルスタイルで表示できなくなります。
この対策に追加のマニフェストファイルを設定して使うライブラリを
ビジュアルスタイルに対応した物に変更するという方法があるんですが、
この方法を使うとListCtrl上のアイコンが選択状態になると見えなくなる
という事が有りました。結論は、マニフェストファイルで指定した
ライブラリは元々Unicodeでの使用を前提としたものである為、
誤動作しているのではないかと言う話でした。
直接関係無いとは思いますが、私がハマッタ所なので
紹介しておきます。

結局、SJISでビジュアルスタイルへの対応は見送りました。

Nezu 2008/04/16(水) 18:03:25
>一つだけ私が体験したCListCtrl絡みの話をば。
現在のプロジェクトの設定は「マルチバイト文字セット」
なんですが、最初から表示されていないので、関係ないですよね...
ありがとうございます。今後のためになる話でした。


以下、DrawItem内の引数設定コードです。

CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
CRect rcItem(lpDrawItemStruct->rcItem);
UINT uiFlags = ILD_TRANSPARENT;
CImageList* pImageList;
int nItem = lpDrawItemStruct->itemID;
BOOL bFocus = (GetFocus() == this);
COLORREF clrTextSave, clrBkSave;
COLORREF clrImage = m_clrBkgnd;
static _TCHAR szBuff[MAX_PATH];
LPCTSTR pszText;

// get item data

LV_ITEM lvi;
lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
lvi.iItem = nItem;
lvi.iSubItem = 0;
lvi.pszText = szBuff;
lvi.cchTextMax = sizeof(szBuff);
lvi.stateMask = 0xFFFF;        // get all state flags
GetItem(&lvi);

BOOL bSelected = (bFocus || (GetStyle() & LVS_SHOWSELALWAYS)) && lvi.state &  
                               LVIS_SELECTED;
bSelected = bSelected || (lvi.state & LVIS_DROPHILITED);

申し訳ありませんが、なにか気づく点があれば、ご教授願います。

Nezu 2008/04/17(木) 15:32:51
画像を実際に描画させているのは
ListCtrl::SetITem() なんですが、そのパラメータでサブアイテムのインデックスを
渡しているのに
DrawItem内のLVITEMの設定でlvi.iSubItem = 0; としている箇所が
気になります。

しかし、そこを変更すると、文字描画に影響がでてしまいます。
2008/04/16(水) 15:42:48 で掲載したコードに問題があるのかな...

PATIO 2008/04/17(木) 17:48:18
気になっているんですが、そもそもDrawItemの呼び出しは
どの単位で来ていますか?
アイテム単位で来ているのであれば、サブアイテムがいくつあるかを
把握した上でサブアイテムの描画も全てそのタイミングでやる必要があると
思います。逆にサブアイテム単位でDrawItemが呼び出されているなら
その時にどのサブアイテムを対象に呼び出したのかを確認する術が必要です。
LPDRAWITEMSTRUCTの中を見る限りではアイテム毎に呼び出しているように
見えますから、それならば一階の呼び出しでそのアイテムに関連する
サブアイテムを全て描画してしまう必要があると思います。

今のコードはアイテムの部分を描画するコードしか無い様に見えます。
あと、イメージを描画する位置にもらったRECTの値を使っていますが、
イメージを描画する時点でその値は間違いなく自分が想定している値に
なっているでしょうか?
この辺はデバッガで実際に動かして確認してみてください。
ソースを見て分からない場合はデバッガでステップごとに追いかけて
確認した方が直接的なのでわかりやすいです。
慣れてくれば、ソースだけを見て間違いに気がつくようになると思いますけれど。

Nezu 2008/04/19(土) 10:46:50
[[解決]]
PATIOさん、何度も貴重なアドバイスありがとうございます。

なんとか表示することができました。
おっしゃる通り、DrawItemはアイテム毎に呼ばれていました。

主な原因としては、一列目のカラム表示の際に
InsertItem(0, "", -1);
としていたために、LV_ITEMのiImageに常に-1が入ってきたために
表示ができなかったようです。
サブアイテム表示に関してはGetSubItemRect()を使いました。

ただ、一列目のカラム表示を
InsertItem(0, "");
とした所、LV_ITEMのiImageには常に0が入ってくるようになりました。
これでは、任意のサブアイテムに任意の画像を表示することできません。

DlgクラスにCImageListに格納したイメージインデックスを返す関数を追加して
ImageList_Draw()の引数に渡していますが、これでは再描画の際に前のインデックスの
画像に変更されてしまいます。

カラムの追加をInsertItem()以外でやるしかないのでしょうか?
LV_ITEMのiImageを可変にする方法があるのでしょうか?
何度もすみませんが、よろしくお願いします。

古いスレッドには追加発言できません。