« Win32APIで発生したエラー処理(GetLastError 処理相当の処理) | トップページ | Intersect/Union/Inflate/Offset メソッド »

2010年10月12日 (火)

ListView/TreeViewにオーバーレイアイコンを表示する

ListView/TreeView には、元々オーバーレイアイコンの表示機能があります。プロパティでのサポートがされてない様なので、ここで Win32API を使用しての表示方法を書きます。

ポイントは、以下の2点です。

  1. イメージリスト中登録されているアイコンの中で、オーバーレイアイコンとして使用する物を登録する。
  2. 実際に表示する段階で、TreeNode あるいは ListViewItem に対して、オーバーレイアイコンの設定を行なう。

では、順に説明しましょう。

1.イメージリスト中登録されているアイコンの中で、オーバーレイアイコンとして使用する物を登録する。

これは、ImageList_SetOverlayImage というAPIを使用します。

[DllImport("comctl32.dll")]
static extern int ImageList_SetOverlayImage(IntPtr himl, int iImage, int iOverlay);

という定義を施せば、使用出来ます。第1引数 ImageList には、ImageList クラスのインスタンスハンドルを、第2引数 iImage はオーバーレイアイコンにしたい ImageList 中のアイコンインデックス(0 を開始点とする)を、第3引数 iOverlay は、登録するオーバーレイインデックス値(1 を開始点とする)を指定します。例えば、

ImageList_SetOverlayImage(imageList1.Handle, 2, 1);

と記述すると、ImageList クラスのインスタンスである imageList1 の3番目のイメージ(imageList1.Images[2] のイメージ)を、オーバーレイインデックス値 1 として登録する事を意味します。

2.実際に表示する段階で、オーバーレイアイコンの設定を行なう。
2-1.TreeViewでのオーバーレイアイコンの設定

これは、C言語等では、TreeView_SetItemState が使用されます。しかし、TreeView_SetItemState という API が存在する訳ではなく、そういうマクロ処理が行われているのです。ですので、これに相当するマクロを展開した処理を実装する事になります。

この TreeView_SetItemState に相当する部分を、以下の様な汎用的な処理にまとめてみました。この処理では、Win32API 関連を、NativeMethods クラスで定義している前提としています。NativeMethods クラスで定義している構造体 TVITEM と、各種定数の説明は省略します。詳しくは、ヘルプ等で調べてみて下さい。但し、TVITEM 構造体の state メンバのみ、最後の方でちょっと解説します。
この処理を呼べば、指定された TreeNode にオーバーレイアイコンが設定されます。

/// <summary>
/// 指定した TreeNode にオーバーレイアイコンを表示する様に指示する。
/// </summary>
/// <param name="node">対象となる TreeNode オブジェクト</param>
/// <param name="overlayIndex">オーバーレイインデックス</param>
public static void TreeViewOverlay(TreeNode node, uint overlayIndex)
{
    // TreeView_SetItemState(node.TreeView.Handle, node.Handle,
    //     overlayIndex << 8, TVIS_OVERLAYMASK); 相当の処理
    TVITEM tvi = new TVITEM();
    tvi.mask = NativeMethods.TVIF_STATE;
    tvi.hItem = node.Handle;
    tvi.stateMask = NativeMethods.TVIS_OVERLAYMASK;
    tvi.state = (overlayIndex << 8);
    NativeMethods.SendMessage(node.TreeView.Handle,
        NativeMethods.TVM_SETITEMW, 0, ref tvi);
}

また、NativeMethods クラスの定義は、後述する ListView 分も含めて、以下の様に定義しています。

using System;
using System.Runtime.InteropServices;

