« Sandcastle 日本語ローカライズ情報 | トップページ | システムイメージリストを使用したアイコン表示(2) »

2010年11月23日 (火)

システムイメージリストを使用したアイコン表示(1)

今回から、しばらくアイコンネタを続けます。
第一弾として、エクスプローラでファイル名と共に表示されるあのアイコンを、C#アプリケーションの ListView 内で実現するやり方です。

1.表示テストAPの準備

まずは、システムイメージアイコン表示のテスト用として右図の様に設定した画面を用意します。

ListView コントロールは、View プロパティを View.List にします。また、SmallImageList や LargeImageList プロパティには、何も設定しません
ボタンコントロールは、クリックする事で、以下のイベント処理を行ないます。なお、dlgFolderBrowser は、コメント等でわかると思いますが、FolderBrowserDialog クラスのコントロールです。

// 「表示フォルダの選択」ボタンのClickイベント処理
private void btnSelectFolder_Click(object sender, System.EventArgs e)
{
    // 現在の表示対象フォルダを初期選択パスにする
    dlgFolderBrowser.SelectedPath = this.Text;
    // フォルダ参照ダイアログを表示し、OKなら選択フォルダで一覧表示
    if (dlgFolderBrowser.ShowDialog() == DialogResult.OK)
        PutFileListMain(dlgFolderBrowser.SelectedPath);
}

/// <summary>
/// 指定フォルダ内に存在するファイルの一覧をリストビューに登録する
/// </summary>
/// <param name="orderFolder">表示対象とするフォルダパス</param>
private void PutFileListMain(string orderFolder)
{
    // 現在表示している内容をクリア
    lsvFileList.Clear();
    // サブディレクトリを取得してListViewに登録
    PutFileListView(Directory.GetDirectories(orderFolder));
    // ファイルを取得してListViewに登録
    PutFileListView(Directory.GetFiles(orderFolder));
    // ウィンドウキャプションに表示対象のフォルダ名を表示
    this.Text = orderFolder;
}
/// <summary>
/// 指定されたファイル名の文字列配列をListViewに登録する
/// </summary>
/// <param name="fileNames">表示対象のファイル名を格納した配列</param>
private void PutFileListView(string[] fileNames)
{
    foreach (string file in fileNames)
    {
        ListViewItem lvi = new ListViewItem(Path.GetFileName(file));
        lsvFileList.Items.Add(lvi);
    }
}

これで、ボタンクリックで、指定フォルダのファイル一覧が表示出来ます。ついでにフォームのLoadイベントでも、カレントフォルダのファイル一覧を表示出来る様にしておきましょう。

// フォームのLoadイベント処理
private void frmFileList_Load(object sender, System.EventArgs e)
{
    // カレントフォルダを初期表示時点の一覧表示対象にする
    PutFileListMain(Environment.CurrentDirectory);
}

もちろん、この時点ではアイコンは表示されません。しかし、任意のフォルダのファイル一覧を表示出来るアプリケーションが出来ました。この時点でのテスト用APの実際のソース一式もここに入れておきます。
「SystemIcon1.cab」をダウンロード
これを使用して、VisualStudio2003上から実行した直後の画面です。

2.システムイメージリストのListViewへの登録

さて、アイコン表示の為、まずはシステムイメージリストのハンドル取得と、そのハンドルを使用しての ListView への登録処理が必要です。

システムイメージリストのハンドルを取得するには、Win32API の SHGetFileInfo を使用します。
APIの詳細情報は、MSDNまたはヘルプを見て下さい。

システムイメージリストは、大きいアイコンと小さいアイコンで別になりますが、下記例では両方取得しています。しかし、このテストAPでは大きいアイコンの方は使用していないので、大きいアイコンの方は削除しても動作には支障はありません。
ハンドルを取得したら、これをListViewへ登録する必要があります。これには、C言語とかだと ListView_SetImageList を使用するのですが、これはマクロです。C# ではそのマクロを展開した形式で呼ぶ必要があります。このマクロを展開すると、ListView のウィンドウに対して、LVM_SETIMAGELIST のメッセージを SendMessage する事になります。この時、パラメータとして、先程のシステムイメージリストのハンドルを一緒に渡します。
このハンドルの登録も、大きいアイコンと小さいアイコンでそれぞれで登録します。以上の部分を、フォームの Load イベントに追加した例を以下に示します。

