In the last few posts I have outlined in great detail how to make a simple Revit Add-in using the IExternalCommand implementation. Doing that is a great and really fast way of adding new tools to Revit, but after a while we will realize that we just need a little more organization. Luckily for us Revit API offers a way to create our own tab in Revit and an array of different ways to add buttons to it. For the sake of keeping this tutorial simple and easy to follow, I will only show you how to convert our all-ready CurveTotalLength tool to a button.
In this post we will cover a few things like:
- IExternalApplication implementation
- new RibbonPanel
- new PushButton
- resource management
First let’s open our Visual Studio session that we worked on last week. It should look like this:
On the right hand side, in our Solution Explorer I will first change the name of the CurveTotalLength cs file from Class1.cs to CurveTotalLength.cs.
- Right click on Class1.cs
- Click on Rename and type in “CurveTotalLength.cs”
Now, let’s just “Build it” and we can close that file.
This was just a quick maintenance procedure to make sure that our file is named properly because next what we will do is import that file into a new Project in Visual Studio. Unfortunately there isn’t an easy way to just take an existing project in Visual Studio and rename it. The easiest way is to actually just create a new project and add an existing one to it if needed. At least that’s what I have been doing.
So let’s create a new project in Visual Studio. This step was described before here. We will call it GrimshawRibbon, but you can call it whatever you want – just remember that it’s not easy to rename the project folder structure after it was created.
Next, just like we discussed before we need to reference in Revit API and Revit APIUI libraries. Again, look at this link here.
Now, let’s just quickly rename the default Class1.cs to App.cs like so(see image above):
Click Yes, to confirm.
Next, let’s add our existing CurveTotalLength project to this solution (SHIFT + ALT + A). When the window pops up we need to navigate to our CurveTotalLength project location and look for CurveTotalLength.cs file. You should see a new file appear in your Solution Explorer like so:
Next let’s quickly add a new folder to our project and then move(drag and drop) the CurveTotalLength to that folder to keep our application nicely organized:
- Right click on our project.
- Hover over Add…
- Click on New Folder
Before we start implementing the IExternalApplication, we need to make sure that we have all of the “using” statements as well as one more assembly loaded (PresentationCore), that we will need to define an icon/image for our button.
- Right click on References in Solution Explorer, then click on Add Reference…
- Click on “Assemblies” on the left side.
- In search field type in “Presentation”.
- Select PresentationCore from the list.
- Click OK.
Now, that we have all of the assemblies needed*, we can get to implementing IExternalApplication. First let’s create a “road map” using pseudo code before we start filling in the blanks:
*You will notice that we also defined using System.Reflection in code below. System is loaded in by default so there was no need to add it in, and all we needed to do was just type “using System.Reflection” to start using methods defined in that assembly.
This is a basic outline for our method. We haven’t done any heavy lifting yet, but we have a good idea about what needs to be done. We already loaded in PresentationCore and I mentioned that we will need it to define an image for our button. Before we get to code and explain how to define that image, let’s create the image and load it into our project first: I usually make my images roughly 320 x 320 pixels. That’s too big for the icon (32 x 32), but it’s much easier to work with that in Illustrator or Photoshop. Save your image to PNG (with transparency) and then you can easily create a 32px icon version of the file using online service called ICO Converter. For a single push button, we need a 32 pixel image so these settings will be fine:
- Select a PNG file.
- Select pixel size needed
- Select Bit Depth
- Click Convert
Files will be automatically saved in our Downloads folder and will be called favicon.ico so all we need to do is rename that file to totalLength.png. You will be prompted if you want to change the file extension to PNG so please click yes to confirm. Now, let’s add it into our Visual Studio project. First create a new folder in our Solution Explorer and call it “Resources” then add our file like so:
- Right click on Resources folder
- Hover over Add
- Click on Existing Item…
Now we just need to navigate to the image that we want to use as our icon and select it. Once the image is inserted we need to change its BuildAction:
- Select our image in the Solution Explorer
- Change Build Action to Resource
Our image is ready, so are all of the assemblies needed, so we can go ahead and define our method that will create the tab and a new button:
Let’s go over it line by line:
- This defines what kind of method we want to create. For our purpose a “static”, “void” method will suffice. Static means that we are creating a method that can be invoked without first creating an instance of its parent class. Void means that our method will not return anything.
- This will be the name displayed on our tab.
- Here we create an instance of a new tab with the given name.
- Here we create a new Panel which will be added to our Tab. We call this panel Tools.
- Here we use Reflection method called Assembly to get a path to folder that our application is being compiled to.
- Before we create a new PushButton we need to create a PushButtonData instance
- first input is a unique name/id for our new button
- this is text that will be displayed under our button. I wanted our name to be two lines hence the System.Environment.NewLine piece of code.
- this is a location of a dll that will be called when button is pushed
- this is the name of the method that will be called including a namespace.
- Here we add the new PushButton to our Panel.
- Here we define a tooltip message that will be displayed when user hovers over our button.
- Here we define a BitmapImage from the source. Bear in mind that it has to be defined as a URI source so we are creating a new instance of URI like so: new Uri(“pack://application:,,,/GrimshawRibbon;component/Resources/totalLength.png“) where “GrimshawRibbon” is the name of our current project followed by “;component” and then a “/Resources” which is the name of the folder we put our image into, then finally “/totalLength.png” which is the name of the file itself.
- Here we set the LargeImage property of the button to our image.
This is really the gist of our application. Just to make sure that it all makes sense here’s where our variable are coming from:
Now that we have a method defined that will create the tab, all we have to do is call it when our plug-in is loaded into Revit – every time Revit starts. Here’s the full code:
The last step before we can fire up Revit is making sure that we have a addin manifest file that will register our application with Revit. Just like we did in the previous tutorial let’s just add a new TextFile to our project and call it GrimshawRibbon.addin. You can reference the steps from here. Here’s the code for this addin file (it differs slightly from an ExternalCommand registration):
Make sure that you are changing the addin file Build Action to Content and its Copy to Output Directory to Copy if newer.
Now, if we build our project we should have two files, that we can copy to our Revit addins location. Again, if you don’t know where that is, please reference previous tutorials here. Truth is that you should really read the previous three tutorials before attempting this one. :-)
Now, if you fire up Revit, you will have something like this:
Adding your own tab will probably make you realize that having a whole new tab just for one tool is kind of meaningless and you will set out on the path to fill it up with things that will make your life as a Revit user that much more easier. I have been doing this for a few months now, but I am up to 15 custom tools that I created for our office. Some are really simple like the one we were working with during this tutorial series, and some are a little bit more involved. Either way, having your own toolbar in your favorite application will for sure give you at least a small reason to be proud of yourself.
Thanks for sticking it out with me. It has been a long tutorial, lots of steps involved, but if you got through then its all it matters. Also, if I have missed anything – forgot to post an important image, skipped an important step – please let me know.
You can download the final DLL files from here:GrimshawDT - GrimshawRibbon2016 (3051 downloads)
Absolutely enjoy these posts Konrad. It’s about time someone explains the process in a easy to follow way! Kudos!
Agree, I had to learn the hard way so you guys have it easy. :-) You are welcome!
I’ve been struggling months trying to figure out a proper way to include my macros in an organized panel within the ribbon. Thanks a lot and congrats for the amazing job.
You are welcome! I do accept gifts of gratitude, usually in form of free drinks. :-)
Great post, thank you!
For Revit 2016 it work like a charm, but when I’ve tested it for Revit 2014 it doesn’t work.
Have you any advice how apopt it for Revit 2014?
Yes, I would recommend going to Start>Control Panel> Programs> Revit 2014 and hitting that Uninstall button on top of the window. :-) After the initial shock and nostalgia passes, believe me, life is better in year 2016 AD. But, seriously I can’t help you here. I refuse to provide backwards compatibility and localization issues help. I know, I am a horrible person. I will burn in hell, but at least it will be a modern one. Peace!
Konrad, you make me laugh!
I’m totally agree with you, but unfortunately, those who are creating content for Revit MEP are forced to use Revit 2014 or even worse Revit 2012 to insure backward compatibility. That’s why I was looking for a solution at least for Revit 2014.
Nonetheless, thank you for your reply!
Adam, first thing that I would check is to make sure that you are referencing in RevitAPI and RevitAPIUI dlls from the 2014 location. Then try and compile. If everything works then you won’t have to anything else. If however, it doesn’t compile because I used some methods that were added to Revit API post 2014 version (I am not sure if I did, since I never built this particular plug-in for older versions) then you will have to replace them with their older versions, or it might not even be possible. I can’t tell because I never tried it myself.
Konrad – thanks for the great tutorial, I was able to get my first button and got it to work great, what I am struggling with is adding a second button. I’m trying to add the Legend Duplicator and I keep getting a lot of errors when I copy the Push Button Data section and try to add it for a second button…
Please see the answer below.
Konrad, Anything special when adding multiple Push buttons to the same panel (separate panels seems to work ok)? I defined the PB data, and image. “AddItem” will not produce another button on the panel. It will build without errors, just all buttons after that will not load. Should I be able to copy (using your code above as an example):
// create push button for CurveTotalLength
PushButtonData b1Data = new PushButtonData(
“Total” + System.Environment.NewLine + ” Length “,
PushButton pb1 = ribbonPanel.AddItem(b1Data) as PushButton;
pb1.ToolTip = “Select Multiple Lines to Obtain Total Length”;
BitmapImage pb1Image = new BitmapImage(new Uri(“pack://application:,,,/GrimshawRibbon;component/Resources/totalLength.png”));
pb1.LargeImage = pb1Image;
And redefine a new button, then add it to the same panel?
I am looking at some examples here:
It doesn’t look like these examples are doing anything different. Defining the push button data, then adding it to the panel. Next button, defining the push button data, then adding it to the panel.
Looks like I’m missing something somewhere. If you have any thoughts on this, much appreciated.
You need to define a new PushButtonData for the new button. Then you just add it to the same panel by calling the RibbonPanel.AddItem() method. It looks like this
Absolutely amazing Konrad
I have just followed the three steps to create a Addin, and finally this to create my own Tab!
There where some bumps on the way. But with a little logic, and help from Visual Studio. You guide is perfect!
Thanks for the good post. Introducing us to true Addins!! :D
I am glad you liked it.
Thanks for the posts they’re very informative.
I was wondering if it is possible to amend your code so I can add scripts which I have made in dynamo to the ribbon.
I’m not that savvy with coding so I haven’t figured it out yet, a bit of a learning curve but I’ve been wanting to learn for a while now so this will push me!
Awesome tutorial, Konrad! Thanks for making this so very easy to comprehend.
Best of luck at the new place!
Hey. Thanks for this tutorial. I’ve been getting one error that is preventing the app from loading, any thoughts? :
There was a mismatch between the processor architecture of the project being built “MSIL” and the processor architecture of the reference “RevitAPI”, “AMD64”. This mismatch may cause runtime failures. Please consider changing the targeted processor architecture of your project through the Configuration Manager so as to align the processor architectures between your project and references, or take a dependency on references with a processor architecture that matches the targeted processor architecture of your project.
This is just a warning. You can ignore it. As far as I remember I have been getting it for the past 5 years.
The issue above was solved by changing the debugger target framework to 4.5 and processor to x64.
However I have one more question. Is there a way to add python scripts to these buttons. I’ve written a few python scripts that I would like to make into a button. Advice?
There is a great project called pyRevit that makes this super easy. https://github.com/eirannejad/pyRevit
Thanks for this very useful tutorial. Finally a good example :-)
But i did everything you said and when i start Revit 2018 i get the message shown in the picture? Can this be solved or has it something to do with the Revit version?
This is the first time i am visiting your website. And this post is really helpful for beginners like me.
Thank you Konrad!
Thanks Konrad, great work!
You are welcome!
Hi Konrad, this information was very useful for me!
I have a problem when traying to share muy .dll and .addin files with my coworkers in the office. They get this message “Revit Cannot run the external application”. Is there any extra step when sharing the plugin with other computers having the same Revit Version?
Thank you for your help!
It looks like one of the dependencies is missing. You have to share them as well. Your plugin might have a dependency on something like Newtonsoft.Json.dll or MvvmLight.dll. Whatever 3rd party libraries you use, please share them along with the plugin dll.
Konrad, it took me a day or so but I finally got through these tutorials with a working menu!
My question is, have you ever run an external IronPython script from within C#? I have a bunch of scripts that I’d like to add to the menu but I keep getting the attached error when I try to run it.
Here’s what I have in the script (yes, trying to figure this out on my own and not sure what to do)…
public class FV : IExternalCommand
private static void doPython()
ScriptEngine engine = Python.CreateEngine();
engine.ExecuteFile(@”Z:\Revit\Visual Studio\Python\Family Versions.py”);
Result IExternalCommand.Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
throw new NotImplementedException();
Any help would be greatly appreciated. AND, fantastic work you’ve done!!!
I haven’t tried running Python code in C# like that using the ScriptEngine. I did run Python code via command line in C#. I am not sure what you are trying to accomplish, but if the goal is to use Python code and then execute it inside of External Command so that you can expose it via buttons on the Ribbon, then perhaps look into pyRevit. You can work in Python and still build a Ribbon.
Thanks for getting back to me Konrad. I appreciate it. I use pyRevit, but what I was trying to learn was to create my own menu system without pyRevit, then still be able to run some of my python scripts I have. I just don’t know a way to use C# (which is what you need to create the menu according to your example – correct?) to point to a python file on the computer and run it. My end goal is to create our own menu to distribute to others in our office without installing pyRevit and/or RevitPythonShell. Still learning, that’s why I asked.
Appreciate what you’ve done, it’s quite well written.
Like I said, I haven’t tried running Python code inside of External Command, but you can do that. Here’s an example of what the Dynamo team did: https://github.com/DynamoDS/Dynamo/blob/b842a9edb06055000150f64aa963ade6cbb0dd2d/src/Libraries/DSIronPython/IronPythonEvaluator.cs
Uh, sorry, wrong screen shot!
I work with point clouds in Revit, I do use a plugin to assist in these efforts but find I could do with some extra tools. I was researching the idea of being able to create my own and came across your Grimshaw Tools and this thread. By the looks of it there’s no way I have time to even begin getting my head around it.
Are you or you followers then men to speak to go about commissioning such a project?
One such button I need is Raster images on / off.
Relied directly via email.
Hey Archi-Konrad! Great work, loving it.
I work for a large company, and have started building tools that are activated with a button now. It is working well, but I was wondering if you would know how to solve this tiny issue I have: That is that the button is grayed out when I do not yet have a project open in Revit. You see, my tool does not need an open project, and it works just as nicely when clicked under Add-Ins/External Tools. Is there a setting I need to change in my code, that ungrays it out?
This explains the zero document button state: https://thebuildingcoder.typepad.com/blog/2011/02/enable-ribbon-items-in-zero-document-state.html
Thank you Konrad, I have been using this example code to help make multiple custom Plug-ins in Revit. I have a question about ribbon organization. I have multiple pushbutton’s that load in to Revit, and it seems that the custom ribbon defaults to organizing them alphabetically by tabName. Is there a way to change this order? And have multiple push button’s in one tab within the custom ribbon?
I don’t think buttons are organized alphabetically. They are ordered in the order of creation.
I have followed your Tutorials, they are great. The first three parts went great, but with this one I have a problem. It does not work. I send you my two files, perhaps you can give me a hint. I use Visual Studio 2019 and i’m trying to creat a Ribbon tab in Revit 2020, 2021 and 2022.
I did everything you said and when i start Revit 2020 i get the message shown in the picture? Can this be solved or has it something to do with the Revit version?
This is probably happening because this tutorial talks about older versions of Revit, and they would be built with older .NET versions. You might want to build them against newer version of .NET.
Since a week it works fine. The biggest Problem was, that in Visual Studio i could not set it to Net Framework 4.8. Then i found a work arround editing the .csproj file manually. I changed the line with the TargetFramework property to: net48
Yup. That should do it. Good luck!
I have a dyn. file (made a dynamo script) that I would like to reference in my own customised ribbon. How do I reference this?
Does it have to be converted to dll.?
There are some 3rd party tools out there that can do that. I think pyRevit, nonica and some others.
That ‘s great. It’s all things i need. Thank you!
I know I’m Johny-Come-Lately…. I’ve been doing Dynamo scripts for a few years now and I started learning C# in last couple of months. I’ve been failing to create a tab and a button every evening for the past two weeks. I couldn’t eat, I couldn’t sleep, I haven’t been looking after my kids… I followed 4-5 tutorials without any luck until I came across your post. Not sure how I’ve missed it until now as I’ve read all your blogs before…Anyhow, all I wanted to say is :
I LOVE YOU MAN! You are amazing, I’ve learned so much from you. Thank you!
PS: Sorry, I’m a bit over ecstatic right now.