Updated color references to COLORREF struct, added XML comments, added Get/SetIconSize extension methods, fixed HIMAGELIST_QueryInterface, added INDEXTOOVERLAYMASK macro, removed ImageListDrawColor

pull/10/head
David Hall 2018-06-08 11:25:08 -06:00
parent 685cd231ac
commit 9390e08017
4 changed files with 286 additions and 119 deletions

View File

@ -114,6 +114,7 @@ namespace Vanara.PInvoke
ILR_SCALE_ASPECTRATIO = 0x0100,
}
/// <summary>Flags used when copying image lists.</summary>
[PInvokeData("Commctrl.h", MSDNShortId = "bb775230")]
public enum IMAGELISTCOPYFLAG
{
@ -174,6 +175,7 @@ namespace Vanara.PInvoke
ILD_ASYNC = 0X00008000
}
/// <summary>Item flags.</summary>
[PInvokeData("Commctrl.h", MSDNShortId = "bb761486")]
public enum IMAGELISTITEMFLAG
{
@ -251,7 +253,7 @@ namespace Vanara.PInvoke
/// is set to 1.
/// </param>
/// <returns>A pointer to an int that contains the index of the first new image when it returns, if successful, or -1 otherwise.</returns>
int AddMasked(IntPtr hbmImage, uint crMask);
int AddMasked(IntPtr hbmImage, COLORREF crMask);
/// <summary>Draws an image list item in the specified device context.</summary>
/// <param name="pimldp">A pointer to an IMAGELISTDRAWPARAMS structure that contains the drawing parameters.</param>
@ -330,11 +332,11 @@ namespace Vanara.PInvoke
/// </summary>
/// <param name="clrBk">The background color to set. If this parameter is set to CLR_NONE, then images draw transparently using the mask.</param>
/// <param name="pclr">A pointer to a COLORREF that contains the previous background color on return if successful, or CLR_NONE otherwise.</param>
void SetBkColor(uint clrBk, ref uint pclr);
void SetBkColor(COLORREF clrBk, out COLORREF pclr);
/// <summary>Gets the current background color for an image list.</summary>
/// <returns>A pointer to a COLORREF that contains the background color when the method returns.</returns>
uint GetBkColor();
COLORREF GetBkColor();
/// <summary>Begins dragging an image.</summary>
/// <param name="iTrack">A value of type int that contains the index of the image to drag.</param>
@ -408,6 +410,22 @@ namespace Vanara.PInvoke
int GetOverlayImage(int iOverlay);
}
/// <summary>Gets the dimensions of images in an image list. All images in an image list have the same dimensions.</summary>
/// <param name="il">The <see cref="IImageList"/> instance.</param>
/// <returns>The size of images.</returns>
public static Size GetIconSize(this IImageList il)
{
il.GetIconSize(out int cx, out int cy);
return new Size(cx, cy);
}
/// <summary>Sets the dimensions of images in an image list and removes all images from the list.</summary>
/// <param name="il">The <see cref="IImageList"/> instance.</param>
/// <param name="size">
/// 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.
/// </param>
public static void SetIconSize(this IImageList il, Size size) => il.SetIconSize(size.Width, size.Height);
/// <summary>Extends IImageList by providing additional methods for manipulating and interacting with image lists.</summary>
[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.
/// </param>
/// <returns>A pointer to an int that contains the index of the first new image when it returns, if successful, or -1 otherwise.</returns>
new int AddMasked(IntPtr hbmImage, uint crMask);
new int AddMasked(IntPtr hbmImage, COLORREF crMask);
/// <summary>Draws an image list item in the specified device context.</summary>
/// <param name="pimldp">A pointer to an IMAGELISTDRAWPARAMS structure that contains the drawing parameters.</param>
@ -532,11 +550,12 @@ namespace Vanara.PInvoke
/// </summary>
/// <param name="clrBk">The background color to set. If this parameter is set to CLR_NONE, then images draw transparently using the mask.</param>
/// <param name="pclr">A pointer to a COLORREF that contains the previous background color on return if successful, or CLR_NONE otherwise.</param>
new void SetBkColor(uint clrBk, ref uint pclr);
new void SetBkColor(COLORREF clrBk, out COLORREF pclr);
/// <summary>Gets the current background color for an image list.</summary>
/// <returns>A pointer to a COLORREF that contains the background color when the method returns.</returns>
new uint GetBkColor();
new COLORREF GetBkColor();
/// <summary>Begins dragging an image.</summary>
/// <param name="iTrack">A value of type int that contains the index of the image to drag.</param>
@ -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);
/// <summary>Get an image list interface from an image list handle.</summary>
/// <param name="himl">
/// <para>Type: <c>HIMAGELIST</c></para>
/// <para>A handle to the image list to query.</para>
/// </param>
/// <returns>
/// When this method returns, contains the interface pointer requested. This is normally <c>IImageList2</c>, which provides the <c>Initialize</c> method.
/// </returns>
[PInvokeData("Commctrl.h")]
public static TIntf HIMAGELIST_QueryInterface<TIntf>(IntPtr himl)
{
if (himl == IntPtr.Zero) throw new ArgumentNullException(nameof(himl));
return (TIntf)Marshal.GetTypedObjectForIUnknown(himl, typeof(TIntf));
}
/// <summary>Get an image list handle from an image list interface.</summary>
/// <param name="himl">
/// <para>Type: <c>HIMAGELIST</c></para>
@ -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);
/// <summary>Creates a new image list.</summary><param name="cx"><para>Type: <c>int</c></para><para>The width, in pixels, of each image.</para></param><param name="cy"><para>Type: <c>int</c></para><para>The height, in pixels, of each image.</para></param><param name="flags"><para>Type: <c><c>UINT</c></c></para><para>A set of bit flags that specify the type of image list to create. This parameter can be a combination of the <c>Image List Creation Flags</c>.</para></param><param name="cInitial"><para>Type: <c>int</c></para><para>The number of images that the image list initially contains.</para></param><param name="cGrow"><para>Type: <c>int</c></para><para>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.</para></param><returns><para>Type: <c>HIMAGELIST</c></para><para>Returns the handle to the image list if successful, or <c>NULL</c> otherwise.</para></returns>
/// <summary>Creates a new image list.</summary>
/// <param name="cx">
/// <para>Type: <c>int</c></para>
/// <para>The width, in pixels, of each image.</para>
/// </param>
/// <param name="cy">
/// <para>Type: <c>int</c></para>
/// <para>The height, in pixels, of each image.</para>
/// </param>
/// <param name="flags">
/// <para>Type: <c><c>UINT</c></c></para>
/// <para>A set of bit flags that specify the type of image list to create. This parameter can be a combination of the <c>Image List Creation Flags</c>.</para>
/// </param>
/// <param name="cInitial">
/// <para>Type: <c>int</c></para>
/// <para>The number of images that the image list initially contains.</para>
/// </param>
/// <param name="cGrow">
/// <para>Type: <c>int</c></para>
/// <para>
/// 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.
/// </para>
/// </param>
/// <returns>
/// <para>Type: <c>HIMAGELIST</c></para>
/// <para>Returns the handle to the image list if successful, or <c>NULL</c> otherwise.</para>
/// </returns>
// 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);
/// <summary>Prepares the index of an overlay mask so that the <c>ImageList_Draw</c> function can use it.</summary>
/// <param name="iOverlay">
/// <para>Type: <c><c>UINT</c></c></para>
/// <para>An index of an overlay mask.</para>
/// </param>
/// <returns>No return value.</returns>
// 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;
/// <summary>Contains information about an image in an image list. This structure is used with the IImageList::GetImageInfo function.</summary>
[PInvokeData("Commctrl.h", MSDNShortId = "bb761393")]
[StructLayout(LayoutKind.Sequential)]
@ -967,43 +1039,11 @@ namespace Vanara.PInvoke
public int cStandby;
}
/// <summary>Image list class.</summary>
[ComImport, Guid("7C476BA2-02B1-48f4-8048-B24619DDC058"), ClassInterface(ClassInterfaceType.None)]
[PInvokeData("CommonControls.h")]
public class CImageList { }
/// <summary>Draw color with options for <see cref="Draw"/> method.</summary>
[StructLayout(LayoutKind.Sequential)]
public struct ImageListDrawColor
{
private uint value;
/// <summary>Initializes a new instance of the <see cref="ImageListDrawColor"/> struct.</summary>
/// <param name="color">The color.</param>
public ImageListDrawColor(Color color) { value = (uint)ColorTranslator.ToWin32(color); }
/// <summary>Initializes a new instance of the <see cref="ImageListDrawColor"/> struct.</summary>
/// <param name="color">The color.</param>
public ImageListDrawColor(COLORREF color) { value = color; }
private ImageListDrawColor(uint val) { value = val; }
/// <summary>Static reference for CLR_NONE representing no color.</summary>
public static ImageListDrawColor None = new ImageListDrawColor(CLR_NONE);
/// <summary>Static reference for CLR_DEFAULT representing the default color.</summary>
public static ImageListDrawColor Default = new ImageListDrawColor(CLR_DEFAULT);
/// <summary>Performs an implicit conversion from <see cref="ImageListDrawColor"/> to <see cref="uint"/>.</summary>
/// <param name="c">The ImageListDrawColor instance.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator uint(ImageListDrawColor c) => c.value;
/// <summary>Performs an implicit conversion from <see cref="Color"/> to <see cref="ImageListDrawColor"/>.</summary>
/// <param name="c">The color.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator ImageListDrawColor(Color c) => new ImageListDrawColor(c);
}
/// <summary>Contains information about an image list draw operation and is used with the <c>IImageList::Draw</c> function.</summary>
// 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
/// </summary>
public int yBitmap;
/// <summary>The image background color. This parameter can be an application-defined RGB value or <see cref="CLR_DEFAULT"/> or <see cref="CLR_NONE"/>.</summary>
public ImageListDrawColor rgbBk;
public COLORREF rgbBk;
/// <summary>
/// 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 <see cref="CLR_DEFAULT"/> or <see cref="CLR_NONE"/>.
/// </summary>
public ImageListDrawColor rgbFg;
public COLORREF rgbFg;
/// <summary>
/// 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
/// </summary>
public uint Frame;
/// <summary>A color used for the glow and shadow effects. You must use comctl32.dll version 6 to use this member. See the Remarks.</summary>
public ImageListDrawColor crEffect;
public COLORREF crEffect;
/// <summary>Initializes a new instance of the <see cref="IMAGELISTDRAWPARAMS"/> class.</summary>
public IMAGELISTDRAWPARAMS() { cbSize = Marshal.SizeOf(this); }
@ -1086,7 +1126,7 @@ namespace Vanara.PInvoke
/// <param name="index">The zero-based index of the image to be drawn.</param>
/// <param name="bgColor">The image background color.</param>
/// <param name="style">A flag specifying the drawing style and, optionally, the overlay image.</param>
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
/// <summary>Gets the IImageList interface for this handle.</summary>
/// <value>The interface.</value>
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<IImageList>(handle));
}
}
}

