Showing posts with label Visual Studio. Show all posts
Showing posts with label Visual Studio. Show all posts

Wednesday, October 24, 2012

Setting the Visual Studio Version for Specific Solution Files

If you have multiple versions of Visual Studio installed such as 2010 and 2012, you can easily control what version of Visual Studio will be used to open your solution when it is double clicked.

First open your *.SLN file in notepad and adjust the very top two lines in the file to the following:

  • Visual Studio 2010
    • Microsoft Visual Studio Solution File, Format Version 11.00
    • # Visual Studio 2010
  • Visual Studio 2012
    • Microsoft Visual Studio Solution File, Format Version 12.00
    • # Visual Studio 2012

Wednesday, April 25, 2012

Revit API 101.2

The topic for this post will be centered on the .addin manifest file and how Add-Ins get loaded into the Revit session. The schema or file organization of the .addin file format will be discussed at first and then I'll explain a little bit about the options you have for configuring the manifest file up so that it loads the resources necessary for your tool to run automatically during debug.

Sorry if you've been hoping for a video, but this kind of information is best absorbed through good old fashioned black and white. And as far as language examples, these concepts are virtually identical between C# and VB.NET.

What is the .addin Manifest File?

For those of you that have experience using Revit Add-Ins in versions prior to Revit 2010, you may remember the old cryptic method of loading an Add-In by editing the Revit.ini file. The old Revit.ini method of loading Add-Ins into Revit is now obsolete and replaced by what is referred to as a manifest file. Manifest files are XML formatted ASCII files that tell Revit where the Add-In resources are and what class that the required IExternalApplication, IExternalCommand, or IExternalDBApplication interface has been implemented so that Revit can load the necessary commands and functionality into the session.

Revit searches for .addin files in two locations. The directory paths shown here are for Windows 7. Replace the YYYY in the paths below to match your Revit 2010 or higher installation versions (2010, 2011, 2012, 2013, etc.).

Constant for all users on the machine (may require admin permissions to modify)
  • C:\ProgramData\Autodesk\Revit\Addins\YYYY
Current user logged into Windows only (does not require admin permissions to modify)
  • %USERPROFILE%\AppData\Roaming\Autodesk\Revit\Addins\YYYY

Required Manifest Tags

There are slightly different requirements between command and application load manifests and quite a few optional tags that you can utilize in your .addin manifest files. This section will focus on the required tags. First I'll outline the required tags that are consistent for both application and command loading manifests and then the one tag specific to application load sequences.

Assembly (Required all All)
This tag is required for all kinds of applications and commands. It must contain the full file name to your DLL file. It is only required to contain a full path to the DLL if the DLL does not exist within the same directory as the .addin file. Relative pathing is supported by entering ".\" before the file path and or name so long as the directory is located beneath the same directory that the .addin file resides.

ClientId (Required for All)
A complete and fully qualified global unique identifier is required in this tag. This GUID needs to be unique across all other application or command ClientId's loaded in your session.

FullClassName (Required for All)
This is where you would enter the namespace(s) followed by the class name that contains the command or application interface that you want to execute or load. This tag should never have any spaces contained in it.

VendorId (Required for All Revit 2012 and Higher)
The four digit registered developer ID that can be obtained from the ADN site to identify the developer.

VendorDescription (Required for All Revit 2012 and Higher)
An explanation or description for the developer. It is common to list the company name and or the web address to a support site that relates to the tool.

Name (Applications Only)
A unique name for the application.

A Sample Manifest File

As you can see in the sample .addin file below, it is possible to nest multiple command and applications load sequences into a common .addin file. A sample application load sequence is loaded first with a command after it. The important thing to remember when combining multiple command or application loads is that they are all nested inside a single "RevitAddIns" tag.

<?xml version="1.0" encoding="utf-8"?>
<RevitAddIns>
  <AddIn Type="Application">
    <Name>Application Name</Name>
    <Assembly>Namespace.dll</Assembly>
    <ClientId>4ea76ff3-bba7-4969-9371-c7a3eb8ac0a8</ClientId>
    <FullClassName>Namespace.Class</FullClassName>
    <VendorId>XXXX</VendorId>
    <VendorDescription>Something about the developer, Link</VendorDescription>
  </AddIn>
  <AddIn Type="Command">
    <Text>My Command Name</Text>
    <Description>Example Command Description</Description>
    <Assembly>Namespace.dll</Assembly>
    <FullClassName>Namespace.CommandClass</FullClassName>
    <ClientId>9db2c58d-33f2-4ad1-8932-329dd83a4d0a</ClientId>
    <VendorId>XXXX</VendorId>
    <VendorDescription>Something about the developer, Link</VendorDescription>
  </AddIn>
