Every time I created a ribbon button, I was faced with the task of creating appropriately scaled icons for it to populate the PushButton
large and small image icon properties LargeImage
and Image
.
They seem to expect a 32 x 32 and 16 x 16 icon, respectively.
I finally solved that once and for all by implementing a couple of methods to perform automatic bitmap scaling:
BitmapImage
to Bitmap
Bitmap
to a BitmapSource
Here they are one by one:
/// <summary> /// Convert a BitmapImage to Bitmap /// </summary> static Bitmap BitmapImageToBitmap( BitmapImage bitmapImage ) { //BitmapImage bitmapImage = new BitmapImage( // new Uri("../Images/test.png", UriKind.Relative)); using( MemoryStream outStream = new MemoryStream() ) { BitmapEncoder enc = new BmpBitmapEncoder(); enc.Frames.Add( BitmapFrame.Create( bitmapImage ) ); enc.Save( outStream ); Bitmap bitmap = new Bitmap( outStream ); return new Bitmap( bitmap ); } }
[System.Runtime.InteropServices.DllImport( "gdi32.dll" )] public static extern bool DeleteObject( IntPtr hObject ); /// <summary> /// Convert a Bitmap to a BitmapSource /// </summary> static BitmapSource BitmapToBitmapSource( Bitmap bitmap ) { IntPtr hBitmap = bitmap.GetHbitmap(); BitmapSource retval; try { retval = Imaging.CreateBitmapSourceFromHBitmap( hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions() ); } finally { DeleteObject( hBitmap ); } return retval; }
/// <summary> /// Resize the image to the specified width and height. /// </summary> /// <param name="image">The image to resize.</param> /// <param name="width">The width to resize to.</param> /// <param name="height">The height to resize to.</param> /// <returns>The resized image.</returns> static Bitmap ResizeImage( Image image, int width, int height ) { var destRect = new System.Drawing.Rectangle( 0, 0, width, height ); var destImage = new Bitmap( width, height ); destImage.SetResolution( image.HorizontalResolution, image.VerticalResolution ); using( var g = Graphics.FromImage( destImage ) ) { g.CompositingMode = CompositingMode.SourceCopy; g.CompositingQuality = CompositingQuality.HighQuality; g.InterpolationMode = InterpolationMode.HighQualityBicubic; g.SmoothingMode = SmoothingMode.HighQuality; g.PixelOffsetMode = PixelOffsetMode.HighQuality; using( var wrapMode = new ImageAttributes() ) { wrapMode.SetWrapMode( WrapMode.TileFlipXY ); g.DrawImage( image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode ); } } return destImage; }
ScaledIcon
simply calls the three helper methods defined above to return a scaled version of the input image:
/// <summary> /// Scale down large icon to desired size for Revit /// ribbon button, e.g., 32 x 32 or 16 x 16 /// </summary> static BitmapSource ScaledIcon( BitmapImage large_icon, int w, int h ) { return BitmapToBitmapSource( ResizeImage( BitmapImageToBitmap( large_icon ), w, h ) ); }
Within the external application PopulatePanel
method, simply read the embedded resource icon image and apply ScaledIcon
to it to populate the large and small image properties with appropriately scaled images:
BitmapImage bmi = new BitmapImage( new Uri( "icons/cmdx.png", UriKind.Relative ) ); PushButton pb = p.AddItem( new PushButtonData( "Command", "Command", path, "CmdX" ) ) as PushButton; pb.ToolTip = "Do something fantastic"; pb.LargeImage = ScaledIcon( bmi, 32, 32 ); pb.Image = ScaledIcon( bmi, 16, 16 );