View File

@ -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();
}
}
}

View File

@ -119,6 +119,7 @@
<Compile Include="Shell\ShellItemPropStoreTests.cs" />
<Compile Include="System\BackgroundCopyTests.cs" />
<Compile Include="System\VirtualDiskTests.cs" />
<Compile Include="UI\Controls\ImageListTests.cs" />
<Compile Include="UI\Controls\CredentialsDialogTests.cs" />
</ItemGroup>
<ItemGroup>

View File

@ -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
{
/// <summary>Extension methods for <see cref="ImageList"/>.</summary>
public static partial class ImageListExtension
{
private static Dictionary<ImageList, List<int>> imageListOverlays = new Dictionary<ImageList, List<int>>();
/// <summary>
/// Draws the image indicated by the given index on the specified <see cref="Graphics"/> at the specified location.
/// </summary>
/// <param name="imageList">The image list.</param>
/// <param name="g">The <see cref="Graphics"/> to draw on.</param>
/// <param name="bounds">The bounds in which to draw the image. Set width and height to 0 to draw image at full size.</param>
/// <param name="index">The index of the image in the ImageList to draw.</param>
/// <param name="bgColor">The background color of the image. This parameter can be a <see cref="Color"/> value or <see cref="ImageListDrawColor.None"/>
/// so the image is drawn transparently or <see cref="ImageListDrawColor.Default"/> so the image is drawn using the background color of the image list.</param>
/// <param name="fgColor">The foreground color of the image. This parameter can be a <see cref="Color"/> value or <see cref="ImageListDrawColor.None"/>
/// so the image is blended with the color of the destination device context or <see cref="ImageListDrawColor.Default"/> so the image is drawn using the system highlight color as the foreground color.</param>
/// <param name="style">The drawing style.</param>
/// <param name="overlayImageIndex">Optional index of an overlay image.</param>
/// <exception cref="System.ComponentModel.Win32Exception">Unable to draw the image with defined parameters.</exception>
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);
}
}
/// <summary>Gets an <see cref="IImageList"/> object for the <see cref="ImageList"/> instance.</summary>
/// <param name="imageList">The image list.</param>
/// <returns>An <see cref="IImageList"/> object.</returns>
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<int> vals) ? vals.Count : 0;
/// <summary>
/// Assigns the image at the specified index as an overlay and returns is overlay index.
/// </summary>
/// <param name="imageList">The image list.</param>
/// <param name="imageIndex">Index of the image within the ImageList.</param>
/// <returns>The 1 based index of the overlay.</returns>
/// <exception cref="System.ArgumentOutOfRangeException">The imageIndex is not in the current list.</exception>
/// <exception cref="System.ComponentModel.Win32Exception">The image cannot be added as an overlay.</exception>
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<int> vals))
{
imageList.RecreateHandle += imageList_RecreateHandle;
imageListOverlays.Add(imageList, vals = new List<int>(15));
}
vals.Add(imageIndex);
var overlayIndex = vals.Count;
imageList.GetIImageList().SetOverlayImage(imageIndex, overlayIndex);
return overlayIndex;
}
/// <summary>
/// Adds the specified image to the ImageList as an overlay, using the specified color to determine transparency.
/// </summary>
/// <summary>Adds the specified image to the ImageList as an overlay, using the specified color to determine transparency.</summary>
/// <param name="imageList">The image list.</param>
/// <param name="value">A Bitmap of the image to add to the list.</param>
/// <param name="transparentColor">The color to use as the transparent color within the Bitmap.</param>
@ -84,7 +24,127 @@ namespace Vanara.Extensions
return SetImageIndexAsOverlay(imageList, idx);
}
private static void imageList_RecreateHandle(object sender, EventArgs e)
/// <summary>Draws the image indicated by the given index on the specified <see cref="Graphics"/> at the specified location.</summary>
/// <param name="imageList">The image list.</param>
/// <param name="g">The <see cref="Graphics"/> to draw on.</param>
/// <param name="bounds">The bounds in which to draw the image. Set width and height to 0 to draw image at full size.</param>
/// <param name="index">The index of the image in the ImageList to draw.</param>
/// <param name="bgColor">
/// The background color of the image. This parameter can be a <see cref="Color"/> value or <see cref="COLORREF.None"/> so the image is drawn
/// transparently or <see cref="COLORREF.Default"/> so the image is drawn using the background color of the image list.
/// </param>
/// <param name="fgColor">
/// The foreground color of the image. This parameter can be a <see cref="Color"/> value or <see cref="COLORREF.None"/> so the image is blended
/// with the color of the destination device context or <see cref="COLORREF.Default"/> so the image is drawn using the system highlight color
/// as the foreground color.
/// </param>
/// <param name="style">The drawing style.</param>
/// <param name="overlayImageIndex">Optional index of an overlay image.</param>
/// <exception cref="System.ComponentModel.Win32Exception">Unable to draw the image with defined parameters.</exception>
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);
}
}
/// <summary>Gets the current background color for an image list.</summary>
/// <param name="imageList">The image list.</param>
/// <returns>The background color.</returns>
public static Color GetBackgroundColor(this ImageList imageList) => imageList.GetIImageList().GetBkColor();
/// <summary>Gets an <see cref="IImageList"/> object for the <see cref="ImageList"/> instance.</summary>
/// <param name="imageList">The image list.</param>
/// <returns>An <see cref="IImageList"/> object.</returns>
public static IImageList GetIImageList(this ImageList imageList) => new SafeImageListHandle(imageList.Handle, false).Interface;
/// <summary>Creates an <see cref="ImageList"/> from a handle.
/// <note type="warning">This is a super hack involving a lot of reflection against internal structures that can change. Use with caution!</note></summary>
/// <param name="himl">The HIMAGELIST value.</param>
/// <returns>An <c>ImageList</c> instance hosting the supplied handle.</returns>
/// <exception cref="PlatformNotSupportedException" />
public static ImageList ImageListFromHandle(IntPtr himl)
{
// Duplicate handle and get IImageList for it
var dhiml = ImageList_Duplicate(himl);
var iil = HIMAGELIST_QueryInterface<IImageList>(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;
}
/// <summary>Creates a new image by combining two existing images.</summary>
/// <param name="il1">The image list that contains the first image.</param>
/// <param name="idx1">The index of the first existing image.</param>
/// <param name="il2">The image list that contains the second image.</param>
/// <param name="idx2">The index of the second existing image.</param>
/// <param name="offset">The offset of the second image relative to the first image.</param>
/// <returns>A merged image.</returns>
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);
}
/// <summary>Gets the current background color for an image list.</summary>
/// <param name="imageList">The image list.</param>
/// <param name="bkColor">The background color to set.</param>
public static void SetBackgroundColor(this ImageList imageList, Color bkColor) =>
imageList.GetIImageList().SetBkColor(bkColor, out var _);
/// <summary>Assigns the image at the specified index as an overlay and returns is overlay index.</summary>
/// <param name="imageList">The image list.</param>
/// <param name="imageIndex">Index of the image within the ImageList.</param>
/// <returns>The 1 based index of the overlay.</returns>
/// <exception cref="System.ArgumentOutOfRangeException">The imageIndex is not in the current list.</exception>
/// <exception cref="System.ComponentModel.Win32Exception">The image cannot be added as an overlay.</exception>
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<int> vals))
{
imageList.RecreateHandle += ImageList_RecreateHandle;
imageListOverlays.Add(imageList, vals = new List<int>(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<int> vals) ? vals.Count : 0;
private static void ImageList_RecreateHandle(object sender, EventArgs e)
{
if (!(sender is ImageList il) || !imageListOverlays.TryGetValue(il, out List<int> vals)) return;
var iil = il.GetIImageList();