private static IntPtr SmallImageListHandle;
private static IntPtr LargeImageListHandle;
// フォームのLoadイベント処理
private void frmFileList_Load(object sender, System.EventArgs e)
{
    SHFILEINFO shFileInfo = new SHFILEINFO();
    SmallImageListHandle = NativeMethods.SHGetFileInfo(String.Empty, 0,
        out shFileInfo, (uint)Marshal.SizeOf(shFileInfo),
        NativeMethods.SHGFI_SMALLICON | NativeMethods.SHGFI_SYSICONINDEX);
    LargeImageListHandle = NativeMethods.SHGetFileInfo(String.Empty, 0,
        out shFileInfo, (uint)Marshal.SizeOf(shFileInfo),
        NativeMethods.SHGFI_LARGEICON | NativeMethods.SHGFI_SYSICONINDEX);
    NativeMethods.SendMessage(lsvFileList.Handle, NativeMethods.LVM_SETIMAGELIST,
        new IntPtr(NativeMethods.LVSIL_SMALL), SmallImageListHandle);
    NativeMethods.SendMessage(lsvFileList.Handle, NativeMethods.LVM_SETIMAGELIST,
        new IntPtr(NativeMethods.LVSIL_NORMAL), LargeImageListHandle);
    // カレントフォルダを初期表示時点の一覧表示対象にする
    PutFileListMain(Environment.CurrentDirectory);
}

また、API関連は別ファイルとして定義しています。こちらは、以下の様に記述します。

using System;
using System.Runtime.InteropServices;

namespace Acha_ya.SampleApplication
{
    [ StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto) ]
    public struct SHFILEINFO 
    {
        public IntPtr hIcon;
        public int iIcon;
        public uint dwAttributes;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public string szDisplayName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
        public string szTypeName;
    }
    /// <summary>
    /// NativeMethods の概要の説明です。
    /// </summary>
    public class NativeMethods
    {
        public const int SHGFI_LARGEICON        = 0x00000000;
        public const int SHGFI_SMALLICON        = 0x00000001;
        public const int SHGFI_USEFILEATTRIBUTES= 0x00000010;
        public const int SHGFI_OVERLAYINDEX     = 0x00000040;
        public const int SHGFI_ICON             = 0x00000100;
        public const int SHGFI_SYSICONINDEX     = 0x00004000;
        public const int LVSIL_NORMAL = 0;
        public const int LVSIL_SMALL  = 1;
        public const int LVM_SETIMAGELIST = 0x1003;
        [DllImport("shell32.dll", CharSet=CharSet.Auto)]
        public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes,
            out SHFILEINFO psfi, uint cbFileInfo, uint uFlags);
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
        public NativeMethods()
        {
        }
    }
}

これらの記述してみた時点で、もう一度実行してみましょう。まだリメージリストを登録しただけですから、アイコンは表示されませんが、アイコン表示用のスペースが確保されて、ファイル名が表示される様になっています。

以上、とりあえず今回はここまでとします。次回、実際のアイコンを表示させる部分の実装を行なってみたいと思います。また、この時点までを実装したソース一式もここに置いておきます。

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


« Sandcastle 日本語ローカライズ情報 | トップページ | システムイメージリストを使用したアイコン表示(2) »

C# Tips」カテゴリの記事

コメント

コメントを書く

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

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

トラックバック


この記事へのトラックバック一覧です: システムイメージリストを使用したアイコン表示(1):

« Sandcastle 日本語ローカライズ情報 | トップページ | システムイメージリストを使用したアイコン表示(2) »