Monday, May 19, 2014

.NET and Reference Versions

Ran into this error today when migrating our company addins to 2015:

The type or namespace name 'Autodesk' could not be found (are you missing a using directive or an assembly reference?

The first thing I tried was to remove the "RevitAPI.dll" and "RevitAPIUL.dll" references and re-add them (I had already confirmed they were being loaded from the 2015 install location). This step failed to correct my issue. Turns out it was a simple oversight on my part; Revit 2015 requires .NET 4.5 and the reference dlls are compiled in that version... my upgraded project had its "Target Framework" set to version 4, simply changing this to 4.5 corrected the problem.

Monday, April 28, 2014

Beware the In Place Family

I have been working to have my latest Revit add on published to the Exchange app store and came across a bug in my code.

The app basically processes all walls (or all selected walls) and allows you to manage the wall join condition (allow/disallow joins) here is a link to the page describing the app: http://krispcad.blogspot.com.au/2014/03/krispcad-wall-join-manager.html.

All was working hunky dory in my test projects so I submitted the app confident of a quick approval. What I got instead was a message telling me that my app throws an error :( Sure enough, when I tested the app with the sample project it threw the error. Turns out that checking that the walls collected are of the correct Category was not enough as the sample project also included an in place family in the wall category... so my code was throwing an error when I was casting my collection's objects to "Walls", the in place family is a "FamilyInstance" and cannot be cast to "Wall".

A simple fix of including an "ElementClassFilter" for a type of "Wall" solved the issue... hopefully my new app will be available on the store in the next few days.

Sunday, March 9, 2014

KrispCAD Wall Join Manager

This post provides instructions on how to use my Wall Join Manager app that is being published on the app store:

Update: the app is now live: http://apps.exchange.autodesk.com/RVT/en/Detail/Index?id=appstore.exchange.autodesk.com%3akrispwalljoinmanager_windows32and64%3aen

Update 2: the app was republished today to include support for the 2015 releases of Revit. If you have previously purchased and downloaded the app you should be able to download the new version with your Autodesk log-in.

Wall Join Manager

This is s simple tool that allows you to align several text notes to another selected text note. To run the command simply follow these instructions:
  1. Optionally select the walls on which you want to change the wall joins.
  2. Run the "Wall Join Manager" command from the Add-Ins ribbon panel.
  3. Select "Join Type", either "Allow" or "Disallow" joins.
  4. Select whether to change the wall join status on one end or both ends of the walls.
  5. Select which walls to apply the changes to. By default if you preselect walls that option will be checked, otherwise you can specify all walls in the project or all in the current view.
  6. Optionally specify whether to filter the walls by one or more wall types.
  7. Click "OK" to apply the changes.
Wall Join Manager - Options Dialog

Wednesday, July 17, 2013

KrispCAD Text Utilities

I have decided to publish two simple apps to the Exchange store:

Text Align 

This is s simple tool that allows you to align several text notes to another selected text note. To run the command simply follow these instructions:
  1. Select the text notes that you wish to align.
  2. Run the "Text Align" command from the Add-Ins ribbon panel.
  3. Select the desired justification option.
  4. Select the text note that the previously selected text notes will be aligned to.
  5. That's it, pretty simple hey?

Change Case

This tool changes the case of the specified text notes to either Upper, Lower, Sentence or Title case. To run the command simply follow these instructions:
  1. Select the text note objects that you want to change the case of. If you do not select any text notes then ALL of the text notes in the project will be changed.
  2. Run the "Change Case" command from the Add-Ins ribbon panel.
  3. Select the desired case option.
  4. That's it, the text notes will have their case changed.

Wednesday, May 25, 2011

Using LINQ with the Revit API

LINQ (or Language Integrated Query) can be used to filter collections retrieved using the Revit API.

For example, say you wanted to retrieve a list of category names of all elements that exist in the active view. To achieve this without LINQ you would:
  1. Select all elements in the active view (using a FilteredElementCollector).
  2. Loop through each element and:
    • Check that category is not null.
    • Check that you haven't already stored this category name (you don't want to see the Doors category 100 times).
    • Store the category in a list.
  3. Sort the list so that it is in alphabetical order.

Using LINQ this can be reduced to:
  1. Select all elements in the active view (using a FilteredElementCollector).
  2. Select all category names from the elements.
  3. Ensure the list of category names contains only distinct items and is in alphabetical order.

using System.Linq;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;

namespace KRSP.DistinctCategories
{
    [Transaction(TransactionMode.Automatic)]
    public class Command : IExternalCommand
    {
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            UIApplication app = commandData.Application;
            UIDocument doc = app.ActiveUIDocument;
            View view = doc.ActiveView;

            //select all elements in the active view
            FilteredElementCollector allInView = new FilteredElementCollector(doc.Document, view.Id);
            allInView.WhereElementIsNotElementType();

            //use LINQ to collect the category names of all elements whilst ensuring that category is not null
            var categoryNames = from elem in allInView
                             where elem.Category != null
                             select elem.Category.Name;

            //strip out duplicates and order the list of category names
            categoryNames = categoryNames.Distinct().OrderBy(catName => catName);

            //construct a string list of the collected category names
            string catList = "Categories:";
            foreach (var catName in categoryNames)
            {
                catList += "\n" + catName;
            }

            //display a dialog showing the results.
            TaskDialog td = new TaskDialog("INFO");
            td.MainInstruction = string.Format("{0} Categor{1} Found.", categoryNames.Count(), categoryNames.Count() == 1 ? "y" : "ies");
            td.MainContent = catList;
            td.Show();

            return Result.Succeeded;
        }
    }
}

