2017-11-27 13:11:20 -05:00
using System ;
using System.Collections.Generic ;
using System.Drawing ;
using System.Windows.Forms ;
2018-06-28 20:02:36 -04:00
using Vanara.PInvoke ;
2017-11-27 13:11:20 -05:00
using static Vanara . PInvoke . ComCtl32 ;
using static Vanara . PInvoke . Gdi32 ;
namespace Vanara.Extensions
{
2018-06-08 13:25:08 -04:00
/// <summary>Extension methods for <see cref="ImageList"/>.</summary>
2017-11-27 13:11:20 -05:00
public static partial class ImageListExtension
{
private static Dictionary < ImageList , List < int > > imageListOverlays = new Dictionary < ImageList , List < int > > ( ) ;
2018-06-08 13:25:08 -04:00
/// <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>
/// <returns>The 1 based index of the overlay.</returns>
/// <exception cref="System.ComponentModel.Win32Exception">The image cannot be added as an overlay.</exception>
public static int AddOverlay ( this ImageList imageList , Image value , Color transparentColor )
{
var idx = imageList . Images . Add ( value , transparentColor ) ;
return SetImageIndexAsOverlay ( imageList , idx ) ;
}
/// <summary>Draws the image indicated by the given index on the specified <see cref="Graphics"/> at the specified location.</summary>
2017-11-27 13:11:20 -05:00
/// <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>
2018-06-08 13:25:08 -04:00
/// <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>
2017-11-27 13:11:20 -05:00
/// <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>
2018-06-08 13:25:08 -04:00
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 )
2017-11-27 13:11:20 -05:00
{
if ( index < 0 | | index > = imageList . Images . Count )
throw new ArgumentOutOfRangeException ( nameof ( index ) ) ;
if ( overlayImageIndex < 0 | | overlayImageIndex > imageList . GetOverlayCount ( ) )
throw new ArgumentOutOfRangeException ( nameof ( overlayImageIndex ) ) ;
2022-01-14 09:32:51 -05:00
using ( var hg = new SafeTempHDC ( g ) )
2018-06-05 13:39:42 -04:00
{
2018-09-22 00:35:34 -04:00
var p = new IMAGELISTDRAWPARAMS ( hg , bounds , index , bgColor , style | ( IMAGELISTDRAWFLAGS ) INDEXTOOVERLAYMASK ( overlayImageIndex ) ) { rgbFg = fgColor } ;
2018-06-05 13:39:42 -04:00
imageList . GetIImageList ( ) . Draw ( p ) ;
}
2017-11-27 13:11:20 -05:00
}
2018-06-08 13:25:08 -04:00
/// <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 ( ) ;
2018-06-05 13:39:42 -04:00
/// <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>
2018-09-22 00:35:34 -04:00
public static IImageList GetIImageList ( this ImageList imageList ) = > new SafeHIMAGELIST ( imageList . Handle , false ) . Interface ;
2018-06-05 13:39:42 -04:00
2018-09-04 14:04:25 -04:00
/// <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 SafeImageListHandle value.</param>
/// <returns>An <c>ImageList</c> instance hosting the supplied handle.</returns>
/// <exception cref="PlatformNotSupportedException" />
2018-09-22 00:35:34 -04:00
public static ImageList ToImageList ( this HIMAGELIST himl )
2018-06-08 13:25:08 -04:00
{
// Duplicate handle and get IImageList for it
var dhiml = ImageList_Duplicate ( himl ) ;
2018-10-26 14:24:07 -04:00
var iil = dhiml . Interface ;
2018-06-08 13:25:08 -04:00
// 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
2018-11-28 09:47:51 -05:00
var nili = nil . Assembly . CreateInstance ( nil . FullName , false , System . Reflection . BindingFlags . Instance | System . Reflection . BindingFlags . NonPublic , null , new object [ ] { dhiml . DangerousGetHandle ( ) } , null , null ) ;
2018-06-08 13:25:08 -04:00
// 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 ) ;
}
2018-10-26 14:24:07 -04:00
var bmp = Image . FromHbitmap ( ( IntPtr ) iil . GetImageInfo ( 0 ) . hbmImage ) ;
2018-06-08 13:25:08 -04:00
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 ;
}
2017-11-27 13:11:20 -05:00
2018-06-08 13:25:08 -04:00
/// <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>
2018-10-26 14:24:07 -04:00
public static Icon MergeImage ( this ImageList il1 , int idx1 , ImageList il2 , int idx2 , Point offset = default )
2018-06-08 13:25:08 -04:00
{
var il3 = il1 . GetIImageList ( ) . Merge ( idx1 , il2 . GetIImageList ( ) , idx2 , offset . X , offset . Y , typeof ( IImageList ) . GUID ) ;
2018-09-22 00:35:34 -04:00
return il3 . GetIcon ( 0 , IMAGELISTDRAWFLAGS . ILD_TRANSPARENT | IMAGELISTDRAWFLAGS . ILD_PRESERVEALPHA ) . ToIcon ( ) ;
2018-06-08 13:25:08 -04:00
}
/// <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>
2017-11-27 13:11:20 -05:00
/// <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 ) ) ;
2018-10-26 14:24:07 -04:00
if ( ! imageListOverlays . TryGetValue ( imageList , out var vals ) )
2017-11-27 13:11:20 -05:00
{
2018-06-08 13:25:08 -04:00
imageList . RecreateHandle + = ImageList_RecreateHandle ;
2017-11-27 13:11:20 -05:00
imageListOverlays . Add ( imageList , vals = new List < int > ( 15 ) ) ;
}
vals . Add ( imageIndex ) ;
var overlayIndex = vals . Count ;
2018-06-05 13:39:42 -04:00
imageList . GetIImageList ( ) . SetOverlayImage ( imageIndex , overlayIndex ) ;
2017-11-27 13:11:20 -05:00
return overlayIndex ;
}
2018-10-26 14:24:07 -04:00
private static int GetOverlayCount ( this ImageList imageList ) = > imageListOverlays . TryGetValue ( imageList , out var vals ) ? vals . Count : 0 ;
2017-11-27 13:11:20 -05:00
2018-06-08 13:25:08 -04:00
private static void ImageList_RecreateHandle ( object sender , EventArgs e )
2017-11-27 13:11:20 -05:00
{
2018-10-26 14:24:07 -04:00
if ( ! ( sender is ImageList il ) | | ! imageListOverlays . TryGetValue ( il , out var vals ) ) return ;
2018-06-05 13:39:42 -04:00
var iil = il . GetIImageList ( ) ;
2017-11-27 13:11:20 -05:00
for ( var i = 0 ; i < vals . Count ; i + + )
2018-06-05 13:39:42 -04:00
iil . SetOverlayImage ( vals [ i ] , i + 1 ) ;
2017-11-27 13:11:20 -05:00
}
}
}