namespace Acha_ya.SampleApplication
{
    [ StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto) ]
    public struct LVITEM
    {
        public uint mask;
        public int  iItem;
        public int  iSubItem;
        public uint state;
        public uint stateMask;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
        public string pszText;
        public int  cchTextMax;
        public int  iImage;
        public uint lParam;
        public int  iIndent;
    }
    [ StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto) ]
    public struct TVITEM
    {
        public uint mask;
        public IntPtr hItem;
        public uint state;
        public uint stateMask;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
        public string pszText;
        public int  cchTextMax;
        public int  iImage;
        public int  iSelectedImage;
        public int  cChildren;
        public uint lParam;
        public int  iIntegral;
    }
    /// <summary>
    /// Win32API及びその定数の定義クラス
    /// </summary>
    public class NativeMethods
    {
        public const int TVIF_STATE       = 0x0008;
        public const int TVIS_OVERLAYMASK = 0x0F00;
        public const int TVM_SETITEMW     = 0x113F;
        public const int LVSIL_SMALL  = 1;
        public const int LVIS_OVERLAYMASK = 0x0F00;
        public const int LVM_SETIMAGELIST = 0x1003;
        public const int LVM_SETITEMSTATE = 0x102B;
        [DllImport("comctl32.dll")]
        public static extern int ImageList_SetOverlayImage(IntPtr himl, int iImage, int iOverlay);
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, ref TVITEM lParam);
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, ref LVITEM lParam);
        public NativeMethods()
        {
        }
    }
}

2-2.ListViewでのオーバーレイアイコンの処理
TreeView 同様で、C言語等では、ListView_SetItemState が使用されます。これもマクロで、その展開した内容を実装する事になります。

そして、これも同様に汎用的な処理にまとめてみました。この処理を呼べば、指定された ListViewItem にオーバーレイアイコンが設定されます。

/// <summary>
/// 指定した ListViewItem にオーバーレイアイコンを表示する様に指示する。
/// </summary>
/// <param name="listItem">対象となる ListViewItem オブジェクト</param>
/// <param name="overlayIndex">オーバーレイインデックス</param>
public static void ListViewOverlay(ListViewItem listItem, uint overlayIndex)
{
    // ListView_SetItemState(listItem.ListView.Handle, listItem.Index,
    //     overlayIndex << 8, LVIS_OVERLAYMASK); 相当の処理
    LVITEM lvi = new LVITEM();
    lvi.stateMask = NativeMethods.LVIS_OVERLAYMASK;
    lvi.state = (overlayIndex << 8);
    NativeMethods.SendMessage(listItem.ListView.Handle,
        NativeMethods.LVM_SETITEMSTATE, listItem.Index, ref lvi);
}


ここで、TVITEM 構造体の state メンバ及び LVITEM 構造体の state メンバについて、ちょっと解説します。上記の処理では、どちらもオーバーレイアイコンのインデックス値を格納しますが、そのままの値を移送するのではなく、ビットシフトした値を格納していました。

これは、それぞれの構造体の state メンバが、オーバーレイアイコン値だけでなく、他の値も一緒に格納出来るように、使用出来るビットエリアが決められているからです。そして、オーバーレイアイコンのインデックス値を格納するエリアが、どちらもビット8~11の範囲に格納する事になっています。左に8ビットシフトするのはこの為です。


さて、上記コード検証するサンプルコードを作成しました。簡単に説明すると、既に右図の様なイメージを登録済みのイメージリストをフォームに貼り付けてあります。イメージリストの TranceparentColor を、Color.Cyan に設定してあるので、イメージインデックス番号1及び3の「水色」部分は、実際は透明になります。そしてこの2つをオーバーレイアイコンとして登録しています。
また、残りのイメージインデックス0, 2, 4 は通常のイメージとして使用します。

右図は、このサンプルを実行して、下部のボタンをそれぞれクリックした後の画面です。項番0, 3, 6 の TreeNode / ListViewItem は、通常のイメージだけを表示しています。項番1, 4, 7 は、これに加えて、オーバーレイアイコンインデックス 1 のイメージ(イメージリストのイメージインデックス番号1のイメージ)が合わせて表示されています。同様に、項番2, 5, 8 は、オーバーレイアイコンインデックス 2 のイメージが合わせて表示されています。

最後に、この手抜きなサンプルコード一式を、CAB形式でまとめたファイルをアップロードしておきます。

「Sample.cab」をダウンロード

« Win32APIで発生したエラー処理(GetLastError 処理相当の処理) | トップページ | Intersect/Union/Inflate/Offset メソッド »

C# Tips」カテゴリの記事

コメント

コメントを書く

コメントは記事投稿者が公開するまで表示されません。

(ウェブ上には掲載しません)

トラックバック


この記事へのトラックバック一覧です: ListView/TreeViewにオーバーレイアイコンを表示する:

« Win32APIで発生したエラー処理(GetLastError 処理相当の処理) | トップページ | Intersect/Union/Inflate/Offset メソッド »