/* * [File purpose] * Author: Phillip Piper * Date: 1 May 2007 7:44 PM * * CHANGE LOG: * 2009-07-08 JPP Don't cache the image collections * 1 May 2007 JPP Initial Version */ using System; using System.Drawing; using System.Runtime.InteropServices; using System.Windows.Forms; using BrightIdeasSoftware; namespace ObjectListViewDemo { /// /// This helper class allows listviews and tree views to use image from the system image list. /// /// Instances of this helper class know how to retrieve icon from the Windows shell for /// a given file path. These icons are then added to the imagelist on the given control. ListViews need /// special handling since they have two image lists which need to be kept in sync. public class SysImageListHelper { private SysImageListHelper() { } protected ImageList.ImageCollection SmallImageCollection { get { if (this.listView != null) return this.listView.SmallImageList.Images; if (this.treeView != null) return this.treeView.ImageList.Images; return null; } } protected ImageList.ImageCollection LargeImageCollection { get { if (this.listView != null) return this.listView.LargeImageList.Images; return null; } } protected ImageList SmallImageList { get { if (this.listView != null) return this.listView.SmallImageList; if (this.treeView != null) return this.treeView.ImageList; return null; } } protected ImageList LargeImageList { get { if (this.listView != null) return this.listView.LargeImageList; return null; } } /// /// Create a SysImageListHelper that will fetch images for the given tree control /// /// The tree view that will use the images public SysImageListHelper(TreeView treeView) { if (treeView.ImageList == null) { treeView.ImageList = new ImageList(); treeView.ImageList.ImageSize = new Size(16, 16); } this.treeView = treeView; } protected TreeView treeView; /// /// Create a SysImageListHelper that will fetch images for the given listview control. /// /// The listview that will use the images /// Listviews manage two image lists, but each item can only have one image index. /// This means that the image for an item must occur at the same index in the two lists. /// SysImageListHelper instances handle this requirement. However, if the listview already /// has image lists installed, they must be of the same length. public SysImageListHelper(ObjectListView listView) { if (listView.SmallImageList == null) { listView.SmallImageList = new ImageList(); listView.SmallImageList.ColorDepth = ColorDepth.Depth32Bit; listView.SmallImageList.ImageSize = new Size(16, 16); } if (listView.LargeImageList == null) { listView.LargeImageList = new ImageList(); listView.LargeImageList.ColorDepth = ColorDepth.Depth32Bit; listView.LargeImageList.ImageSize = new Size(32, 32); } //if (listView.SmallImageList.Images.Count != listView.LargeImageList.Images.Count) // throw new ArgumentException("Small and large image lists must have the same number of items."); this.listView = listView; } protected ObjectListView listView; /// /// Return the index of the image that has the Shell Icon for the given file/directory. /// /// The full path to the file/directory /// The index of the image or -1 if something goes wrong. public int GetImageIndex(string path) { if (path.Length <= 3) { path = "C:\\"; } else if (System.IO.Directory.Exists(path)) path = System.Environment.SystemDirectory; // optimization! give all directories the same image else if (System.IO.Path.HasExtension(path)) path = System.IO.Path.GetExtension(path); if (this.SmallImageCollection.ContainsKey(path)) return this.SmallImageCollection.IndexOfKey(path); try { this.AddImageToCollection(path, this.SmallImageList, ShellUtilities.GetFileIcon(path, true, true)); this.AddImageToCollection(path, this.LargeImageList, ShellUtilities.GetFileIcon(path, false, true)); } catch (ArgumentNullException) { return -1; } return this.SmallImageCollection.IndexOfKey(path); } private void AddImageToCollection(string key, ImageList imageList, Icon image) { if (imageList == null) return; if (imageList.ImageSize == image.Size) { imageList.Images.Add(key, image); return; } using (Bitmap imageAsBitmap = image.ToBitmap()) { Bitmap bm = new Bitmap(imageList.ImageSize.Width, imageList.ImageSize.Height); Graphics g = Graphics.FromImage(bm); g.Clear(imageList.TransparentColor); Size size = imageAsBitmap.Size; int x = Math.Max(0, (bm.Size.Width - size.Width) / 2); int y = Math.Max(0, (bm.Size.Height - size.Height) / 2); g.DrawImage(imageAsBitmap, x, y, size.Width, size.Height); imageList.Images.Add(key, bm); } } } /// /// ShellUtilities contains routines to interact with the Windows Shell. /// public static class ShellUtilities { /// /// Execute the default verb on the file or directory identified by the given path. /// For documents, this will open them with their normal application. For executables, /// this will cause them to run. /// /// The file or directory to be executed /// Values < 31 indicate some sort of error. See ShellExecute() documentation for specifics. /// The same effect can be achieved by System.Diagnostics.Process.Start(path). public static int Execute(string path) { return ShellUtilities.Execute(path, ""); } /// /// Execute the given operation on the file or directory identified by the given path. /// Example operations are "edit", "print", "explore". /// /// The file or directory to be operated on /// What operation should be performed /// Values < 31 indicate some sort of error. See ShellExecute() documentation for specifics. public static int Execute(string path, string operation) { IntPtr result = ShellUtilities.ShellExecute(0, operation, path, "", "", SW_SHOWNORMAL); return result.ToInt32(); } /// /// Get the string that describes the file's type. /// /// The file or directory whose type is to be fetched /// A string describing the type of the file, or an empty string if something goes wrong. public static String GetFileType(string path) { SHFILEINFO shfi = new SHFILEINFO(); int flags = SHGFI_TYPENAME; IntPtr result = ShellUtilities.SHGetFileInfo(path, 0, out shfi, Marshal.SizeOf(shfi), flags); if (result.ToInt32() == 0) return String.Empty; else return shfi.szTypeName; } /// /// Return the icon for the given file/directory. /// /// The full path to the file whose icon is to be returned /// True if the small (16x16) icon is required, otherwise the 32x32 icon will be returned /// If this is true, only the file extension will be considered /// The icon of the given file, or null if something goes wrong public static Icon GetFileIcon(string path, bool isSmallImage, bool useFileType) { int flags = SHGFI_ICON; if (isSmallImage) flags |= SHGFI_SMALLICON; int fileAttributes = 0; if (useFileType) { flags |= SHGFI_USEFILEATTRIBUTES; if (System.IO.Directory.Exists(path)) fileAttributes = FILE_ATTRIBUTE_DIRECTORY; else fileAttributes = FILE_ATTRIBUTE_NORMAL; } SHFILEINFO shfi = new SHFILEINFO(); try { IntPtr result = ShellUtilities.SHGetFileInfo(path, fileAttributes, out shfi, Marshal.SizeOf(shfi), flags); if (result.ToInt32() == 0) return null; else return Icon.FromHandle(shfi.hIcon); } catch { return null; } } /// /// Return the index into the system image list of the image that represents the given file. /// /// The full path to the file or directory whose icon is required /// The index of the icon, or -1 if something goes wrong /// This is only useful if you are using the system image lists directly. Since there is /// no way to do that in .NET, it isn't a very useful. public static int GetSysImageIndex(string path) { SHFILEINFO shfi = new SHFILEINFO(); int flags = SHGFI_ICON | SHGFI_SYSICONINDEX; IntPtr result = ShellUtilities.SHGetFileInfo(path, 0, out shfi, Marshal.SizeOf(shfi), flags); if (result.ToInt32() == 0) return -1; else return shfi.iIcon; } #region Native methods private const int SHGFI_ICON = 0x00100; // get icon private const int SHGFI_DISPLAYNAME = 0x00200; // get display name private const int SHGFI_TYPENAME = 0x00400; // get type name private const int SHGFI_ATTRIBUTES = 0x00800; // get attributes private const int SHGFI_ICONLOCATION = 0x01000; // get icon location private const int SHGFI_EXETYPE = 0x02000; // return exe type private const int SHGFI_SYSICONINDEX = 0x04000; // get system icon index private const int SHGFI_LINKOVERLAY = 0x08000; // put a link overlay on icon private const int SHGFI_SELECTED = 0x10000; // show icon in selected state private const int SHGFI_ATTR_SPECIFIED = 0x20000; // get only specified attributes private const int SHGFI_LARGEICON = 0x00000; // get large icon private const int SHGFI_SMALLICON = 0x00001; // get small icon private const int SHGFI_OPENICON = 0x00002; // get open icon private const int SHGFI_SHELLICONSIZE = 0x00004; // get shell size icon private const int SHGFI_PIDL = 0x00008; // pszPath is a pidl private const int SHGFI_USEFILEATTRIBUTES = 0x00010; // use passed dwFileAttribute //if (_WIN32_IE >= 0x0500) private const int SHGFI_ADDOVERLAYS = 0x00020; // apply the appropriate overlays private const int SHGFI_OVERLAYINDEX = 0x00040; // Get the index of the overlay private const int FILE_ATTRIBUTE_NORMAL = 0x00080; // Normal file private const int FILE_ATTRIBUTE_DIRECTORY = 0x00010; // Directory private const int MAX_PATH = 260; [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] private struct SHFILEINFO { public IntPtr hIcon; public int iIcon; public int dwAttributes; [MarshalAs(UnmanagedType.ByValTStr, SizeConst=MAX_PATH)] public string szDisplayName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst=80)] public string szTypeName; } private const int SW_SHOWNORMAL = 1; [DllImport("shell32.dll", CharSet = CharSet.Auto)] private static extern IntPtr ShellExecute(int hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, int nShowCmd); [DllImport("shell32.dll", CharSet = CharSet.Auto)] private static extern IntPtr SHGetFileInfo(string pszPath, int dwFileAttributes, out SHFILEINFO psfi, int cbFileInfo, int uFlags); #endregion } }