Lets examine the LINQ statement above:
//use LINQ to collect the category names of all elements whilst ensuring that category is not null
            var categoryNames = from elem in allInView
                             where elem.Category != null
                             select elem.Category.Name;
There are three parts to the query:
  1. The "from/in" clause; this is where you define which collection you are querying. In this example we are querying the "elem" items inside the "allInView" FilteredElementCollector.
  2. The "where" clause; this clause is optional and allows us to conditionally filter the selection. In this example we are only selecting those elements whose categories are not null.
  3. The "select" clause; this is where we state what we want to retrieve from the collection. In this example we are retrieving the "Category.Name" of each "elem" item.
The results of running the above code on a simple model are shown below:

Monday, May 23, 2011

RFO Ribbon Addin

I recently uploaded the initial release of the RFO Ribbon Addin (thanks to Martijn for all the ground work). You can download it here: http://revitforum.org/showthread.php/1793-RFO-Ribbon-Add-In-Downloads.

The following explains the folder structure and files associated with the addin and how to add new commands to it.

Folder Structure
The addin is structured such that the individual commands that appear on the ribbon are separate dll files (each separate command could be added to Revit as a stand alone addin with its own addin manifest file if you didn't want to use the RFO Ribbon Addin) this allows new commands to be added and commands to be updated easily without the need to recompile the core addin.

The root folder contains the following files:

FileDescription
Addins FolderContains "Vendor" subfolders.
RFO_Addins.xmlThis xml stores a list of all addins and whether or not they are "Enabled".
RibbonAddins.dllThis is the main app that creates the ribbon tab and loads individual addins.
Various ImagesThe thumbnails used on the ribbon.

RFO_Addins.xml
This file lists all available addins and whether each one is displayed on the ribbon not. The xml is divided into section by "Vendor". I use "Vendor" to represent individual contributers, I used the term "Vendor" as this matches what Autodesk uses in their addin manifest files.

The xml contains multiple "Vendor" tags which have a single attribute "Id". The "Id" attribute is equal to the "VendorId" attribute found in the Autodesk addin manifest files. You can obtain a Vendor Id from: http://www.autodesk.com/symbreg/startreg.htm. The "Id" attribute is used to identify each contributor and the folder containing their addins must have the same name as their "Id", additionally the xml file containing the information about their addins must also have the same name. For example; Contributor "ABCD" would have an xml file named "ABCD.xml" contianed within the "Addins/ABCD" subfolder.

Each "Vendor" tag contains one or more "Command" tags with two attributes; "Id" and "Enabled". The "Id" attribute is a GUID which is equal to the "AddInId" attribute found in the Autodesk addin manifest files. The "Id" must match the corresponding "Id" in the contributors xml file.The "Enabled" attribute tells the program whether or not to display this command on the ribbon, it can have a value of "True" or "False".

Individual Commands
Each command on the RFO Ribbon is loaded by a separate dll file and is located within a subfolder named after the contributors unique Id (or "VendorId"). For example; if a vendor with Id "ABCD" contributes a command "My Command" then the dll file would be located within a folder named "ABCD" within the "Addins" folder mentioned in the table above.

Each contributor also must provide an xml file named after their unique Id. The xml file also must reside in the subfolder named after their unique Id.

The xml file has one "Vendor" tag which has three attributes; "Id" is the contributors unique Id, "Name" is the name of the contributor and will appear on the ribbon panel containing their command(s), "Description" is a short description of the contributor and can contain contact information.

The "Vendor" tag contains one or more "Command" tags. Each "Command" tag has one attribute; "Id" is a GUID and should match the "Id" used in the main "RFO_Ribbon.xml" file.

Each "Command" tag contains seven tags describing the command:

  1. "Name" The name of the command, this name is not seen by the user but is used internally by Revit.
  2. "Assembly" The filename of the dll file associated with this command.
  3. "FullClassName" The fully qualified entry point to the command, usually this is the namespace and class name separated by periods.
  4. "Text" The text that appears on the command button.
  5. "Description" The basic tooltip for the command.
  6. "LongDescription" The extended tooltip that appears when hovering over the command for more than two seconds.
  7. "LargeImage" The thumbnail image that appears on the command button. This should be 32x32 pixels.


Add New Command
To add a new command is a two step process:

  1. Extract/copy the command files (including the contributor's xml file) into a subfolder of RFO Ribbon's "Addin" folder", the subfolder should have the same name as the contributor's Id.
  2. Update the RFO_Ribbon.xml to include the new "Vendor" tag and "Command" tags.


A future release of the ribbon app will automate the process of adding a new command.

Thursday, May 5, 2011

Debugging in Visual C# 2010 Express

When you try to debug a Revit add-in by selecting "Start Debugging" from the "Debug" menu (or by hitting the "F5" key) you will prompted with an error saying that "A project with an Output Type of Class Library cannot be started directly.".

While this is true as you can't run your add-in without it first being loaded into Revit it is also a bit frustrating. In the full paid version of Visual Studio there are options to specify the starting application for class libraries allowing you to debug your add-in, these options are missing from the Express versions. There is a workaround though, see below:

When you start a project, Visual C# 2010 Express automatically creates the files needed to create your application or add-in, one of these files is named "YourProject.csproj.user", by editing this file you will be able to specify the starting application for your add-in.

When you open this file in Visual C# it appears like this:
<?xml version="1.0" encoding="utf-8"?>
<project toolsversion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
</project>
Simply add a new "PropertyGroup" tag between the "Project" opening and closing tags so that the file now looks like this:
<?xml version="1.0" encoding="utf-8"?>
<project toolsversion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <propertygroup condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <startaction>Program</startaction>
    <startprogram>C:\Program Files\Autodesk\Revit Architecture 2012\Program\Revit.exe</startprogram>
  </propertygroup>
</project>
Close and then re-open your solution and you can now debug your add-in!

Note that the path shown in the xml above within the "StartProgram" tag relates to the default location for Revit Architecture 2012, you will need to change this path if you are using a different version or have installed in a different location.