2012年11月20日火曜日

[C#] ListView のカラム部分に画像表示

更新履歴:
2012/11/29:宣言部分で DLL の宣言が抜けていたので追記。

[共通] 当 Blog の投稿に関する基本事項について
→ http://fuunyan.blogspot.jp/2012/09/blog.html


タイトル通り ListView のカラム部分に画像を表示する方法です。
ただしカラム文字列の左に表示するだけなら SmallImageList にImageList を登録して Columns.ImageIndex を設定するだけで表示できるので、取り立てて記事にするほどでもありません。
←これは簡単

今回記事にした理由は、文字列の右側に画像を表示しようとしているためです。
たとえば列でソートする機能を作成した時や、何らかの印を表示したい時などに便利かと。
ではさっそく。

環境は Form 上に Button, ListView, ImageList をそれぞれ一つずつ配置しています。
ImageList には適当に画像を登録しておいてください。
カラムに登録するサイズなのでフォントサイズにもよりますが、縦16 x 横8 程度が見栄えいいかと。
とりあえず、この投稿では以下のような感じで登録しています。

参照したサイトは以下の通り。
●PRB: Cannot Assign Images to a ColumnHeader Control in a Windows Forms ListView Control in Visual C# .NET
●Common Control Versions (Windows)
●LVCOLUMN structure (Windows)

おまけの類似項目
●HDITEM structure (Windows)

とりあえず宣言部分です
using System.Runtime.InteropServices;

public const UInt32 LVM_GETHEADER = 4127;
public const UInt32 HDM_SETIMAGELIST = 4616;
public const UInt32 LVM_SETCOLUMN = 4122;
public const uint LVCF_FMT = 1;
public const uint LVCF_IMAGE = 16;
public const int LVCFMT_IMAGE = 2048;

// ListView のカラム情報設定用構造体
[StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Auto)]
public struct LVCOLUMN
{
    public uint mask;
    public int fmt;
    public int cx;
    public IntPtr pszText;
    public int cchTextMax;
    public int iSubItem;
    public int iImage;
    public int iOrder;
}

// 以下 fmt で利用する定義で参照元に無かったやつ( CommCtrl.h より定義参照)
// ComCtl32.dll ver4.70 以降でのみ利用可能らしい
// ただし 4.70 というのは Internet Explorer 3.0 って事なので今時では制約にならない
public const int HDF_IMAGE = 0x0800;      // Same as LVCFMT_IMAGE
public const int HDF_BITMAP_ON_RIGHT = 0x1000;      // Same as LVCFMT_BITMAP_ON_RIGHT
// おまけ:参考サイトのコメント欄にて発見。隠し?項目らしいです(CommCtrl.h には定義あり)
public const int HDF_SORTUP = 0x0400;      // Windows7 Explorer のソート順表示(上)
public const int HDF_SORTDOWN = 0x0200;      // Windows7 Explorer のソート順表示(下)

[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, UInt32 wParam, UInt32 lParam);

[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 msg, UInt32 wParam, ref LVCOLUMN lParam);
覚書みたいなものはコメントにゴチャゴチャ書いているので省略します。

次はボタン押下イベントです。
IntPtr colHeader;
int i;

// リストビューのカラム部分のハンドルを取得
colHeader = SendMessage(listView1.Handle, LVM_GETHEADER, (UInt32)0, (UInt32)0);

// カラム部のハンドルにイメージリストを割り当て
SendMessage(colHeader, HDM_SETIMAGELIST, (UInt32)0, (UInt32)imageList1.Handle);

// カラムの設定
for (i = 0; i < listView1.Columns.Count; i++)
{
    // Use the LVM_SETCOLUMN message to set the column's image index. 
    LVCOLUMN col;
    //  col.mask: include LVCF_FMT | LVCF_IMAGE 
    col.mask = LVCF_FMT | LVCF_IMAGE;

    // LVCFMT_IMAGE 
    col.fmt = LVCFMT_IMAGE | HDF_BITMAP_ON_RIGHT;
//    col.fmt = LVCFMT_IMAGE | HDF_SORTUP;

    // 表示するイメージの番号(仮に固定値で ImageList の 1 番目をしていしている)
    col.iImage = 1;

    //  Initialize the rest to zero.
    col.pszText = (IntPtr)0;
    col.cchTextMax = 0;
    col.cx = 0;
    col.iSubItem = 0;
    col.iOrder = 0;

    // Send the LVM_SETCOLUMN message.
    // The column that we are assigning the image to is defined in the third parameter.
    SendMessage(listView1.Handle, LVM_SETCOLUMN, (UInt32)i, ref col);
}
まぁ何というか、ほぼ MS サイトのコードまる写しです、はい。
col.fmt 部分の設定がキモになっています。
上記通りにやればリンクしてある ImageList の 1 番目の画像が表示されます。
コメントになっているように記述すると Windows7 の explorer の詳細表示部分のソート表記と同じにできます。
左:LVCFMT_IMAGE | HDF_BITMAP_ON_RIGHT
右:LVCFMT_IMAGE | HDF_SORTUP

何にしろ画像は表示できたので目的達成です。

0 件のコメント:

コメントを投稿