Revit API Forum
Welcome to Autodesk’s Revit API Forums. Share your knowledge, ask questions, and explore popular Revit API topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Ribbon panel button image from embedded resource

20 REPLIES 20
SOLVED
Reply
Message 1 of 21
Gary_J_Orr
15582 Views, 20 Replies

Ribbon panel button image from embedded resource

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

Gary J. Orr
(Your Friendly Neighborhood) CADD/BIM/VDC Applications Manager
http://www.linkedin.com/in/garyorr

aka (current and past user names):
Gary_J_Orr (GOMO Stuff 2008-Present); OrrG (Forum Studio 2005-2008); Gary J. Orr (LHB Inc 2002-2005); Orr, Gary J. (Gossen Livingston 1997-2002)
20 REPLIES 20
Message 2 of 21
L.Maas
in reply to: Gary_J_Orr

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

EESignature

Please mention Revit version, especially when uploading Revit files.

Message 3 of 21

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"

 

Properties.PNG

 

In the UI elements just set the path starting from the Namespace to the icon path in your project for the image properties.

image path.PNG


¯\_(ツ)_/¯
Let it work like a charm.

Mustafa Salaheldin


EESignature




Digital Integration Manager, DuPod

Facebook | Twitter | LinkedIn

Message 4 of 21
jeremytammik
in reply to: Gary_J_Orr

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 Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 5 of 21
Gary_J_Orr
in reply to: jeremytammik

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

Gary J. Orr
(Your Friendly Neighborhood) CADD/BIM/VDC Applications Manager
http://www.linkedin.com/in/garyorr

aka (current and past user names):
Gary_J_Orr (GOMO Stuff 2008-Present); OrrG (Forum Studio 2005-2008); Gary J. Orr (LHB Inc 2002-2005); Orr, Gary J. (Gossen Livingston 1997-2002)
Message 6 of 21

Just replace "this" with "Me" for VB Smiley Happy and good luck.


¯\_(ツ)_/¯
Let it work like a charm.

Mustafa Salaheldin


EESignature




Digital Integration Manager, DuPod

Facebook | Twitter | LinkedIn

Message 7 of 21
L.Maas
in reply to: Gary_J_Orr

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

EESignature

Please mention Revit version, especially when uploading Revit files.

Message 8 of 21

<<slaps forehead>>  "Me"... for some reason I was stuck on the "My" keyword, even knowing that wasn't correct...

Gary J. Orr
(Your Friendly Neighborhood) CADD/BIM/VDC Applications Manager
http://www.linkedin.com/in/garyorr

aka (current and past user names):
Gary_J_Orr (GOMO Stuff 2008-Present); OrrG (Forum Studio 2005-2008); Gary J. Orr (LHB Inc 2002-2005); Orr, Gary J. (Gossen Livingston 1997-2002)
Message 9 of 21
Gary_J_Orr
in reply to: L.Maas

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.pn​g")
        pushButton1.Image = PNGtoImageSource("RevitTest.Resources.Update-16.pn​g")

 

becomes:

        pushButton1.LargeImage = PNGtoImageSource("RevitTest.Update-32.pn​g")
        pushButton1.Image = PNGtoImageSource("RevitTest.Update-16.pn​g")

 

 

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
Gary J. Orr
(Your Friendly Neighborhood) CADD/BIM/VDC Applications Manager
http://www.linkedin.com/in/garyorr

aka (current and past user names):
Gary_J_Orr (GOMO Stuff 2008-Present); OrrG (Forum Studio 2005-2008); Gary J. Orr (LHB Inc 2002-2005); Orr, Gary J. (Gossen Livingston 1997-2002)
Message 10 of 21
L.Maas
in reply to: Gary_J_Orr

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

EESignature

Please mention Revit version, especially when uploading Revit files.

Message 11 of 21

all roads lead to rome Smiley Very Happy


¯\_(ツ)_/¯
Let it work like a charm.

Mustafa Salaheldin


EESignature




Digital Integration Manager, DuPod

Facebook | Twitter | LinkedIn

Message 12 of 21

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

Message 13 of 21
Anonymous
in reply to: DWhiteley

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?

Message 14 of 21
jeremytammik
in reply to: Anonymous

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.

 



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 15 of 21
Anonymous
in reply to: jeremytammik

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;
        }
    }
}

 

 

Message 16 of 21
Mustafa.Salaheldin
in reply to: Anonymous

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.


¯\_(ツ)_/¯
Let it work like a charm.

Mustafa Salaheldin


EESignature




Digital Integration Manager, DuPod

Facebook | Twitter | LinkedIn

Message 17 of 21
Anonymous
in reply to: Mustafa.Salaheldin

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.

png icon prob.png

Message 18 of 21
Mustafa.Salaheldin
in reply to: Anonymous

It is not about changing the extension of the file, the format has to be PNG, meaning you need to open it in an image editor and re-save it as PNG.


¯\_(ツ)_/¯
Let it work like a charm.

Mustafa Salaheldin


EESignature




Digital Integration Manager, DuPod

Facebook | Twitter | LinkedIn

Message 19 of 21
Mustafa.Salaheldin
in reply to: Anonymous

also you need to consider the relative path, make sure it is reading from the assembly directory not from the RVT file directory.


¯\_(ツ)_/¯
Let it work like a charm.

Mustafa Salaheldin


EESignature




Digital Integration Manager, DuPod

Facebook | Twitter | LinkedIn

Message 20 of 21
Mustafa.Salaheldin
in reply to: Anonymous

Three things to do when you are reading from embedded resource:

  • Add the full Namespace, e.g.: "MyNameSpace.SubNameSpace.RevitAddin.Filename.png"

In the example shown below it would be something like this: Namespace.Resources.CreateAreaSeparatioLines16x16.png

  • Right click on the file in the Solution browser and select properties:

1.png

 

Then from the properties pane make sure you set the properties as shown:

2.PNG

  • Cheers

 


¯\_(ツ)_/¯
Let it work like a charm.

Mustafa Salaheldin


EESignature




Digital Integration Manager, DuPod

Facebook | Twitter | LinkedIn

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk Design & Make Report