Tuesday 2 October 2012

Creating a 3D View

In my previous post, I explained that I was having trouble with creating a 3D view as I needed the viewfamilytypeid of the 3D view, which I believe can be different for each project, I figured out that I needed to create a collection of the viewFamilyTypes within the project and go through this until I found the ThreeDimensional viewfamily, so here is how I did it:


Using tx As New Transaction(doc)
            tx.Start("Create 3D View")
            Dim ThreeDViews As ICollection(Of Autodesk.Revit.DB.Element) = coll.OfClass(GetType(ViewFamilyType)).ToElements()
            For Each VFT As ViewFamilyType In ThreeDViews
                If VFT.ViewFamily = ViewFamily.ThreeDimensional Then
                    Dim ThreeDView As ViewFamilyType = VFT
                    Dim view3D As View3D = view3D.CreateIsometric(doc, ThreeDView.Id)
                    view3D.Name = "Drew's QTO Export View"
                    tx.Commit()
                    tx.Dispose()
                    uidoc.ActiveView = view3D
                End If
            Next
        End Using

Seems to work, so now to figure out how to isolate a category in the new view.... anyone have an idea?

Creating New View's

I have decided that I want to be able to export from my model certain categories of object for use in QTO (Check out QTO if you are interested in the quantification of elements within your model).

In order to do this I think it would be easiest if I clicked a button and views were made that are 3D with a particular category isolated.

This seemed to be easy in my head, I had already found out how to generate a new view, for example to make a new drafting view and set it as the current view I used:


Public Sub CreateNewDraftingView(ViewName As String)
        Dim uidoc As UIDocument = IncomingCleanup.uiapp.ActiveUIDocument
        Dim doc As Document = uidoc.Document
        Using tx As New Transaction(doc)
            tx.Start("Create Drafting View")
            Dim myDraftingView As ViewDrafting = doc.Create.NewViewDrafting()
            myDraftingView.ViewName = ViewName
            tx.Commit()
            uidoc.ActiveView = myDraftingView
        End Using
    End Sub

(By the way, for anyone interested in how to get colour coded text, all I do is copy the text from Visual Studio into Word 2010 then copy that text into Blogger)

to run it I simply need to use:

CreateNewDraftingView("Drew's Magic View")

so I figured it would be just as easy to make a 3D view, however, it turns our that's not the case...


