From 9390e080177266b4f36e400b9b918d3b44520856 Mon Sep 17 00:00:00 2001 From: David Hall Date: Fri, 8 Jun 2018 11:25:08 -0600 Subject: [PATCH] Updated color references to COLORREF struct, added XML comments, added Get/SetIconSize extension methods, fixed HIMAGELIST_QueryInterface, added INDEXTOOVERLAYMASK macro, removed ImageListDrawColor --- PInvoke/ComCtl32/CommCtrl.ImageList.cs | 141 +++++++++++-------- UnitTests/UI/Controls/ImageListTests.cs | 77 ++++++++++ UnitTests/UnitTests.csproj | 1 + WIndows.Forms/Extensions/ImageListExtension.cs | 186 ++++++++++++++++--------- 4 files changed, 286 insertions(+), 119 deletions(-) create mode 100644 UnitTests/UI/Controls/ImageListTests.cs diff --git a/PInvoke/ComCtl32/CommCtrl.ImageList.cs b/PInvoke/ComCtl32/CommCtrl.ImageList.cs index f7e9930c..789e9031 100644 --- a/PInvoke/ComCtl32/CommCtrl.ImageList.cs +++ b/PInvoke/ComCtl32/CommCtrl.ImageList.cs @@ -114,6 +114,7 @@ namespace Vanara.PInvoke ILR_SCALE_ASPECTRATIO = 0x0100, } + /// Flags used when copying image lists. [PInvokeData("Commctrl.h", MSDNShortId = "bb775230")] public enum IMAGELISTCOPYFLAG { @@ -174,6 +175,7 @@ namespace Vanara.PInvoke ILD_ASYNC = 0X00008000 } + /// Item flags. [PInvokeData("Commctrl.h", MSDNShortId = "bb761486")] public enum IMAGELISTITEMFLAG { @@ -251,7 +253,7 @@ namespace Vanara.PInvoke /// is set to 1. /// /// A pointer to an int that contains the index of the first new image when it returns, if successful, or -1 otherwise. - int AddMasked(IntPtr hbmImage, uint crMask); + int AddMasked(IntPtr hbmImage, COLORREF crMask); /// Draws an image list item in the specified device context. /// A pointer to an IMAGELISTDRAWPARAMS structure that contains the drawing parameters. @@ -330,11 +332,11 @@ namespace Vanara.PInvoke /// /// The background color to set. If this parameter is set to CLR_NONE, then images draw transparently using the mask. /// A pointer to a COLORREF that contains the previous background color on return if successful, or CLR_NONE otherwise. - void SetBkColor(uint clrBk, ref uint pclr); + void SetBkColor(COLORREF clrBk, out COLORREF pclr); /// Gets the current background color for an image list. /// A pointer to a COLORREF that contains the background color when the method returns. - uint GetBkColor(); + COLORREF GetBkColor(); /// Begins dragging an image. /// A value of type int that contains the index of the image to drag. @@ -408,6 +410,22 @@ namespace Vanara.PInvoke int GetOverlayImage(int iOverlay); } + /// Gets the dimensions of images in an image list. All images in an image list have the same dimensions. + /// The instance. + /// The size of images. + public static Size GetIconSize(this IImageList il) + { + il.GetIconSize(out int cx, out int cy); + return new Size(cx, cy); + } + + /// Sets the dimensions of images in an image list and removes all images from the list. + /// The instance. + /// + /// A value that contains the width and height, in pixels, of the images in the image list. All images in an image list have the same dimensions. + /// + public static void SetIconSize(this IImageList il, Size size) => il.SetIconSize(size.Width, size.Height); + /// Extends IImageList by providing additional methods for manipulating and interacting with image lists. [PInvokeData("Commctrl.h", MSDNShortId = "bb761419")] [ComImport, Guid("192b9d83-50fc-457b-90a0-2b82a8b5dae1"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), CoClass(typeof(CImageList))] @@ -453,7 +471,7 @@ namespace Vanara.PInvoke /// is set to 1. /// /// A pointer to an int that contains the index of the first new image when it returns, if successful, or -1 otherwise. - new int AddMasked(IntPtr hbmImage, uint crMask); + new int AddMasked(IntPtr hbmImage, COLORREF crMask); /// Draws an image list item in the specified device context. /// A pointer to an IMAGELISTDRAWPARAMS structure that contains the drawing parameters. @@ -532,11 +550,12 @@ namespace Vanara.PInvoke /// /// The background color to set. If this parameter is set to CLR_NONE, then images draw transparently using the mask. /// A pointer to a COLORREF that contains the previous background color on return if successful, or CLR_NONE otherwise. - new void SetBkColor(uint clrBk, ref uint pclr); + new void SetBkColor(COLORREF clrBk, out COLORREF pclr); /// Gets the current background color for an image list. /// A pointer to a COLORREF that contains the background color when the method returns. - new uint GetBkColor(); + new COLORREF GetBkColor(); + /// Begins dragging an image. /// A value of type int that contains the index of the image to drag. @@ -706,6 +725,21 @@ namespace Vanara.PInvoke [PInvokeData("Commctrl.h")] public static extern HRESULT HIMAGELIST_QueryInterface(IntPtr himl, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, [Out, MarshalAs(UnmanagedType.IUnknown)] out object ppv); + /// Get an image list interface from an image list handle. + /// + /// Type: HIMAGELIST + /// A handle to the image list to query. + /// + /// + /// When this method returns, contains the interface pointer requested. This is normally IImageList2, which provides the Initialize method. + /// + [PInvokeData("Commctrl.h")] + public static TIntf HIMAGELIST_QueryInterface(IntPtr himl) + { + if (himl == IntPtr.Zero) throw new ArgumentNullException(nameof(himl)); + return (TIntf)Marshal.GetTypedObjectForIUnknown(himl, typeof(TIntf)); + } + /// Get an image list handle from an image list interface. /// /// Type: HIMAGELIST @@ -745,7 +779,34 @@ namespace Vanara.PInvoke [PInvokeData("CommonControls.h", MSDNShortId = "bb761518")] public static extern HRESULT ImageList_CoCreateInstance([MarshalAs(UnmanagedType.LPStruct)] Guid rclsid, [MarshalAs(UnmanagedType.IUnknown)] object punkOuter, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, [Out, MarshalAs(UnmanagedType.IUnknown)] out object ppv); - /// Creates a new image list.Type: intThe width, in pixels, of each image.Type: intThe height, in pixels, of each image.Type: UINTA set of bit flags that specify the type of image list to create. This parameter can be a combination of the Image List Creation Flags.Type: intThe number of images that the image list initially contains.Type: intThe number of images by which the image list can grow when the system needs to make room for new images. This parameter represents the number of new images that the resized image list can contain.Type: HIMAGELISTReturns the handle to the image list if successful, or NULL otherwise. + /// Creates a new image list. + /// + /// Type: int + /// The width, in pixels, of each image. + /// + /// + /// Type: int + /// The height, in pixels, of each image. + /// + /// + /// Type: UINT + /// A set of bit flags that specify the type of image list to create. This parameter can be a combination of the Image List Creation Flags. + /// + /// + /// Type: int + /// The number of images that the image list initially contains. + /// + /// + /// Type: int + /// + /// The number of images by which the image list can grow when the system needs to make room for new images. This parameter represents the number of new + /// images that the resized image list can contain. + /// + /// + /// + /// Type: HIMAGELIST + /// Returns the handle to the image list if successful, or NULL otherwise. + /// // HIMAGELIST ImageList_Create( int cx, int cy, UINT flags, int cInitial, int cGrow); // https://msdn.microsoft.com/en-us/library/windows/desktop/bb761522(v=vs.85).aspx [DllImport(Lib.ComCtl32, SetLastError = false, ExactSpelling = true)] @@ -919,6 +980,17 @@ namespace Vanara.PInvoke [PInvokeData("Commctrl.h", MSDNShortId = "bb775229")] public static extern HRESULT ImageList_WriteEx(IntPtr himl, ILP dwFlags, IStream pstm); + /// Prepares the index of an overlay mask so that the ImageList_Draw function can use it. + /// + /// Type: UINT + /// An index of an overlay mask. + /// + /// No return value. + // UINT INDEXTOOVERLAYMASK( UINT iOverlay); + // https://msdn.microsoft.com/en-us/library/windows/desktop/bb761408(v=vs.85).aspx + [PInvokeData("Commctrl.h", MSDNShortId = "bb761408")] + public static int INDEXTOOVERLAYMASK(int iOverlay) => iOverlay << 8; + /// Contains information about an image in an image list. This structure is used with the IImageList::GetImageInfo function. [PInvokeData("Commctrl.h", MSDNShortId = "bb761393")] [StructLayout(LayoutKind.Sequential)] @@ -967,43 +1039,11 @@ namespace Vanara.PInvoke public int cStandby; } + /// Image list class. [ComImport, Guid("7C476BA2-02B1-48f4-8048-B24619DDC058"), ClassInterface(ClassInterfaceType.None)] [PInvokeData("CommonControls.h")] public class CImageList { } - /// Draw color with options for method. - [StructLayout(LayoutKind.Sequential)] - public struct ImageListDrawColor - { - private uint value; - - /// Initializes a new instance of the struct. - /// The color. - public ImageListDrawColor(Color color) { value = (uint)ColorTranslator.ToWin32(color); } - - /// Initializes a new instance of the struct. - /// The color. - public ImageListDrawColor(COLORREF color) { value = color; } - - private ImageListDrawColor(uint val) { value = val; } - - /// Static reference for CLR_NONE representing no color. - public static ImageListDrawColor None = new ImageListDrawColor(CLR_NONE); - - /// Static reference for CLR_DEFAULT representing the default color. - public static ImageListDrawColor Default = new ImageListDrawColor(CLR_DEFAULT); - - /// Performs an implicit conversion from to . - /// The ImageListDrawColor instance. - /// The result of the conversion. - public static implicit operator uint(ImageListDrawColor c) => c.value; - - /// Performs an implicit conversion from to . - /// The color. - /// The result of the conversion. - public static implicit operator ImageListDrawColor(Color c) => new ImageListDrawColor(c); - } - /// Contains information about an image list draw operation and is used with the IImageList::Draw function. // typedef struct _IMAGELISTDRAWPARAMS { DWORD cbSize; HIMAGELIST himl; int i; HDC hdcDst; int x; int y; int cx; int cy; int xBitmap; int yBitmap; COLORREF rgbBk; COLORREF rgbFg; UINT fStyle; DWORD dwRop; DWORD fState; DWORD Frame; DWORD crEffect;} IMAGELISTDRAWPARAMS; // https://msdn.microsoft.com/en-us/library/windows/desktop/bb761395(v=vs.85).aspx @@ -1044,12 +1084,12 @@ namespace Vanara.PInvoke /// public int yBitmap; /// The image background color. This parameter can be an application-defined RGB value or or . - public ImageListDrawColor rgbBk; + public COLORREF rgbBk; /// /// The image foreground color. This member is used only if fStyle includes the ILD_BLEND25 or ILD_BLEND50 flag. This parameter can be an /// application-defined RGB value or or . /// - public ImageListDrawColor rgbFg; + public COLORREF rgbFg; /// /// A flag specifying the drawing style and, optionally, the overlay image. See the comments section at the end of this topic for information on the /// overlay image. This member can contain one or more image list drawing flags. @@ -1075,7 +1115,7 @@ namespace Vanara.PInvoke /// public uint Frame; /// A color used for the glow and shadow effects. You must use comctl32.dll version 6 to use this member. See the Remarks. - public ImageListDrawColor crEffect; + public COLORREF crEffect; /// Initializes a new instance of the class. public IMAGELISTDRAWPARAMS() { cbSize = Marshal.SizeOf(this); } @@ -1086,7 +1126,7 @@ namespace Vanara.PInvoke /// The zero-based index of the image to be drawn. /// The image background color. /// A flag specifying the drawing style and, optionally, the overlay image. - public IMAGELISTDRAWPARAMS(IntPtr hdcDst, RECT bounds, int index, ImageListDrawColor bgColor, IMAGELISTDRAWFLAGS style = IMAGELISTDRAWFLAGS.ILD_NORMAL) : this() + public IMAGELISTDRAWPARAMS(IntPtr hdcDst, RECT bounds, int index, COLORREF bgColor, IMAGELISTDRAWFLAGS style = IMAGELISTDRAWFLAGS.ILD_NORMAL) : this() { i = index; this.hdcDst = hdcDst; @@ -1117,18 +1157,7 @@ namespace Vanara.PInvoke /// Gets the IImageList interface for this handle. /// The interface. - public IImageList Interface - { - get - { - if (iImageList == null) - { - HIMAGELIST_QueryInterface(handle, typeof(IImageList).GUID, out var ppv).ThrowIfFailed(); - iImageList = (IImageList)ppv; - } - return iImageList; - } - } + public IImageList Interface => iImageList ?? (iImageList = HIMAGELIST_QueryInterface(handle)); } } } \ No newline at end of file diff --git a/UnitTests/UI/Controls/ImageListTests.cs b/UnitTests/UI/Controls/ImageListTests.cs new file mode 100644 index 00000000..766074e8 --- /dev/null +++ b/UnitTests/UI/Controls/ImageListTests.cs @@ -0,0 +1,77 @@ +using NUnit.Framework; +using System; +using System.Windows.Forms; +using Vanara.Extensions; +using static Vanara.PInvoke.ComCtl32; +using System.Drawing; + +namespace Vanara.Windows.Forms.Tests +{ + [TestFixture] + public class ImageListTests + { + [Test] + public void GetIImageListTest() + { + var il = new ImageList(); + var iil = il.GetIImageList(); + Assert.That(iil, Is.Not.Null); + Assert.That(iil.GetImageCount(), Is.Zero); + } + + [Test] + public void ImageListFromHandleTest() + { + var himl = ImageList_Create(32, 32, ILC.ILC_COLOR32 | ILC.ILC_MASK, 8, 8); + himl.Interface.Add((Image.FromFile(@"C:\Temp\TriggerTypeLogon.png", true) as Bitmap).GetHbitmap(), IntPtr.Zero); + Assert.That(himl.IsInvalid, Is.False); + var il2 = ImageListExtension.ImageListFromHandle(himl); + Assert.That(il2.HandleCreated, Is.True); + Assert.That(il2.ColorDepth, Is.EqualTo(ColorDepth.Depth32Bit)); + Assert.That(il2.ImageSize, Is.EqualTo(new Size(32, 32))); + Assert.That(il2.Images.Count, Is.EqualTo(1)); + } + + [Test] + public void AddOverlayAndDrawTest() + { + var il = new ImageList() { ColorDepth = ColorDepth.Depth32Bit, ImageSize = new Size(32, 32) }; + il.Images.Add(new Icon(@"C:\Temp\help.ico")); + Assert.That(il.Images.Count, Is.EqualTo(1)); + var ovIdx = il.AddOverlay(new Bitmap(@"C:\Temp\overlay32.png"), Color.Transparent); + Assert.That(il.Images.Count, Is.EqualTo(2)); + var bmp = new Bitmap(32, 32, System.Drawing.Imaging.PixelFormat.Format32bppArgb); + using (var g = Graphics.FromImage(bmp)) + il.Draw(g, new Rectangle(0, 0, 32, 32), 0, Color.Transparent, PInvoke.Gdi32.COLORREF.None, overlayImageIndex: ovIdx); + ShowImage(bmp); + } + + [Test] + public void MergeTest() + { + var il = new ImageList() { ColorDepth = ColorDepth.Depth32Bit, ImageSize = new Size(32, 32) }; + il.SetBackgroundColor(Color.Transparent); + var img1 = Image.FromFile(@"C:\Temp\tsnew32.png", true); + il.Images.Add(img1, Color.Transparent); + ShowImage(il.Images[0]); + var il2 = new ImageList() { ColorDepth = ColorDepth.Depth32Bit, ImageSize = new Size(32, 32) }; + il2.SetBackgroundColor(Color.Transparent); + var img2 = Image.FromFile(@"C:\Temp\TriggerTypeLogon.png", true); + il2.Images.Add(img2, Color.Transparent); + ShowImage(il2.Images[0]); + var ico = il.MergeImage(0, il2, 0); + ShowImage(ico.ToAlphaBitmap()); + } + + private static void ShowImage(Image img, [System.Runtime.CompilerServices.CallerMemberName] string title = "") + { + Application.EnableVisualStyles(); + var frm = new Form { Size = new Size(300, 300), FormBorderStyle = FormBorderStyle.Sizable, Text = title }; + var pbox = new PictureBox { Dock = DockStyle.Fill, Image = img, SizeMode = PictureBoxSizeMode.CenterImage }; + pbox.DoubleClick += (s, e) => pbox.SizeMode = PictureBoxSizeMode.Zoom; + frm.KeyDown += (s, e) => frm.BackColor = Color.Red; + frm.Controls.Add(pbox); + frm.ShowDialog(); + } + } +} \ No newline at end of file diff --git a/UnitTests/UnitTests.csproj b/UnitTests/UnitTests.csproj index cff27aa3..0834eb9b 100644 --- a/UnitTests/UnitTests.csproj +++ b/UnitTests/UnitTests.csproj @@ -119,6 +119,7 @@ + diff --git a/WIndows.Forms/Extensions/ImageListExtension.cs b/WIndows.Forms/Extensions/ImageListExtension.cs index 1ce3c44e..3fd8f3db 100644 --- a/WIndows.Forms/Extensions/ImageListExtension.cs +++ b/WIndows.Forms/Extensions/ImageListExtension.cs @@ -1,78 +1,18 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Drawing; -using System.Runtime.InteropServices; using System.Windows.Forms; 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>(); - /// - /// 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, ImageListDrawColor bgColor, ImageListDrawColor 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 SafeDCHandle(g)) - { - var p = new IMAGELISTDRAWPARAMS(hg.DangerousGetHandle(), bounds, index, bgColor, style | (IMAGELISTDRAWFLAGS)(overlayImageIndex << 8)) { rgbFg = fgColor }; - imageList.GetIImageList().Draw(p); - } - } - - /// Gets an object for the instance. - /// The image list. - /// An object. - public static IImageList GetIImageList(this ImageList imageList) => new SafeImageListHandle(imageList.Handle, false).Interface; - - private static int GetOverlayCount(this ImageList imageList) => imageListOverlays.TryGetValue(imageList, out List vals) ? vals.Count : 0; - - /// - /// 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 List 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; - } - - /// - /// Adds the specified image to the ImageList as an overlay, using the specified color to determine transparency. - /// + /// 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. @@ -84,7 +24,127 @@ namespace Vanara.Extensions return SetImageIndexAsOverlay(imageList, idx); } - private static void imageList_RecreateHandle(object sender, EventArgs e) + /// 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 SafeDCHandle(g)) + { + var p = new IMAGELISTDRAWPARAMS(hg.DangerousGetHandle(), 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 SafeImageListHandle(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 HIMAGELIST value. + /// An ImageList instance hosting the supplied handle. + /// + public static ImageList ImageListFromHandle(IntPtr himl) + { + // Duplicate handle and get IImageList for it + var dhiml = ImageList_Duplicate(himl); + var iil = HIMAGELIST_QueryInterface(dhiml); + // 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 }, 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(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(Point)) + { + var il3 = il1.GetIImageList().Merge(idx1, il2.GetIImageList(), idx2, offset.X, offset.Y, typeof(IImageList).GUID); + var hico = il3.GetIcon(0, IMAGELISTDRAWFLAGS.ILD_TRANSPARENT | IMAGELISTDRAWFLAGS.ILD_PRESERVEALPHA); + return Icon.FromHandle(hico); + } + + /// 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 List 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 List vals) ? vals.Count : 0; + + private static void ImageList_RecreateHandle(object sender, EventArgs e) { if (!(sender is ImageList il) || !imageListOverlays.TryGetValue(il, out List vals)) return; var iil = il.GetIImageList();