Everything that I have been able to find so far on creating buttons and the like all use a hard coded path for the associated image and largeImage properties of the button. Usually using something along the lines of:
button.Image = new BitmapImage(new Uri("C:\\MyTools\\Icons\\MyIcon.ico"))
I would like to embed my icons (as png probably) as resources within my dll. How do I reference the button image from that location and apply it to my button?
Thanks in advance for any assistance.
-G
Solved! Go to Solution.
Solved by Gary_J_Orr. Go to Solution.
Solved by Mustafa.Salaheldin. Go to Solution.
Solved by L.Maas. Go to Solution.
I am working on the same (VB.Net). The following seems to work (not fully tested).
On the location of the images I have something like this
pushButton.LargeImage = BitmapToImageSource(Global.RevitTest.My.Resources.Update_32) pushButton.Image = BitmapToImageSource(Global.RevitTest.My.Resources.Update_16)
'RevitTest' is the namespace of my add-in. I have images (png) in my Resources. In this case i have two images Update-32.png and Update-16.png
The name in the resources have been automatically named as update_32 and Update_16.
As the images are bitmaps and the button require an 'imagesource' I need some conversion.
Private Function BitmapToImageSource(bitmap As Bitmap) As BitmapImage Using memory As New MemoryStream() bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Png) memory.Position = 0 Dim bitmapimage As New BitmapImage() bitmapimage.BeginInit() bitmapimage.StreamSource = memory bitmapimage.CacheOption = BitmapCacheOption.OnLoad bitmapimage.EndInit() Return bitmapimage End Using End Function
Found this function HERE.
Louis
Please mention Revit version, especially when uploading Revit files.
Please find attached a sample project that demonstrate how to do the task. You have to take care of two things:
1 - the format of the Icons images.
It is so important to adjust the image decoder in the following function:
private System.Windows.Media.ImageSource BmpImageSource(string embeddedPath) { Stream stream = this.GetType().Assembly.GetManifestResourceStream(embeddedPath); var decoder = new System.Windows.Media.Imaging.BmpBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); return decoder.Frames[0]; }
i.e. for PNG you can use:
private System.Windows.Media.ImageSource PngImageSource(string embeddedPath) { Stream stream = this.GetType().Assembly.GetManifestResourceStream(embeddedPath); var decoder = new System.Windows.Media.Imaging.PngBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); return decoder.Frames[0]; }
2 - the properties of the Icons images.
Right click on the icons images in the Solution Explorer and select Properties. From Properties make sure that "Build Action" is set to "Embedded Resource"
In the UI elements just set the path starting from the Namespace to the icon path in your project for the image properties.
Dear Gary, Louis and Mustafa,
Thank you for your wonderful question and answers.
The Building Coder has implemented and documented this kind of thing several times over as well, e.g.:
http://thebuildingcoder.typepad.com/blog/2009/11/ribbon-embed-image.html
http://thebuildingcoder.typepad.com/blog/2012/06/retrieve-embedded-resource-image.html
One of the more recent implementation using an embedded icon resource is the ExportCncFab application:
https://github.com/jeremytammik/ExportCncFab
https://github.com/jeremytammik/ExportCncFab/blob/master/ExportCncFab/App.cs
I hope this helps.
Gary, please let us know what you end up choosing, and why, and what you learned evaluating your various options.
Thank you!
Best regards,
Jeremy
Jeremy,
I'm trying to dig through Louis and Mustafa's suggested solutions until I understand what my options are. Both seem good (although I can't convert Mustafa's function to VB and the "this" reference is causing me problems) but with largely different approaches so I'm playing around a bit (so much so that I broke my puter and had to spend a couple of hours with our IT folks to get back in the game ;-).
And now, of course, I have your links to wade through as well... I miss the days before the "easy Internet" when you only had one comprehensive resource to have to deal with instead of having several different places to have to try to find the answer to one (of many) questions 🙂
I'm currently trying to figure out which option allows for a better Select Case (Yes, I'm still VB... just can't make the switch to C# on top of learning the ins and outs of the API). My hope is to pass the name of an image that is embedded by using the actual file name (including extension), or by passing in the named resource then asking it what kind of image it is... then let the function determine the correct decoder to use then output the usable image resource.
I'll post back when I get something working (assuming that I can stay on the project long enough at a stretch to get it completed).
-G
I also tried the code from Mustafa in VB.NET. This should work
Private Function PNGtoImageSource(embeddedPath As String) As System.Windows.Media.ImageSource Dim stream As Stream = Me.[GetType]().Assembly.GetManifestResourceStream(embeddedPath) Dim decoder = New System.Windows.Media.Imaging.BmpBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.[Default]) Return decoder.Frames(0) End Function
and then you can call it like
pushButton1.LargeImage = PNGtoImageSource("RevitTest.Resources.Update-32.png") pushButton1.Image = PNGtoImageSource("RevitTest.Resources.Update-16.png")
I decided to use the option I proposed as I could feed the image with intellisense directly from resources (like My.Resources.Update_32), not needing a string. So I get the (intellisense) options for the items available in Resources. So getting immediate feedback when I try to use a non-exisiting resource.
Not sure/tested if the conversion code from Mustafa is more efficient.
Louis
Please mention Revit version, especially when uploading Revit files.
<<slaps forehead>> "Me"... for some reason I was stuck on the "My" keyword, even knowing that wasn't correct...
I was beginning to believe that I was just being stupid... missing some obvious thing, or misspelling something, or otherwise having fumble fingers...
So I copied and pasted the code suggestions and still had issues... the issues were always in not obtaining the stream from the passed in image name string in the addImage function...
Mustafa's (and Louis's VB conversion of it) call the function by supplying a string consisting of:
Namespace.Resources.Imagename.ext
Then I got to digging around in Jeremy's posted examples and realized a difference... I took out the "Resources." in calls to the function and everything works as it should:
Namespace.Imagename.ext
so (in Louis' VB conversion):
pushButton1.LargeImage = PNGtoImageSource("RevitTest.Resources.Update-32.png")
pushButton1.Image = PNGtoImageSource("RevitTest.Resources.Update-16.png")
becomes:
pushButton1.LargeImage = PNGtoImageSource("RevitTest.Update-32.png")
pushButton1.Image = PNGtoImageSource("RevitTest.Update-16.png")
Thanks guys... I don't know whom to credit the solution to since it was bits and pieces from all of you... but I do have a solution and it was the 3 of you that got me there. Here's the function that I ended up with:
Private Function addImage(imageName As String) As ImageSource 'required imports: 'System.Windows.Media 'System.IO Dim imageStream As Stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(imageName) Dim imgDecoder Select Case LCase(Path.GetExtension(imageName)) Case Is = ".png" imgDecoder = New Imaging.PngBitmapDecoder(imageStream, Imaging.BitmapCreateOptions.PreservePixelFormat, Imaging.BitmapCacheOption.Default) Case Is = ".bmp" imgDecoder = New Imaging.BmpBitmapDecoder(imageStream, Imaging.BitmapCreateOptions.PreservePixelFormat, Imaging.BitmapCacheOption.Default) Case Is = ".jpg" imgDecoder = New Imaging.JpegBitmapDecoder(imageStream, Imaging.BitmapCreateOptions.PreservePixelFormat, Imaging.BitmapCacheOption.Default) Case Else 'icons imgDecoder = New Imaging.IconBitmapDecoder(imageStream, Imaging.BitmapCreateOptions.PreservePixelFormat, Imaging.BitmapCacheOption.Default) End Select addImage = imgDecoder.Frames(0) End Function
Thanks for getting back with your solution. Pleas note that you can mark multiple posts as a solution. It is always good to mark at least one post as solution. This will make it easier for other user to find what they are looking for.
Louis
Please mention Revit version, especially when uploading Revit files.
With Revit 2019 this now returns a null (GetManifestResourceStream). (From builds that worked fine in 2017 & 2018).
Does anyone know why this is?
Dave
Envisage
I'm having the exact same problem in 2019, the stream always comes back as null. Any one have some ideas on how to resolve in Revit 2019?
Create a minimal reproducible case to prove that the problem exists.
All you need is the external command and application skeleton code and a single embedded icon resource.
Chances are, if you make it simple enough, the problem will go away by itself.
If it doesn't, people can take a look for you.
If you provide no sample, they cannot.
Here's a simple version of what I'm doing. Image is stored in a Resources folder. I've tried this with and without .Resources in the path but myStream always comes up null.
using System.IO; using System.Reflection; using System.Windows.Media; using System.Windows.Media.Imaging; using Autodesk.Revit.UI; namespace RevitAddin2 { class App : IExternalApplication { private ImageSource PNGtoImageSource(string embeddedPath) { Stream myStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(embeddedPath); PngBitmapDecoder decoder = new PngBitmapDecoder(myStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); return decoder.Frames[0]; } public Result OnStartup(UIControlledApplication application) { application.CreateRibbonTab("RibbonTab"); string path = Assembly.GetExecutingAssembly().Location; RibbonPanel panelTools = application.CreateRibbonPanel("RibbonTab", "Tools"); PushButtonData buttonLinkRoomAreaSum = new PushButtonData("Button6", "Get Linked Total Area", path, "RevitAddin2.LinkRoomAreaSum"); SplitButtonData splitButtonArea = new SplitButtonData("SButton1", "SplitArea"); SplitButton sbAreas = panelTools.AddItem(splitButtonArea) as SplitButton; sbAreas.IsSynchronizedWithCurrentItem = true; sbAreas.AddPushButton(buttonLinkRoomAreaSum).LargeImage = PNGtoImageSource("RevitAddin2.FileName.bmp"); return Result.Succeeded; } public Result OnShutdown(UIControlledApplication a) { return Result.Succeeded; } } }
The solution is so simple:
In this line:
sbAreas.AddPushButton(buttonLinkRoomAreaSum).LargeImage = PNGtoImageSource("RevitAddin2.FileName.bmp");
You are providing bmp format while it was supposed to receive PNG format. Either change the image format or the source type.
You can use:
private System.Windows.Media.ImageSource BmpImageSource(string embeddedPath) { Stream stream = this.GetType().Assembly.GetManifestResourceStream(embeddedPath); var decoder = new System.Windows.Media.Imaging.BmpBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); return decoder.Frames[0]; }
If this reply solved your issue please mark it as an answer.
My bad! That was from me trying out both a .bmp and a .png, just forgot to change it back to .png before adding my code. It still isn't working with a .png.
Three things to do when you are reading from embedded resource:
In the example shown below it would be something like this: Namespace.Resources.CreateAreaSeparatioLines16x16.png
Then from the properties pane make sure you set the properties as shown:
Can't find what you're looking for? Ask the community or share your knowledge.