Dim my3DView As View3D = View3D.CreateIsometric(doc,.......

is kind of what I need, however I need the ViewFamilyTypeID of a 3D view and I am a little stumped on how to get that, especially if I dont have existing 3D views in the project


I am going to go figure out how to do it and report back with this and how to then isolate categories in the view prior to exporting to DWF. 

Thursday 19 July 2012

Using Forms in an Add-in

I decided to create a command that would allow me to walk through the worksets within a project, the idea being that a form can show a list of the worksets in the active document then as you pick one of them it will turn off the display of all other worksets in the active view. This will allow you to check quickly if objects are on the wrong workset, for example if you select Shared Levels and Grids and you can see walls in the view, then you have a problem!

I created my form with a listview box as you can see to the right, however I came across a problem when trying to access the Application within the code.

So far I have used the following to reference the application:

Public Function Execute(ByVal commandData As ExternalCommandData............
        Dim uiapp As UIApplication = commandData.Application

However in the form code there is no commandData, so I had to change the definition of the uiapp to a Shared item.

Public Class AdskCommand
    Implements IExternalCommand
    Public Shared uiapp As UIApplication
    Public Function Execute(ByVal commandData As ExternalCommandData,........
    uiapp = commandData.Application

This allows me to reference the uiapp in the Execute function when the command starts and also with this code:

Private Sub lstWorkSets_Click(sender As Object, e As System.EventArgs) Handles lstWorkSets.Click
    Dim uidoc As UIDocument = AdskCommand.uiapp.ActiveUIDocument

So this refers back to the Public Shared uiapp.
Notice that I have none of the correct terminology for what I am describing, I am hoping this will come with time and practise!
Here is a video of what I managed to make, a form that controls the visibility of the current Worksets in a project:

Tuesday 17 July 2012

Toggle a Display Category in the Active View


There was a question on on the AUGI forum regarding getting a keyboard shortcut to toggle the display of the Space Reference Category in a view, so I figured this would be a good learning experience for me. Its always best to have a goal to aim for with programming so I intended to create a simple add-in that could achieve the request and a keyboard shortcut could then be linked to the add-in.



Here is the code I created:

#Region "Imported Namespaces"
Imports Autodesk.Revit.DB
#End Region
<Transaction(TransactionMode.Manual)> Public Class AdskCommand
    Implements IExternalCommand
    Public Function Execute(ByVal commandData As ExternalCommandData, ByRef message As String, ByVal elements As ElementSet) As Result Implements IExternalCommand.Execute
        'Function to isolate a workset based on a selected element
        Dim uiapp As UIApplication = commandData.Application
        Dim uidoc As UIDocument = uiapp.ActiveUIDocument
        Dim doc As Document = uidoc.Document
        Dim Transaction As New Transaction(doc)
        Dim cat As Category = doc.Settings.Categories.Item(BuiltInCategory.OST_MEPSpaceReferenceVisibility)
        Transaction.Start("Toggle")
        If doc.ActiveView.getVisibility(cat) = False Then
            doc.ActiveView.setVisibility(cat, True)
        Else
            doc.ActiveView.setVisibility(cat, False)
        End If
        Transaction.Commit()
        'Must return some code
        Return Result.Succeeded
    End Function
End Class

So essentially all that happens is the Space Reference category is defined

Dim cat As Category = doc.Settings.Categories.Item(BuiltInCategory.OST_MEPSpaceReferenceVisibility)

then if it is currently on it is hidden, if it is hidden then it is turned on:

        If doc.ActiveView.getVisibility(cat) = False Then
            doc.ActiveView.setVisibility(cat, True)
        Else
            doc.ActiveView.setVisibility(cat, False)
        End If


Not much to it really, so I compiled it into a dll and add-in file that can be downloaded and tested.


Download the AddIn File
Download the DLL


Save the AddIn file to the correct location for your Revit installation and then save the dll to:


C:\Revit\Addins\2012\


you can change the location, but you will have to modify the addin file to point to the correct location.

Selecting an object and getting some data from it

As promised, here is my post regarding getting information from selected elements within Revit.
In order to get information from selected elements within the project, the 1st thing you need to do is select an object.
the following seems to work for that:


Imports Autodesk.Revit.UI.Selection

Dim uiapp As UIApplication = commandData.Application
Dim uidoc As UIDocument = uiapp.ActiveUIDocument
Dim sel As Selection = uidoc.Selection
Dim doc As Document = uidoc.Document

Dim elem As Element = doc.GetElement(sel.PickObject(ObjectType.Element))

You will be asked to select an element on the screen which will be saved as elem so that you can start to get information from it.

for example:

MsgBox("Category = " & elem.Category.Name.ToString)

will give you the category of the object you select in a message box (I am a big fan of message boxes as I don't really understand immediate windows, debugging and the like if I am honest).

I decided to try to make a command that would isolate a workset within a view based on the selection a user makes. Something like LayerIsolate in AutoCAD, so WorkSetIsolate it shall be.


So after selecting the element, next thing is to get the workset of the object, this involves accessing the WorkSetTable of the document that stores the names of the worksets.

Dim worksetTable As WorksetTable = doc.GetWorksetTable()
Dim worksetIdByElement As WorksetId = doc.GetWorksetId(elem.Id)
    If worksetIdByElement <> WorksetId.InvalidWorksetId Then
        Dim worksetByElement As Workset = worksetTable.GetWorkset(worksetIdByElement)
        Dim WorkSetName As String = worksetByElement.Name
    End If

I put a check in there to confirm that the worksetIdByElement was valid to hopefully avoid errors

So now we have the name of the workset of the selected element. Next thing is to use the visibility controls to turn off the visibility of all of the User-Created Worksets apart from the one we just found...

When we do this we will be editing the project file, therefore we need to start a transaction and then commit it at the end of our modifications in order that the changes happen.

I place:

Transaction.Start("Hide WorkSet")

just after my Dim's

In order to hide the selected workset the following does the work:

Dim visibility As WorksetVisibility = view.GetWorksetVisibility(worksetIdByElement)
    If visibility <> WorksetVisibility.Hidden Then
        view.SetWorksetVisibility(worksetIdByElement, WorksetVisibility.Hidden)
    End If

and then once out of the If I put in the commit to actually do the work defined in the transaction:

Transaction.Commit()

So at the end of it all I have this:

Dim uiapp As UIApplication = commandData.Application
Dim uidoc As UIDocument = uiapp.ActiveUIDocument
Dim sel As Selection = uidoc.Selection
Dim doc As Document = uidoc.Document
Dim view As View = uidoc.ActiveView
Dim elem As Element = doc.GetElement(sel.PickObject(ObjectType.Element))
Dim worksetTable As WorksetTable = doc.GetWorksetTable()
Dim worksetIdByElement As WorksetId = doc.GetWorksetId(elem.Id)
Dim Transaction As New Transaction(doc)
Transaction.Start("Hide WorkSet")
If worksetIdByElement <> WorksetId.InvalidWorksetId Then
    Dim worksetByElement As Workset = worksetTable.GetWorkset(worksetIdByElement)
    Dim WorkSetName As String = worksetByElement.Name
    Dim visibility As WorksetVisibility = view.GetWorksetVisibility(worksetIdByElement)
    If visibility <> WorksetVisibility.Hidden Then
view.SetWorksetVisibility(worksetIdByElement, WorksetVisibility.Hidden)
    End If
End If
Transaction.Commit()

This allows me to select an element on the screen and the workset of the selected element is hidden.

Next time I will look at a toggle to change a visibility setting and linking this to a keyboard shortcut.

Friday 6 July 2012

Getting Started

I have found there are great references out there regarding setting up your project from the start, I will try to remember to add some links at the bottom of the document, but here is my checklist of things to setup:

Application - Set the Assembly name to LearningAPI, this will help you reference it later.





References - You have to include references to RevitAPI.dll & RevitAPIUI.dll, the reason for this is that there are methods and such within these dll files that you will need, this will enable you to have access to Revit commands in your program. Within these references you should set the Copy Local property to False, this means that the Revit dll's will not be copied to your Build folder when you build the program.



  Debug - You should set the location of the program to use for testing, so set the external program to the Revit.exe of your choice, I have 7 to choose from on my machine, at least in 2013 it is a single application per year!

Adding the Revit Application to the Debug page















Compile - Point the output path to a known location











You should also click Build Events... In the Build Events dialog box paste the following code into Post-build event command line:

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

This will ensure that the addin sml file you create is copied to the program's addin folder


Add a Class - Right click in the Solution Browser and create a new Class via add. Select Class from the dialog that opens:






and Name this class AdskApplication.vb. repeat the process to create AdskCommand.vb.

Add a XML Module - In order to get the command to autoload you have to define an addin file, this is an xml file with certain properties, right click in the Solution Browser and create a new Module,







in the dialog box select XML File and name this LearningAPI.addin

Add some code - You then need to put a bunch of code into the Classes/Modules so that they can do something when required. Put the following into AdskApplication:


Imports Autodesk.Revit.ApplicationServices
Imports Autodesk.Revit.Attributes
Imports Autodesk.Revit.DB
Imports Autodesk.Revit.UI
Class AdskApplication
        Implements IExternalApplication
        Public Function OnStartup(ByVal application As UIControlledApplication) As Result Implements IExternalApplication.OnStartup
            'TODO put some code here
            'Must return some code
            Return Result.Succeeded
        End Function
        Public Function OnShutdown(ByVal application As UIControlledApplication) As Result Implements IExternalApplication.OnShutdown
            'TODO: Add your code here
            'Must return some code
            Return Result.Succeeded
        End Function
    End Class



Put the following into AdskCommand:


Imports System
Imports System.Collections.Generic
Imports Autodesk.Revit.ApplicationServices
Imports Autodesk.Revit.Attributes
Imports Autodesk.Revit.DB
Imports Autodesk.Revit.UI
Imports Autodesk.Revit.UI.Selection
&lt;Transaction(TransactionMode.Manual)&gt; Public Class AdskCommand
   Implements IExternalCommand
    Public Function Execute(ByVal commandData As ExternalCommandData, ByRef message As String, ByVal elements As ElementSet) As Result Implements IExternalCommand.Execute
        Dim uiapp As UIApplication = commandData.Application
        Dim uidoc As UIDocument = uiapp.ActiveUIDocument
        Dim app As Application = uiapp.Application
        Dim doc As Document = uidoc.Document
        Dim sel As Selection = uidoc.Selection
        'TODO put code in here
        'Must return some code
        Return Result.Succeeded
    End Function
End Class


Put the following into LearningAPI.addin:


<?xml version="1.0" encoding="utf-8"?>
<RevitAddIns>
  <AddIn Type="Command">
    <Text>LearningAPI</Text>
    <Description>Some description for LearningAPI</Description>
    <Assembly>LOCATION DEFINED IN COMPILE OUTPUT PATH\LearningAPI.dll</Assembly>
    <FullClassName>LearningAPI.AdskCommand</FullClassName>
    <ClientId>a6518a22-3138-433e-8ce0-144bd12fd520</ClientId>
    <VendorId>TBC_</VendorId>
    <VendorDescription>Drew's Revit Add-Ins</VendorDescription>
  </AddIn>
  <AddIn Type="Application">
    <Name>Application LearningAPI</Name>
    <Assembly>LOCATION DEFINED IN COMPILE OUTPUT PATH\LearningAPI.dll</Assembly>
    <ClientId>aaeff130-2e2d-40dc-85e3-f4029cc65d04</ClientId>
    <FullClassName>LearningAPI.AdskApplication</FullClassName>
    <VendorId>TBC_</VendorId>
    <VendorDescription>Drew's Revit Add-Ins</VendorDescription>
  </AddIn>
</RevitAddIns>



Add Hello World - replace the text 'TODO put code in here within AdskCommand with the following:

Msgbox("Hello World")

Build the program, then open Revit and run the addinn from the Add-Ins Ribbon -> External Tools, let me know what I messed up and I will improve the page!

Next time.... Selecting an object and getting some data from it...








Introduction

I intend to post my experiences with learning the Revit API using vb.Net only. I find a lot of information out there with c# as the examples and get very frustrated, so I figured something that shows only vb.Net would be of benefit to people like me who have come from a VBA background.

I will also use it as a simple place to store my code as I do it, meaning I can access it anywhere.

I started with AutoCAD scripts, macros, Lisp and then VBA back in the last century. However this year I was tasked with converting some code from VBA to vb.Net for AutoCAD so I learnt vb.Net.

I have in the past tried to get into the Revit API, but never had the time to do it properly, any time I tried I got very frustrated and moved onto other things, however with my new found knowledge of vb for AutoCAD I am hoping this is the time.

If however this blog stops after 1 or 3 posts then you know it didn't work this time either!!