毎度おせわになります。
前回、リストコントロールのセル毎の値を左クリックで取得することができました。今度はリストコントロールのサブアイテムにアイコンを表示させたいのです。
環境は、visual studio2005 VC++(MFC) vistaです。
カスタムドローを使うというところまではたどり着いたのですが、どういう風に処理を書けばいいのか途方に暮れている状態です。
皆様の力をお貸しいただければありがたいです。宜しくお願い致します。
カスタムドローの必要がありますかね?
LVS_EX_SUBITEMIMAGES 拡張スタイルをセットして、後は普通に SetItem するときにイメージのインデックスを指定してやればいいんじゃないでしょうか?
aetosさん、さっそくの返信ありがとうございます。
LVS_EX_SUBITEMIMAGES拡張スタイルを利用するというのを初めて知りました。
カスタムドローは自分にとっては敷居が高い気がしてたんですが、この拡張スタイルを使うと比較的簡単に実現できるという事でしょうか?
そこらへんも含めて使い方を調べてやってみようと思いますが、参考になるサイトをご存知でしたら教えてもらえると助かります。
とにかく、貴重な意見ありがとうございます。
aetosさんのおかげで、サブアイテムにアイコンを表示することが出来ました。ですが今度は別の問題が出てきました。
アイコンを表示する前はテキストを入れていたんですが、今の状態で
| ① | ② | ③ |
| [アイコン] あ | [アイコン] い | [アイコン] う |
| [アイコン] え | [アイコン] お | [アイコン] か |
| [アイコン] き | [アイコン] く | [アイコン] け |
こんな感じで表示されているのを、テキストを消してアイコンだけ表示しなければならなくなりました。
ソースを張ります。
// リストコントロール初期化
void CNoticeDlg::ListInit()
{
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
// サブアイテムのイメージリスト設定
m_lcIconList.SetExtendedStyle(
m_lcIconList.GetExtendedStyle() | LVS_EX_HEADERDRAGDROP | LVS_EX_SUBITEMIMAGES
);
//イメージリストを初期化。
m_imageList.Create(
16, //イメージの幅
16, //高さ
ILC_COLOR, //色数
3, //初期イメージ数
1 //拡張単位
);
//####イメージリストにアイコンを追加###########################
//CWinApp::LoadIcon()関数でアイコンリソースを読み込み
if((hIcon = AfxGetApp()->LoadIcon(IDI_ICON1)) != NULL) {
//CImageList::Add()関数で読み込んだアイコンを追加
m_imageList.Add(hIcon);
}
if((hIcon = AfxGetApp()->LoadIcon(IDI_ICON2)) != NULL) {
m_imageList.Add(hIcon);
}
if((hIcon = AfxGetApp()->LoadIcon(IDI_ICON3)) != NULL) {
m_imageList.Add(hIcon);
}
//#############################################################
//CListCtrl::SetImageList()でイメージリストをリストコントロールにセット
m_lcIconList.SetImageList(&m_imageList, LVSIL_SMALL);
//int imageCount = m_imageList.GetImageCount();
lvc.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; // 有効フラグ
for (int i = 0; i < clmNum; i++)
{
lvc.iSubItem = i; // サブアイテム番号
lvc.pszText = caption[i]; // 見出しテキスト
lvc.cx = 100; // 横幅
m_lcIconList.InsertColumn(i, &lvc);
}
}
-------------------------------------------------------
void CNoticeDlg::ListAddItem()
{
//構造体
struct{
TCHAR name[32];
int price;
int num;
}
item [] = {
{_T("鉛筆"), 50, 3},
{_T("消しゴム"), 100, 7},
{_T("ボールペン"), 120, 10},
{_T("万年筆"), 1000, 5},
{_T("定規"), 150, 9},
};
//変数で確保されているメモリサイズを調べる
const int itemNum = sizeof item /sizeof item[0];
//LVITEMはアイテム情報が入った構造体
LVITEM lvi;
CString str;
int i=0;
int index = 0;
int err = 0;
//itemNumのサイズ分まわす
for(i = 0; i < itemNum; i++)
{
// 商品名
if(!err)
{
//lvi.mask = LVIF_TEXT | LVIF_IMAGE; //lvi.maskには構造体のどのメンバが有効なのかを指定
lvi.mask = LVIF_TEXT | LVIF_IMAGE;
lvi.iItem = i; //アイテム番号(行のインデックス)
lvi.iSubItem = 0; //サブアイテム番号(列のインデックス)を指定
//lvi.pszText = item[i].name;
lvi.pszText = _T("");
//lvi.iImage = i %3;
lvi.iImage = 0;
if( (index = m_lcIconList.InsertItem(&lvi)) == -1 )
{
err = 1;
}
}
// 単価
if(!err)
{
// m_imageList.DrawIndirect();
str.Format(_T("%d"), item[i].price);
lvi.iItem = index;
lvi.iSubItem = 1;
//lvi.pszText = const_cast<LPTSTR>(static_cast<LPCTSTR>(str));
//lvi.iImage = i %3;
lvi.iImage = 1;
//SetItem()でリストビューコントロールにアイテムを挿入
if(!m_lcIconList.SetItem(&lvi))
{
err = 1;
}
}
// 個数
if(!err)
{
str.Format(_T("%d"), item[i].num);
lvi.iItem = index;
lvi.iSubItem = 2;
//lvi.pszText = const_cast<LPTSTR>(static_cast<LPCTSTR>(str));
//lvi.iImage = i %3;
lvi.iImage = 2;
if(!m_lcIconList.SetItem(&lvi))
{
err = 1;
}
}
if(err)
{
break;
}
}
}
ここでlvi.pszText = 〜〜としているところをlvi.pszText = _T("");として空文字を入れた場合、当然ですがテキストが表示されていた部分が空白で表示されてしまします。
これをテキスト部分は空白も出さないようにして完全に消して、アイコンだけの表示にしたいのです。
どうか力をお貸しください。宜しくお願い致します。
> ここでlvi.pszText = 〜〜としているところを
とは、提示のコード中のどこで?
> テキスト部分は空白も出さないようにして完全に消して、アイコンだけの表示にしたいのです。
というのをもう少し詳しく。
カラム幅をギリギリにすればよいとか、そういう問題ではなく?
aetosさん、いつも御世話になっております。
> ここでlvi.pszText = 〜〜としているところを
コードをきちんと掲示していなかったようです。
商品名の部分:lvi.pszText = item[i].name;
単価と個数の部分:lvi.pszText = const_cast<LPTSTR>(static_cast<LPCTSTR>(str));
と書いてあったのを、lvi.pszText = _T("")として見たのですが、テキストが表示されていた部分が空白として表示されるのです。
> テキスト部分は空白も出さないようにして完全に消して、アイコンだけの表示にしたいのです。
僕のイメージとしては、そのセル内にはアイコンだけを表示するようにしたいのです。ゆくゆくはそのアイコンをクリックしてそれぞれの処理を行えるようにしたいのですが・・・・・
僕の伝えたいことがうまく伝わるといいのですが・・・駄文ですみません。
宜しくお願い致します。
すみません、もう一度、元のソースを張り直します。
// リストコントロール初期化
void CNoticeDlg::ListInit()
{
LVCOLUMN lvc; //LVCOLUMNはカラム情報が入った構造体
TCHAR caption[][32] = {_T("1"), _T("2"), _T("3")};
const int clmNum = sizeof caption /sizeof caption[0];
HICON hIcon = NULL;
// サブアイテムのイメージリスト設定
m_lcIconList.SetExtendedStyle(
m_lcIconList.GetExtendedStyle() | LVS_EX_HEADERDRAGDROP | LVS_EX_SUBITEMIMAGES
);
//イメージリストを初期化。
m_imageList.Create(
16, //イメージの幅
16, //高さ
ILC_COLOR, //色数
3, //初期イメージ数
1 //拡張単位
);
//####イメージリストにアイコンを追加###########################
//CWinApp::LoadIcon()関数でアイコンリソースを読み込み
if((hIcon = AfxGetApp()->LoadIcon(IDI_ICON1)) != NULL) {
//CImageList::Add()関数で読み込んだアイコンを追加
m_imageList.Add(hIcon);
}
if((hIcon = AfxGetApp()->LoadIcon(IDI_ICON2)) != NULL) {
m_imageList.Add(hIcon);
}
if((hIcon = AfxGetApp()->LoadIcon(IDI_ICON3)) != NULL) {
m_imageList.Add(hIcon);
}
//#############################################################
//CListCtrl::SetImageList()でイメージリストをリストコントロールにセット
m_lcIconList.SetImageList(&m_imageList, LVSIL_SMALL);
//int imageCount = m_imageList.GetImageCount();
lvc.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; // 有効フラグ
for (int i = 0; i < clmNum; i++)
{
lvc.iSubItem = i; // サブアイテム番号
lvc.pszText = caption[i]; // 見出しテキスト
lvc.cx = 100; // 横幅
m_lcIconList.InsertColumn(i, &lvc);
}
}
----------------------------------------------------------------
void CNoticeDlg::ListAddItem()
{
//構造体
struct{
TCHAR name[32];
int price;
int num;
}
item [] = {
{_T("鉛筆"), 50, 3},
{_T("消しゴム"), 100, 7},
{_T("ボールペン"), 120, 10},
{_T("万年筆"), 1000, 5},
{_T("定規"), 150, 9},
};
//変数で確保されているメモリサイズを調べる
const int itemNum = sizeof item /sizeof item[0];
//LVITEMはアイテム情報が入った構造体
LVITEM lvi;
CString str;
int i=0;
int index = 0;
int err = 0;
//itemNumのサイズ分まわす
for(i = 0; i < itemNum; i++)
{
// 商品名
if(!err)
{
//lvi.mask = LVIF_TEXT | LVIF_IMAGE; //lvi.maskには構造体のどのメンバが有効なのかを指定
lvi.mask = LVIF_TEXT | LVIF_IMAGE;
lvi.iItem = i; //アイテム番号(行のインデックス)
lvi.iSubItem = 0; //サブアイテム番号(列のインデックス)を指定
lvi.pszText = item[i].name;
lvi.iImage = 0;
if( (index = m_lcIconList.InsertItem(&lvi)) == -1 )
{
err = 1;
}
}
// 単価
if(!err)
{
str.Format(_T("%d"), item[i].price);
lvi.iItem = index;
lvi.iSubItem = 1;
lvi.pszText = const_cast<LPTSTR>(static_cast<LPCTSTR>(str));
lvi.iImage = 1;
//SetItem()でリストビューコントロールにアイテムを挿入
if(!m_lcIconList.SetItem(&lvi))
{
err = 1;
}
}
// 個数
if(!err)
{
str.Format(_T("%d"), item[i].num);
lvi.iItem = index;
lvi.iSubItem = 2;
lvi.pszText = const_cast<LPTSTR>(static_cast<LPCTSTR>(str));
lvi.iImage = 2;
if(!m_lcIconList.SetItem(&lvi))
{
err = 1;
}
}
if(err)
{
break;
}
}
}
ListAddItem()の中のlvi.pszTextを、それぞれ_T("")にした場合にと言うことです。
もしかして、根本的な事を質問しているのかもしれません(汗)
宜しくお願い致します。
> ゆくゆくはそのアイコンをクリックしてそれぞれの処理を行えるようにしたいのですが
というのは、一番左の列だけでなく、サブアイテムのクリックも考えていますか?
だとすると、なんだか、リストコントロールで実現するのが適当ではない気がしてきました。
(VC++/MFC には標準では用意されていませんが)グリッドコントロールとかで実現した方がいいような気がしています。
aetosさん、お世話になります。
>というのは、一番左の列だけでなく、サブアイテムのクリックも考えていますか
そうですね。サブアイテムのアイコンをクリックして、それぞれのアイコンに対応した処理をする風にしたいです。
ちなみに、指定セルのデータを左クリックで取得することは出来ています。
サブアイテムにアイコンだけが表示されるようにするというのは、リストコントロールでは実現出来ないのでしょうか?見た目だけでもそうみえているようになればいいかなと考えているのですが・・・・・
lvc.cx = 100; // 横幅
この数字を減らすのは?
カラムの幅をアイコンしか見えないように調節すればよいのではないかと思いますが、カラム幅=アイコンの幅としてしまうと、余白が入るので、アイコンがすべて表示しきれません。
では余白をどれだけ設ければいいのかと言うと…わかりません。
何度か試行錯誤して、ちょうどいい値を見つけ出すしかないんじゃないかと思いますが、そうやって見つけた値は、OS が変わったりすると通用しないかもしれません。
rinさん、書き込みありがとうございます。
lvc.cxの値をアイコンのサイズに合わせてみましたが、アイコンが正しく表示されないですね・・・。
何度か試行錯誤してみているのですが、うまくいかないです。
もしかして、カスタムドローとかオーナードローとかのお話になるのでしょうか?
aetosさん、毎度お世話になります。
lvc.cxの幅は何度か調整してみているのですが、アイコンのサイズにしてみても重なって表示されないです。正確に言うと、先頭のカラムのアイコンの左側に少しだけ余白があり、アイコンサイズジャストで表示してみると、先頭カラムのアイコンだけがその余白の分だけ重なってうまく表示されない、といった感じです。
先頭カラムにアイコンを表示した時は、左側に必ず余白が出来てしまうものなのでしょうか?これが仕様でしたら、お手上げです・・・・・・。
後、お聞きしたいのが、アイコンをクリックしてそれぞれの処理をさせたいときに、そのアイコンに何かしらの情報を持たせることが必要になってくると思うのですが、アイコンに情報を持たせることはできるのでしょか?
もしくは、アイコン表示しているセルをクリックしたときに、何のアイコンが表示されているか(アイコンのIDとか)を取得することは出来るのでしょうか?
的外れな質問でしたらすみません。
宜しくお願い致します。
> 左側に必ず余白が出来てしまうものなのでしょうか?
おそらくはそうでしょう。
オーナードローを使えば、左の余白をゼロにできるかもしれません。
> アイコン表示しているセルをクリックしたときに、何のアイコンが表示されているか(アイコンのIDとか)を取得することは出来るのでしょうか?
どのセルがクリックされたかはわかりますね。
http://madia.world.coocan.jp/cgi-bin/Vcbbs/wwwlng.cgi?print+200906/09060022.txt
あとは、このセルに対して CListCtrl::GetItem を呼んでやれば、SetItem 時に指定したイメージインデックスを得ることができます。
aetosさん、毎度お世話になります。
>おそらくはそうでしょう。オーナードローを使えば、左の余白をゼロにできるかもしれません。
やはり、カスタムドローを行わないと実現できないっぽいですね・・・。
>このセルに対して CListCtrl::GetItem を呼んでやれば、SetItem 時に指定したイメージインデックスを得ることができます
GetItemの使い方がいまいちわからないです><
LVHITTESTINFO lvHitTestInfo;
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
LVITEM item;
CString str;
ZeroMemory( &item , sizeof(LVITEM) );
item.mask = LVIF_TEXT | LVIF_IMAGE;
item.pszText =str.GetBuffer( 256 );
item.iItem = lvHitTestInfo.iItem;
item.iSubItem = lvHitTestInfo.iSubItem;
item.cchTextMax = 256;
m_lcIconList.GetItem( &item );
str.ReleaseBuffer();
int iconIndex = item.iImage;
とやってみましたが、取得できずでした。根本的に間違ってるのでしょうか?
何度もすみません。
上記の方法で取得できていました。お手数おかけしました。これで、どこの何のアイコンが押されたかがわかるようになりました。
aetosさん、rinさん誠にありがとうございました。今度ともよろしくお願い致します。
> カスタムドローを行わないと実現できないっぽいですね・・・。
オーナードローとカスタムドローは違います。
たぶんカスタムドローでは実現できません。
カスタムドローは、色やフォントを指定することができますが、描画自体はリストビューが行います。
カスタムドローでは余白は変更できません。
オーナードローは、あらゆる描画をプログラマが行います。
aetosさん、毎度おせわになります。
そうだったんですね。両方大差ないものだと間違った認識をしておりました。
御指摘ありがとうございます。
表示についてなんですが、以前aetosさんやrinさんががおっしゃった通りカラムの幅を調整して、それっぽく見せようかと思っています。先頭カラムにアイコンを表示しなければいいお話ですし。
もし、先頭カラムにアイコンを表示しなければならなくなった時は、余白をなくすためにオーナードローですべての描画を行わなければならないという事ですね・・・・。
勉強になりました。
本当にありがとうございました。今後も宜しくお願い致します。
ツイート | ![]() |