using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; using Vanara.PInvoke; using static Vanara.PInvoke.ComCtl32; using static Vanara.PInvoke.Gdi32; namespace Vanara.Extensions { /// Extension methods for . public static partial class ImageListExtension { private static Dictionary> imageListOverlays = new Dictionary>(); /// Adds the specified image to the ImageList as an overlay, using the specified color to determine transparency. /// The image list. /// A Bitmap of the image to add to the list. /// The color to use as the transparent color within the Bitmap. /// The 1 based index of the overlay. /// The image cannot be added as an overlay. public static int AddOverlay(this ImageList imageList, Image value, Color transparentColor) { var idx = imageList.Images.Add(value, transparentColor); return SetImageIndexAsOverlay(imageList, idx); } /// Draws the image indicated by the given index on the specified at the specified location. /// The image list. /// The to draw on. /// The bounds in which to draw the image. Set width and height to 0 to draw image at full size. /// The index of the image in the ImageList to draw. /// /// The background color of the image. This parameter can be a value or so the image is drawn /// transparently or so the image is drawn using the background color of the image list. /// /// /// The foreground color of the image. This parameter can be a value or so the image is blended /// with the color of the destination device context or so the image is drawn using the system highlight color /// as the foreground color. /// /// The drawing style. /// Optional index of an overlay image. /// Unable to draw the image with defined parameters. public static void Draw(this ImageList imageList, Graphics g, Rectangle bounds, int index, COLORREF bgColor, COLORREF fgColor, IMAGELISTDRAWFLAGS style = IMAGELISTDRAWFLAGS.ILD_NORMAL, int overlayImageIndex = 0) { if (index < 0 || index >= imageList.Images.Count) throw new ArgumentOutOfRangeException(nameof(index)); if (overlayImageIndex < 0 || overlayImageIndex > imageList.GetOverlayCount()) throw new ArgumentOutOfRangeException(nameof(overlayImageIndex)); using (var hg = new SafeTempHDC(g)) { var p = new IMAGELISTDRAWPARAMS(hg, bounds, index, bgColor, style | (IMAGELISTDRAWFLAGS)INDEXTOOVERLAYMASK(overlayImageIndex)) { rgbFg = fgColor }; imageList.GetIImageList().Draw(p); } } /// Gets the current background color for an image list. /// The image list. /// The background color. public static Color GetBackgroundColor(this ImageList imageList) => imageList.GetIImageList().GetBkColor(); /// Gets an object for the instance. /// The image list. /// An object. public static IImageList GetIImageList(this ImageList imageList) => new SafeHIMAGELIST(imageList.Handle, false).Interface; /// Creates an from a handle. /// This is a super hack involving a lot of reflection against internal structures that can change. Use with caution! /// The SafeImageListHandle value. /// An ImageList instance hosting the supplied handle. /// public static ImageList ToImageList(this HIMAGELIST himl) { // Duplicate handle and get IImageList for it var dhiml = ImageList_Duplicate(himl); var iil = dhiml.Interface; // Get internal handle class var nilfi = typeof(ImageList).GetField("nativeImageList", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); if (nilfi == null) throw new PlatformNotSupportedException(); var nil = nilfi.FieldType; // Create a new instance with the handle param var nili = nil.Assembly.CreateInstance(nil.FullName, false, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic, null, new object[] { dhiml.DangerousGetHandle() }, null, null); // Create a new ImageList and initialize with settings from handle var il = new ImageList(); nilfi.SetValue(il, nili); var depthfi = typeof(ImageList).GetField("colorDepth", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); var szfi = typeof(ImageList).GetField("imageSize", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); if (depthfi == null || szfi == null) throw new PlatformNotSupportedException(); var add1 = false; if (iil.GetImageCount() == 0) { add1 = true; iil.SetImageCount(1); } var bmp = Image.FromHbitmap((IntPtr)iil.GetImageInfo(0).hbmImage); var depth = (ColorDepth)(((uint)bmp.PixelFormat & 0x3C00) >> 8); if (add1) iil.SetImageCount(0); depthfi.SetValue(il, depth); var sz = iil.GetIconSize(); szfi.SetValue(il, sz); return il; } /// Creates a new image by combining two existing images. /// The image list that contains the first image. /// The index of the first existing image. /// The image list that contains the second image. /// The index of the second existing image. /// The offset of the second image relative to the first image. /// A merged image. public static Icon MergeImage(this ImageList il1, int idx1, ImageList il2, int idx2, Point offset = default) { var il3 = il1.GetIImageList().Merge(idx1, il2.GetIImageList(), idx2, offset.X, offset.Y, typeof(IImageList).GUID); return il3.GetIcon(0, IMAGELISTDRAWFLAGS.ILD_TRANSPARENT | IMAGELISTDRAWFLAGS.ILD_PRESERVEALPHA).ToIcon(); } /// Gets the current background color for an image list. /// The image list. /// The background color to set. public static void SetBackgroundColor(this ImageList imageList, Color bkColor) => imageList.GetIImageList().SetBkColor(bkColor, out var _); /// Assigns the image at the specified index as an overlay and returns is overlay index. /// The image list. /// Index of the image within the ImageList. /// The 1 based index of the overlay. /// The imageIndex is not in the current list. /// The image cannot be added as an overlay. public static int SetImageIndexAsOverlay(this ImageList imageList, int imageIndex) { if (imageIndex < 0 || imageIndex >= imageList.Images.Count) throw new ArgumentOutOfRangeException(nameof(imageIndex)); if (!imageListOverlays.TryGetValue(imageList, out var vals)) { imageList.RecreateHandle += ImageList_RecreateHandle; imageListOverlays.Add(imageList, vals = new List(15)); } vals.Add(imageIndex); var overlayIndex = vals.Count; imageList.GetIImageList().SetOverlayImage(imageIndex, overlayIndex); return overlayIndex; } private static int GetOverlayCount(this ImageList imageList) => imageListOverlays.TryGetValue(imageList, out var vals) ? vals.Count : 0; private static void ImageList_RecreateHandle(object sender, EventArgs e) { if (!(sender is ImageList il) || !imageListOverlays.TryGetValue(il, out var vals)) return; var iil = il.GetIImageList(); for (var i = 0; i < vals.Count; i++) iil.SetOverlayImage(vals[i], i + 1); } } }