BETA

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

投稿日:2019-07-31
最終更新:2019-07-31

前回に引き続き、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 HHss}", 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 HHss}", 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になりました。

まとめ

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

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

@doraxdoraの技術ブログ 主に Java, C#, Python, Javascript の記事を載せていく予定。

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう