2019-07-31に投稿

【C#】TreeViewとListViewにシステムイメージ(アイコン)を表示する

読了目安:10分

前回に引き続き、Windows Forms で、エクスプローラーのような表示をするために、
今回はファイル・フォルダの横にアイコンを表示してみます。

プログラムは前回のものを流用します。

https://www.doraxdora.com/blog/2018/01/30/post-3801/

プログラムの修正

DLL関数の呼び出し宣言を追加

システムイメージを利用するには「Shll32.dll」、「user32.dll」の関数を呼び出す必要があるため、関数呼び出しの宣言を追加します。

    // DllImportに必要
    using System.Runtime.InteropServices;

            /// <summary>
            /// ファイル情報を取得
            /// </summary>
            /// <param name="pszPath"></param>
            /// <param name="dwFileAttributes"></param>
            /// <param name="psfi"></param>
            /// <param name="cbFileInfo"></param>
            /// <param name="uFlags"></param>
            /// <returns></returns>
            [DllImport("shell32.dll", CharSet = CharSet.Auto)]
            private static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, out SHFILEINFO psfi, uint cbFileInfo, uint uFlags);

            /// <summary>
            /// イメージリストを登録
            /// </summary>
            /// <param name="hWnd"></param>
            /// <param name="Msg"></param>
            /// <param name="wParam"></param>
            /// <param name="lParam"></param>
            /// <returns></returns>
            [DllImport("user32.dll", CharSet = CharSet.Auto)]
            private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

構造体や定数の定義

DLL関数で利用する構造体や定数の定義を追加します。

            /// <summary>
            /// SHGetFileInfo関数で使用する構造体
            /// </summary>
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
            private 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;
            };

            // ファイル情報用
            private const int SHGFI_LARGEICON = 0x00000000;
            private const int SHGFI_SMALLICON = 0x00000001;
            private const int SHGFI_USEFILEATTRIBUTES = 0x00000010;
            private const int SHGFI_OVERLAYINDEX = 0x00000040;
            private const int SHGFI_ICON = 0x00000100;
            private const int SHGFI_SYSICONINDEX = 0x00004000;
            private const int SHGFI_TYPENAME = 0x000000400;

            // TreeView用
            private const int TVSIL_NORMAL = 0x0000;
            private const int TVSIL_STATE = 0x0002;
            private const int TVM_SETIMAGELIST = 0x1109;

            // ListView用
            private const int LVSIL_NORMAL = 0;
            private const int LVSIL_SMALL = 1;
            private const int LVM_SETIMAGELIST = 0x1003;

            // 選択された項目を保持
            private String selectedItem = "";

起動時にシステムイメージを登録

起動時の処理で、TreeView、ListViewにイメージのリストを登録します。

            /// <summary>
            /// 起動時の処理
            /// </summary>
            public Form1()
            {
                InitializeComponent();

                // イメージリストの設定
                SHFILEINFO shFileInfo = new SHFILEINFO();
                IntPtr imageListHandle = SHGetFileInfo(String.Empty, 0, out shFileInfo, (uint)Marshal.SizeOf(shFileInfo), SHGFI_SMALLICON | SHGFI_SYSICONINDEX);
                // TreeView
                SendMessage(treeView1.Handle, TVM_SETIMAGELIST, new IntPtr(TVSIL_NORMAL), imageListHandle);
                // ListView
                SendMessage(listView1.Handle, LVM_SETIMAGELIST, new IntPtr(LVSIL_SMALL), imageListHandle);

                // ドライブ一覧を走査してツリーに追加
                foreach (String drive in Environment.GetLogicalDrives())
                {
                    // アイコンの取得
                    int iconIndex = 0;
                    IntPtr hSuccess = SHGetFileInfo(drive, 0, out shFileInfo, (uint)Marshal.SizeOf(shFileInfo), SHGFI_SYSICONINDEX );
                    if (hSuccess != IntPtr.Zero)
                    {
                        iconIndex = shFileInfo.iIcon;
                    }

                    // 新規ノード作成
                    // プラスボタンを表示するため空のノードを追加しておく
                    TreeNode node = new TreeNode(drive, iconIndex, iconIndex);
                    node.Nodes.Add(new TreeNode());
                    treeView1.Nodes.Add(node);
                }

                // 初期選択ドライブの内容を表示
                setListItem(Environment.GetLogicalDrives().First());
            }

ListViewの内容更新時の処理を修正