</RevitAddIns>

Automatic Copying of a Solution Manifest to Install on Debug

You can have your .addin manifest copied to your .addins load directory automatically while debugging in Visual Studio by setting a post build event. This requires that your .addin file exist in the root of your project and included within your project's solution. An example on where and how to enter this to work on a Visual Basic .NET example is shown below.



Note: If you left out a file path to your assembly (file name only), you will be able to debug from your debug directory location during debug mode as well as run the main DLL in your Add-In load directory using the same manifest file. You will need to adjust command below to match the Revit version year and .addin file name as required. Just remember, you will need to include your .addin file withini your Visual Studio solution for this trick to work.


copy "$(ProjectDir)MyAddinFile.addin"
"$(AppData)\Autodesk\REVIT\Addins\2012\MyAddinFile.addin"

Tuesday, April 17, 2012

Revit API Training 101.01

I've been getting lots of requests from folks to post a "Getting Started with the Revit API" or "Revit API 101" series, so here is the introduction to such a series! I better see lots of views on these posts! Don't let me down.

Sample Code on GitHub

I've setup a git repository for you guys to download the source code for the samples (as they become available). Revit API 101 Samples on GitHub: http://github.com/rudderdon/Revit101 .

Introduction

This is the first of what might be an endless series on how to get going with the Revit API. Since Revit 2013 just came out, these topics will focus on 2013. If Revit 2012 is all that you have installed, don't worry. The Revit 2012 and 2013 API's differ only slightly and I'll do my best to point out what is different between the two versions in my posts as we run into those differences.


Getting Started, the Development Environment

The first thing that I would recommend for someone that is bran spanking new at programming altogether would be to download and install an Integrated Development Environment (IDE) suitable for .NET development. The Application Programming Interface (API) for Revit is based on the Microsoft .NET Framework 4.0. There are several free IDE platforms out there that you can use, but if you have access to or can afford it, I recommend Microsoft Visual Studio Professional (VS). The latest official versions of VS at the time of this post is VS 2010. There is a free BETA version for 2011 out that you can use, but it will stop working later in the summer of 2012 unless you purchase a license.

Download and Purchase VS 2010 Professional
Download VS 2011 BETA
Download VS 2010 Express Versions (FREE)

Other non Microsoft Sanctioned .NET IDE's
SharpDevelop (Free)
MonoDevelop

Download and Explore the Revit SDK

The Software Development Kit for Revit contains several samples as well as key documentation that can keep you moving in the right direction. The 2012 SDK has a lot more samples than the 2013 version, but you can download either from the Autodesk Revit website. You can also read through Autodesk's tutorial entitled "My First Revit Plug-in."


The next post will cover the Revit API implementations and how they work. We will also have our first sample piece of code to work with.

Friday, April 6, 2012

Revit 2013 Visual Studio 2010 Template

For those of you that also write Add-Ins for Revit, I went ahead and posted an updated template for Autodesk Revit Architecture 2013. I've included links to a VB.NET and C# template below.
Installing VB.NET
Copy this zip file as-is (do not unzip it) into a directory beneath
"%USERPROFILE%\Documents\Visual Studio 2010\Templates\ProjectTemplates\Visual Basic" 

Installing C#
Copy this zip file as-is (do not unzip it) into a directory beneath
"%USERPROFILE%\Documents\Visual Studio 2010\Templates\ProjectTemplates\Visual C#" 

The next time you launch Visual Studio 2010, you will notice a new project template named "Revit Architecture 2013 Template" in the directory's name you placed it under kinda like what you see below.


Saturday, October 1, 2011

How to Build a ClickOnce Installer for Revit Add-Ins

Have you ever wanted to deploy an installation package that did not required Admin privileges for the distribution of your Revit Add-ins? Do you also need a simple means to deploy frequent updates? ClickOnce installers can do just that for you.


Microsoft unveiled the ClickOnce technology way back in 2003 and is great so long as your application is simple and does not require any modifications during install that may require administrative access. ClickOnce works by copying an entire isolated application to the user's profile on install. There is a small issue in that ClickOnce is not available for class library projects (Revit Add-Ins are class library projects).

Here is a workaround that you can use to deploy a ClickOnce application to install your Revit Add-Ins.

From inside your current Revit Add-In Visual Studio solution, add a new Windows Console project named the same as your target project but with a ".Updater" suffix added to the end. Save this new project alongside your current project. This new console project will only be used to copy the Revit Add-In program files into the user's Revit Add-Ins directory under their Windows user profile.



This new project will allow you to generate the published installer that a standard class will not. Open the project settings and click on the "Publish" tab along the left (VB.NET, C# will be different). You can enter a version setting as well as an installation URL where youy will post your updates.


Notice the highlighted path above in the URL section? You need to also enter this in the "Updates" section. Click on the "Updates" button to access the optional updates settings.


You can set the project and publisher settings from the "Options" button shown here.


The next thing that you need to do is add a reference to your main project so that the resulting dll files can be included as part of the installation of this console project. Add a reference to your console project and choose your project from the Projects tab.


Now that you have a reference to this project, you can add the output dll to your ClickOnce installation process. From the Publish tab, click the "Application Files" button and make sure that everything required by your Revit-Add-In is also available within this dialog. Set each file to "Include" that you will require in your main installation.


You may have noticed that the ".addin" file is included in the above list. Be sure to add your addin file to the console project so that it can be included in this list as it is required in order for the Revit Add-In to launch in a Revit session.

You can also add an icon to be used by your installer if you like by setting an icon in the "Application" tab of the console project.

Now that you've got the basic format of the publish project configured, you'll need some code to copy the files into the user's Revit Add-Ins directory. Enter the following code in the default "Module1" that was created automatically in your new console project to do just that:


Imports System.IO
Imports System.Reflection

Module mod1

    Private m_sourcePath As String = Path.GetDirectoryName(Assembly.GetExecutingAssembly.Location)
    Private m_w7 As String = Environment.GetEnvironmentVariable("UserProfile") & "\AppData\Roaming\"
    Private m_xp As String = Environment.GetEnvironmentVariable("UserProfile") & "\Application Data\"

    ''' <summary>
    ''' The Main Function
    ''' </summary>
    ''' <remarks></remarks>
    Sub Main()
        ' Test for Win7
        If Directory.Exists(m_w7) Then
            DoCopy(m_w7)
            Exit Sub
        End If
        ' Test for XP
        If Directory.Exists(m_xp) Then
            DoCopy(m_xp)
            Exit Sub
        End If
        ' Warn on Failure
        MsgBox("Your Operating System was not Properly Detected", MsgBoxStyle.Exclamation, "Installation Failed")
    End Sub

    ''' <summary>
    ''' Copy the Files
    ''' </summary>
    ''' <param name="p_destination"></param>
    ''' <remarks></remarks>
    Private Sub DoCopy(p_destination As String)
        ' Addin path
        Dim m_PathAddin As String = p_destination & "Autodesk\Revit\Addins\2012"
        ' Get Files
        Dim m_di As New DirectoryInfo(m_sourcePath)
        Dim m_FilesAddin As FileInfo() = m_di.GetFiles("*.addin")
        Dim m_FilesDll As FileInfo() = m_di.GetFiles("*.dll")
        For Each x In m_FilesAddin
            Try
                x.CopyTo(m_PathAddin & "\" & x.Name, True)
            Catch ex As Exception
                ' Quiet Fail
            End Try
        Next
        For Each x In m_FilesDll
            Try
                x.CopyTo(m_PathAddin & "\" & x.Name, True)
            Catch ex As Exception
                ' Quiet Fail
            End Try
        Next
    End Sub

End Module

After you've made all the necessary adjustments, you can click the "Publish Now" button in the Publish tab to publish the project. A new Publish directory will be created beneath the directory where your new console project was saved. This is your ClickOnce installer? Copy the entire set of files to either a location on your network or to an FTP on the web where you will host the installations. The ".application" file is the installer. Setup.exe can be used for browsers other than IE.


Each subsequent time that you update your code, click the "Publish Now" button to create a new publish installer. Each sequence will be saved in the same location. Copy the updated files to the location where you are hosting your updates and will become available to your users by executing the "updater" program that was originally installed on your client's machines (Executing the tool from inside Revit will NOT automatically update the tools in this configuration).

Be sure and use Internet Explorer to run the "*.application" ClickOnce installer as this is the only browser that will support this technology so far.

Tuesday, August 30, 2011

Relax Dataset Constraints for TableAdapters in .NET

This is a very common issue that people run into as they begin to use complex dataset objects in their .NET development work. This applies to ASP.NET as well as Winforms development work... even WPF.

If you've ever put together a complex join query in one of your TableAdapter objects and then in your code attempted to apply it to a DataTable object, you may have seen this warning and had no idea what to do about it:

The solution is simple. Open your XSD in design view and select on the background area (do not select a query or a TableAdapter).

Then in the properties pallet you will see a property named "EnforceConstraints". Set this property to False.


This option gives you the ability to provide dataset level constraints which are usually not necessary if your database backend has its own constraints.

Monday, August 15, 2011

Check a String for Valid File Naming Characters

Here's a super simple solution to a surprisingly common problem for validating file naming strings using .NET.

If you have ever required a user to enter a file name and had to validate the characters they entered, I'm sure you have some sort of function that can do this. Surprisingly though, I see a bunch of code where people build their own file name character validation functions.

There is a built-in .NET function for validating file naming characters available from the System.IO namespace. The snippet below shows a very basic use of this command accepting an input of a string to validate and returns the same string back out if valid and an empty string if invalid file naming characters are detected.

    ''' <summary>
    ''' Make sure the path does not contain any invalid file naming characters
    ''' </summary>
    ''' <param name="fileName">The Filename to check</param>
    ''' <returns>A string</returns>
    ''' <remarks></remarks>
    Private Function CheckValidFileName(ByVal fileName As String) As String
        For Each c In Path.GetInvalidFileNameChars()
            If fileName.Contains(c) Then
                ' Invalid filename characters detected...
                ' Could either replace characters or return empty
                Return ""
            End If
        Next
        Return fileName
    End Function
You can call this function like:

If CheckValidFileName(m_string) = "" Then Throw New Exception
If an empty string is returned, you've got invalid characters in the file name...

Thursday, April 21, 2011

Navisworks Controls on 64 Bit

If you are running a 64 bit development environment like the rest of the world and have attempted to utilize the Navisworks ViewControl you have probably run into some rather annoying issues. This control will not work in a 64 bit environment at design time in your Visual Studio project, but behold... there is a workaround.

You will need a 32 bit OS where you can install the 32 bit version of Navisworks so you can steal the API dll references that you can use on your 64 bit OS for development. Or if you're super duper lazy, you can just download the one that I'm using and adjust all your Autodesk.Navisworks.Controls references to this 32 bit flavor of the dll for use at design time.

Download the 32 bit control DLL here. Hopefully Autodesk doesn't try to kick my ass for offering this file for download. But after all you can install a 30 day trial to keep this file forever, so no big whoop I guess.

When compiled, the control will work just fine on a 32 bit and 64 bit machine (just like it should otherwise).

Monday, April 4, 2011

Revit 2012 Addin Templates for Visual Studio 2010

Well, if you have not yet begun using Visual Studio 2010 for your Addin development with Revit 2011... you don't have much of a choice with Revit 2012.

If you want the antiquated Revit 2011 templates, go here.

If you haven't heard, the API for Revit 2012 has been updated to support .NET 4.0. Your .NET 3.5 and Revit 2011 Add-ins will still function with Revit 2012 (with some key edits to your .addin manifest files) but you will no longer be able to debug .NET 3.5 Add-Ins with Revit 2012.

I like to maintain separate templates for each flavor of Revit that I develop for since the paths to the API references are different for each product.


All you have to do is export a project as a template (the result will be a zip file) and place them under your current user's profile at:
"%USERPROFILE%\Documents\Visual Studio 2010\Templates\ProjectTemplates\Visual Basic".

It is important to place your templates into a sub directory (we'll name ours "Revit 2012") and to NOT unzip the template files. Leave the zip files intact and this will work just fine. Your directory structure should look something like the image below.


For you lazy folks... here are the links to the preconfigured templates:

Wednesday, March 9, 2011

Winning Visual Studio Stretchy Form Design 101

I get this question a bunch: "How Don, how do you build those awesome winning forms all the time that stretch all sweet like?"... The answer is simple and I'll share how it's done right here in this very post!

First off, if you're using the form resize events to calculate the widths and heights of your controls based on the size of the form, you're on the wrong path... there's a far easier way to do it.

If you want to follow along, create a new form and layout some controls like the one shown here in this tricky example (datagrid on the left, grouped listbox in upper right, two buttons on the lower right):




Here is how it looks as it stretches (in design view too!!!).... notice how the controls morph in a logical way? It's magic... and logic (I know, I get the two confused sometimes too...)



Here's how I got it to do this... the trick is in each control's anchor settings!... Select a control and click on the Anchor properties. You can set any combination of four directions for anchoring for a single control.



Setting the anchoring of opposite directions will result in the control stretching as the form is stretched in that same direction. Setting all four directions will result in the control stretching in all directions as the form is stretched in both directions from the corner.

Setting the anchor to upper and right will simply justify the control in the form in that direction as the form is stretched in any direction...

When working with groups, the controls inside the groups are anchored within the group boundary only. So if you want a group to stretch, you must set the anchor settings for the group... you should then set the anchoring for the control within the group boundary separately.

See you guys at RTC 2011!!

Sunday, February 27, 2011

Convert Rooms to 3D Masses

Well, it has been quite a while since I shared something pertinent to anytime of code or .NET. I've been busy with less exciting things and haven't really had the time, but that's about to change!

This post will touch on how to solve a common problem where designers want to see how a room looks in full 3D while in the programming (Room and Area Programming) stages to help figure out the relationships from one room to others. This is difficult to do with traditional rooms that Revit creates, mainly because they aren't real physical objects (and you cannot see the damn things in 3D).

Here is what my test model looks like with five simple rooms placed in. One surrounded entirely by room separation lines, one with curved walls, and some others for verification and proof that the tool will actually work in most conditions.


Create a new .NET 3.5 class project in Visual Studio 2010. You'll need to build in the Command class on your own this time... Add a form object and name it Form_Main. Add the following referenced to your project and import their name spaces as shown below:


Imports System
Imports System.Collections.Generic
Imports System.Diagnostics
Imports Autodesk.Revit.DB
Imports Autodesk.Revit.UI
Imports Autodesk.Revit.ApplicationServices
Imports Autodesk.Revit.Creation

Add two buttons to your form named "ButtonGenerateMasses" and "ButtonCancel"... their suggested placements are shown here:



Now we need to focus on what all variables we need to expose to our form class and how they will be used. Add the following private variables to this form class just beneath the class declaration:


Private m_CmdData As ExternalCommandData
    Private m_Doc As Autodesk.Revit.DB.Document
    Private m_FamDoc As Autodesk.Revit.DB.Document
    Private m_App As Autodesk.Revit.ApplicationServices.Application
    Private m_SECategory As Category
    Private m_AppCreate As Autodesk.Revit.Creation.Application

As you can see in the listing above, our variable requirements are actually quite simple. We're mainly concerned with document and application objects that are required to generate families in Revit.

The next item we'll code in is the base class constructor. Now keep in mind that one argument is required to generate an instance of this class, IExternalCommand. This argument is the same as the one required in the IExternalCommand's Execute function. The Constructor is shown here:


''' <summary>
    ''' General Class Constructor
    ''' </summary>
    ''' <param name="settings"></param>
    ''' <remarks></remarks>
    Public Sub New(ByVal settings As ExternalCommandData)
        ' Always call this when using a constructor for a form object
        InitializeComponent()
        ' Settings reference to UI and DB objects
        m_CmdData = settings
        ' Application and Document References
        m_App = m_CmdData.Application.Application
        m_Doc = m_CmdData.Application.ActiveUIDocument.Document
        ' Mass Category
        m_SECategory = m_Doc.Settings.Categories.Item(BuiltInCategory.OST_SpecialityEquipment)
        ' App creator
        m_AppCreate = m_App.Create
        ' Set the form title
        Me.Text = "Rooms to Masses"""
    End Sub

Now that we have all of the main framework ready to go, we can dig down into how the families are built using data from the room elements in the model. Create a new subroutine named GenerateMasses. This subroutine is where all the magic happens.


''' <summary>
    ''' Generate 3D Specialty Equipment Extrusions for Rooms
    ''' Specialty Equipment is Scheduleable
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub GenerateMasses()

    End Sub

Before we get back into filling in the functionality in this subroutine, let's get the button assignments in our form out of the way. Double click each of the buttons in the form designer to autogenerate their click event functions and add the following very simple code to these functions:


''' <summary>
    ''' Close the App
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub ButtonCancel_Click(ByVal sender As System.Object, _
                                   ByVal e As System.EventArgs) _
                               Handles ButtonCancel.Click
        Me.Close()
    End Sub

    ''' <summary>
    ''' Launch GenerateMasses
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub ButtonGenerateMasses_Click(ByVal sender As System.Object, _
                                           ByVal e As System.EventArgs) _
                                       Handles ButtonGenerateMasses.Click
        GenerateMasses()
    End Sub

Now let's focus the rest of this post on what's missing!.... haha I know, you'll have to wait until tomorrow to see the ending. So stay tuned and come back tomorrow for the meat of this idea finalized into true usable code! Tomorrow's post will be on the completion of the GenerateMasses subroutine.

Tuesday, January 4, 2011

Visual Studio 2010 Revit 2011 Addin Templates

Have you begun using Visual Studio 2010 for your Addin development yet? Well if you have, then this post is for you! Just don't forget to always set your .NET Framework to 3.5

As you begin to develop with the Revit API, it can be annoying to have to duplicate your addin setup code and references to the Revit API. Did you know that you can create custom addin templates for your projects with all references and boiler plate code prepopulated? Just think of all the women that you can impress at the bar with a story about how you can do this!!!

Create a project with no special functionality but with all required references to the Revit API along with a basic command class setup the way you like and an application class as well. This will help make it easier to use the same template for both commands and applications. Links to the prebuilt template files that I use are at the bottom of this post.

You should maintain separate templates for each flavor of Revit that you develop for since the paths to the API references are different.


The provided "Command" class


'.NET common used namespaces
Imports System
Imports System.Windows.Forms
Imports System.Collections.Generic

'Revit.NET common used namespaces
Imports Autodesk.Revit.ApplicationServices
Imports Autodesk.Revit.Attributes
Imports Autodesk.Revit.DB
Imports Autodesk.Revit.UI
Imports Autodesk.Revit.UI.Selection

&lt;Transaction(TransactionMode.Automatic)> _
&lt;Regeneration(RegenerationOption.Manual)> _
Public Class Commands
    Implements IExternalCommand

    ''' &lt;summary>
    ''' Main entry point for every external command.
    ''' &lt;/summary>
    ''' &lt;param name="commandData">Provides access to the Revit app and docs&lt;/param>
    ''' &lt;param name="message">Return message&lt;/param>
    ''' &lt;param name="elements">elements&lt;/param>
    ''' &lt;returns>Cancelled, Failed or Succeeded Result code.&lt;/returns>
    Public Function Execute(ByVal commandData As ExternalCommandData, _
                            ByRef message As String, _
                            ByVal elements As ElementSet) As Result Implements IExternalCommand.Execute
        Try
            ' Add your code here


            Return Result.Succeeded
        Catch ex As Exception
            ' Add failure handling here

            Return Result.Failed
        End Try

    End Function
End Class


The provided "Application" class


'.NET common used namespaces
Imports System.Windows.Forms
Imports System.Collections.Generic

'Revit.NET common used namespaces
Imports Autodesk.Revit.ApplicationServices
Imports Autodesk.Revit.Attributes
Imports Autodesk.Revit.DB
Imports Autodesk.Revit.UI
Imports Autodesk.Revit.UI.Selection

&lt;Transaction(TransactionMode.Automatic)> _
 &lt;Regeneration(RegenerationOption.Manual)> _
Class Application
    Implements IExternalApplication
    ''' &lt;summary>
    ''' Implement the external application when Revit starts
    ''' before a file or default template is actually loaded.
    ''' &lt;/summary>
    ''' &lt;param name="application">Contains the controlled application.&lt;/param>
    ''' &lt;returns>Return the status &lt;/returns>
    Public Function OnStartup(ByVal application As UIControlledApplication) _
                    As Result Implements IExternalApplication.OnStartup
        ' Add your code here


        ' Return Success
        Return Result.Succeeded
    End Function

    ''' &lt;summary>
    ''' Implement the external application when Revit is about to exit.
    ''' Any documents must have been closed before this method is called.
    ''' &lt;/summary>
    ''' &lt;param name="application">Contains the controlled application.&lt;/param>
    ''' &lt;returns>Return the status&lt;/returns>
    Public Function OnShutdown(ByVal application As UIControlledApplication) _
                    As Result Implements IExternalApplication.OnShutdown

        ' Add your code here


        ' Return Success
        Return Result.Succeeded
    End Function
End Class

All you have to do is export a project as a template (the result will be a zip file) and place them under your current user's profile at:
"%USERPROFILE%\Documents\Visual Studio 2010\Templates\ProjectTemplates\Visual Basic".

It is important to place your templates into a sub directory (we'll name ours "Revit 2011") and to NOT unzip the template files. Leave the zip files intact and this will work just fine. Your directory structure should look something like the image below.


Links to the above mentioned templates can be found here (for those that do not want or know how to create their own):

Sunday, December 5, 2010

AU2010 CP333-1 .... 10001110100001011010100001101010001 1110100

Well I'm finally back and unwound from the trip to Las Vegas where I taught my first class for Autodesk University.

From what I could tell, about 140 (93 people completed a survey for me - THANKS) of the 186 that signed up for the class actually showed up and surprisingly, my speaker rating is an overall 4.469 out of 5! The comments people left were mostly right on point and next time I'll avoid the live code scrolling and slap the key stuff on some super duper slides and maybe even add some sound FX to keep people's senses going.

I just wished I had more than a tiny 60 minutes to present all the madness... I left a ton of information out of the presentation due to timing constraints... Oh well (enough complaining)...

William Lopez Campo mentioned my class as his favorite (I think he's just being nice ;) in his recently famous blog post entitled "AU 2010: My top 5 lists"... I have to say I read the whole post and couldn't agree more with what he wrote in there... right on point.

It was also really cool to meet all the industry badasses in regards to BIM in person (too many of them to name... you know who you are).



So the class was entitled "Leveraging the Tail End of the BIM Life Cycle with APIs" and was centered around how to build a powerful web environment where people could directly interact with data in a BIM model and even synchronize modified data back into the model if they so chose.

I'll be elaborating on this topic in future posts so don't go anywhere!!

Monday, November 29, 2010

By the Book Part 2 - Harvesting Families from a Project Model

This post is part 2 in response to the sample Revit API application I wrote for the book entitled "Mastering Revit Architecture 2011"...  in Chapter 24 "Under the Hood of Revit."


Well, I promised I would show you the updated family export code in my previous post "By the Book Part 1 - Harvesting Families from a Project Model."... so without further procrastination...

We'll get started off by setting the code to our export button illustrated below. All this does is hide the lower buttons to make room for the progress bar and then runs the export routine. When we're all done, we'll call the close function for the form and exit out.


''' <summary>
    ''' Export the families and then quietly close
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub ButtonExport_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonExport.Click
        Me.ButtonCancel.Visible = False
        Me.ButtonExport.Visible = False
        Me.ButtonSelectAll.Visible = False
        Me.ButtonSelectNone.Visible = False
        doExport()
        ' We're all done
        Me.Close()
    End Sub

I guess we should get the last remaining function out of the way as well before we dive into the export function. The function below is used to verify that all characters used to create a file name are valid for the Windows OS.


''' <summary>
    ''' Make sure the path does not contain any invalid file naming characters
    ''' </summary>
    ''' <param name="fileName">The Filename to check</param>
    ''' <returns>A string</returns>
    ''' <remarks></remarks>
    Private Function CheckValidFileName(ByVal fileName As String) As String
        For Each c In Path.GetInvalidFileNameChars()
            If fileName.Contains(c) Then
                ' Invalid filename characters detected...
                ' Could either replace characters or return empty
                Return ""
            End If
        Next
        Return fileName
    End Function

The export function starts out by first verifying that at least one category has been checked for export. Then a hashtable is used as a means to reference each selection (hashtables are FAST).


''' <summary>
    ''' This routine performs the exports
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub doExport()
        ' Ony export families that belong to our selected categories!
        Dim m_SelectedCategories = Me.CheckedListBoxCategories.CheckedItems
        ' Do nothing if nothing selected
        If m_SelectedCategories.Count = 0 Then
            MsgBox("You did not select any categories..." & vbCr & "Nothing to do...", _
                   MsgBoxStyle.Information, "No Categories Selected! Exiting...")
            Exit Sub
        End If
        ' A hashtable comes in handy when verifying multiple situations... or 1
        Dim m_CatHash As New Hashtable
        For Each xItemC In m_SelectedCategories
            m_CatHash.Add(xItemC.ToString, "Category")
        Next

The next thing to do is make sure the target directory exists for the export.


Try ' If the parent export directory is missing, create it
            Directory.CreateDirectory(Replace(Me.LabelExportPath.Text, "/", "\", , , CompareMethod.Text))
        Catch ex As Exception
            ' Message to show any errors
            MsgBox(Err.Description, MsgBoxStyle.Information, Err.Source)
        End Try

With the main directory created, we can continue with the element collection and progress bar setup. The filter below grabs all "Type" elements from the model and turns the result into an easy to use list of DB.Element.


' Filter to get a set of elements that are elementType 
        Dim m_SymbFilter As New DB.ElementIsElementTypeFilter
        Dim collector As New DB.FilteredElementCollector(m_Doc)
        collector.WherePasses(m_SymbFilter)
        ' Create a list from the collector
        Dim FamilySymbols As New List(Of DB.Element)
        FamilySymbols = collector.ToElements
        ' Start the progressbar
        Dim iCnt As Integer = 0
        Dim iCntFam As Integer = FamilySymbols.Count
        Me.ProgressBar1.Visible = True
        Me.ProgressBar1.Minimum = 0
        Me.ProgressBar1.Maximum = iCntFam
        Me.ProgressBar1.Value = iCnt

Now we can iterate the element list and perform the necessary exports as external RFA files.


' The export process
For Each x As DB.Element In FamilySymbols
    If (TypeOf x Is DB.FamilySymbol) Then
        Dim m_category As DB.Category = x.Category
        If Not (m_category Is Nothing) Then
            ' Is it a selected category?
            If m_CatHash.Contains(m_category.Name) Then
                Dim m_ExportPath As String = ""
                Try ' Create the subdirectory
                    m_ExportPath = Me.LabelExportPath.Text & "\" & m_category.Name & "\"
                    Directory.CreateDirectory(Replace(m_ExportPath, "/", "\", , , CompareMethod.Text))
                Catch ex As Exception
                    ' Category subdirectory exists
                End Try
                Try ' The family element
                    Dim m_FamSymb As DB.FamilySymbol = x
                    Dim m_FamInst As DB.Family = m_FamSymb.Family
                    Dim m_FamName As String = m_FamInst.Name
                    ' Verify famname is valid filename and exists
                    If Dir$(m_ExportPath + m_FamName & ".rfa") = "" And CheckValidFileName(m_FamName) <> "" Then
                        Me.LabelFileName.Text = "...\" & m_category.Name & "\" & m_FamInst.Name
                        Dim famDoc As DB.Document = m_Doc.EditFamily(m_FamInst)
                        famDoc.SaveAs(m_ExportPath + m_FamName & ".rfa")
                        famDoc.Close(False)
                    End If
                Catch ex As Exception
                    ' Prevent hault on system families
                End Try
            End If
        End If
    End If
    ' Step the progress bar
    Me.ProgressBar1.Increment(1)
Next

That's it! Now you have the means to quickly harvest families from a Revit 2011 model.

If you have any questions or suggestions for other Revit 2011 code solutions, don't hesitate to leave a comment or ask a question.

Saturday, November 6, 2010

Debugging a .NET 3.5 Class with Revit 2011 from Visual Studio 2010

Have you ever tried to build a Revit 2011 addin in Visual Studio 2010 only to discover that your debug will not work!!!??? This can be very frustrating but easily fixed if you follow the quick configuration steps illustrated in this post!!


You will need to make a couple minor configurations to both the Autodesk Revit 2011 configuration file and the Visual Studio 2010 development environment in order to successfully debug a Visual Studio 2010 application.

Since all projects built for Autodesk Revit 2011 must be compiled targeting the Microsoft .NET 3.5 Framework, you will first need to set your Visual Studio project to target the .NET Framework 3.5:



The Visual Studio 2010 default .NET Framework is 4.0 while the required .NET Framework for Autodesk Revit 2011 is 3.5. You will need to specify a target framework environment in the Revit 2011 executable configuration file so Visual Studio doesn’t try to debug in its default .NET Framework 4 mode.

Modify the Revit.exe.config file to support debug with Visual Studio 2010 by navigating to the Program directory beneath your Revit product's installation directory and open the XML formatted file named “Revit.exe.config” in an ASCII text editor such as Notepad.exe.


Add the following three lines highlighted in yellow just inside the closing tag of “configuration” to allow debug control with Visual Studio 2010:



That's it!... You can now debug a Revit 2011 addin from Visual Studio 2010

Monday, October 18, 2010

Application Startup in Visual Startup Class Debug

If you've downloaded either of the C# or VB.NET free express versions of Microsoft Visual Studio 2010 and attempted to create any Revit Add-In class applications you may have noticed that there is no way to tell Visual Studio how to launch Revit for debug purposes.

You will have to manually open your .vbproj or .csproj file in in an ASCII text editor such as Notepad.exe and add a couple lines to accomplish debugging.

Open your .vbproj or .csproj file in notepad add the two lines that I have circled in the image below in the 'Debug|AnyCPU' group condition:

The StartAction must have a value of "Program" and the StartProgram attribute must contain the full path to the Revit executable file that you wish to use for debug. So besides a couple small nuisances, I think the Express versions of Visual Studio Express 2010 work just fins as long as you can live without any environment customizations...