TreeViewが選択された際にListViewの内容を更新する処理で、
表示するファイル、フォルダ―のイメージを取得して設定します。

            /// <summary>
            /// リストビューの項目を設定します.
            /// </summary>
            private void setListItem(String filePath)
            {
                // リストビューのヘッダーを設定
                listView1.View = View.Details;
                listView1.Clear();
                listView1.Columns.Add("名前");
                listView1.Columns.Add("種類");
                listView1.Columns.Add("更新日時");
                listView1.Columns.Add("サイズ");

                try
                {
                    // フォルダ一覧
                    DirectoryInfo dirList = new DirectoryInfo(filePath);
                    foreach (DirectoryInfo di in dirList.GetDirectories())
                    {
                        ListViewItem item = new ListViewItem(di.Name);

                        // フォルダ種類、アイコンの取得
                        String type = "";
                        int iconIndex = 0;
                        SHFILEINFO shFileInfo = new SHFILEINFO();
                        IntPtr hSuccess = SHGetFileInfo(di.FullName, 0, out shFileInfo, (uint)Marshal.SizeOf(shFileInfo), SHGFI_ICON | SHGFI_LARGEICON | SHGFI_SMALLICON | SHGFI_SYSICONINDEX | SHGFI_TYPENAME);
                        if (hSuccess != IntPtr.Zero)
                        {
                            type = shFileInfo.szTypeName;
                            iconIndex = shFileInfo.iIcon;
                        }

                        // 各列の内容を設定
                        item.ImageIndex = iconIndex;
                        item.SubItems.Add(type);
                        item.SubItems.Add(String.Format("{0:yyyy/MM/dd HH:mm:ss}", di.LastAccessTime));
                        item.SubItems.Add("");

                        // リストに追加
                        listView1.Items.Add(item);

                    }

                    // ファイル一覧
                    List<String> files = Directory.GetFiles(filePath).ToList<String>();
                    foreach (String file in files)
                    {
                        FileInfo info = new FileInfo(file);
                        ListViewItem item = new ListViewItem(info.Name);

                        // ファイル種類、アイコンの取得
                        String type = "";
                        int iconIndex = 0;
                        SHFILEINFO shinfo = new SHFILEINFO();
                        IntPtr hSuccess = SHGetFileInfo(info.FullName, 0, out shinfo, (uint)Marshal.SizeOf(shinfo), SHGFI_ICON | SHGFI_LARGEICON | SHGFI_SMALLICON | SHGFI_SYSICONINDEX | SHGFI_TYPENAME);
                        if (hSuccess != IntPtr.Zero)
                        {
                            type = shinfo.szTypeName;
                            iconIndex = shinfo.iIcon;
                        }

                        // 各列の内容を設定
                        item.ImageIndex = iconIndex;
                        item.SubItems.Add(type);
                        item.SubItems.Add(String.Format("{0:yyyy/MM/dd HH:mm:ss}", info.LastAccessTime));
                        item.SubItems.Add(getFileSize(info.Length));
                        listView1.Items.Add(item);
                    }
                }
                catch (IOException ie)
                {
                    MessageBox.Show(ie.Message, "選択エラー");
                }

                // 列幅を自動調整
                listView1.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);
            }

TreeView展開時の処理を修正

同様に TreeView を展開する際の処理で、
フォルダ―のアイコンを取得して設定するように修正します。

            /// <summary>
            /// ツリービュー項目展開時(前)のイベントハンドラ.
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void treeView1_BeforeExpand(object sender, TreeViewCancelEventArgs e)
            {
                TreeNode node = e.Node;
                node.Nodes.Clear();

                try
                {
                    DirectoryInfo dirList = new DirectoryInfo(node.FullPath);
                    foreach (DirectoryInfo di in dirList.GetDirectories())
                    {
                        // フォルダのアイコンを取得
                        SHFILEINFO shinfo = new SHFILEINFO();
                        int iconIndex = 0;
                        IntPtr hSuccess = SHGetFileInfo(di.FullName, 0, out shinfo, (uint)Marshal.SizeOf(shinfo), SHGFI_ICON | SHGFI_LARGEICON | SHGFI_SMALLICON | SHGFI_SYSICONINDEX | SHGFI_TYPENAME);
                        if (hSuccess != IntPtr.Zero)
                        {
                            iconIndex = shinfo.iIcon;
                        }

                        // 子を追加してノードを展開
                        TreeNode child = new TreeNode(di.Name, shinfo.iIcon, shinfo.iIcon);
                        child.ImageIndex = iconIndex;
                        child.Nodes.Add(new TreeNode());
                        node.Nodes.Add(child);
                    }
                }
                catch (IOException ie)
                {
                    MessageBox.Show(ie.Message, "選択エラー");
                }

            }

起動してみる

起動イメージ

無事に TreeView、ListViewにアイコンを表示することができ、
更にエクスプローラーのようなUIになりました。

まとめ

プログラムについては共通化したり、別クラスに処理をまとめたりできるので整理が必要ですが、
ひとまずアイコンを表示する方法を試してみました。

Originally published at www.doraxdora.com

doraxdora

IT関係の仕事をしています/1985年生まれの東京在住/便利なサービスやツール漁りや料理などが好き/2017年~ブログやってます/自分でサービスとか作ってリリースしたい/何かありましたらお気軽にDMどうぞ

Crieitは個人で開発中です。 興味がある方は是非記事の投稿をお願いします! どんな軽い内容でも嬉しいです。
なぜCrieitを作ろうと思ったか

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください!

ボードとは?

関連記事

コメント