Moving to a new place

Thank you, everyone, for following my blog.

As I am beginning to move to more serious endeavors, especially in establishing new working grounds, I am not continuing to post on this blog, and eventually close this within six months.

However, all the posts I made here were already migrated to this new blog of mine, and have new posts as well:

https://mathcadbimthingys.technikstudio.com

And I will be posting new things on the above blog. So if you have any questions regarding the contents of the posts and other related matters, please do so in the above blog.

Meanwhile, I will still be answering unanswered comments before I close this blog, so please wait for the reply (it will be a long wait though, sorry for that).

Lastly, let me take this opportunity to thank you all who read and follow this blog. I was delighted to know that my posts were of great help in your Revit API programming journey.

See you at technikstudio.com.

Advertisement

Revit classes and WPF? The first M in MVVM

I have been receiving inquiries on my blog regarding the use of Revit classes inside the WPF window, because I mentioned that we must not use the Revit API classes inside our view models, the reason being the Revit classes obstruct the use of view-view model functionality during the design of our WPF windows. So how are we going to use these functions/classes especially in Revit’s Document class when the WPF window is active?

This is now the turn of the first M in the acronym MVVM, which is the model.

We represent some of our Revit classes in a model that we will call here the Revit model.

Inside this Revit model, the UIApplication, UIDocument, and Document classes are represented by their respective variables, which are made private. These variables shall be initialized inside the model’s constructor, which has an argument of a UIApplication class. We name this modelRevitBridge.cs.

Below is a sample of it.

    class modelRevitBridge
    {
        // Just like what you do when creating a Revit command, declare the necessary variable such as below.
        private UIApplication UIAPP = null;
        private Application APP = null;
        private UIDocument UIDOC = null;
        private Document DOC = null;

        // The model constructor. Include a UIApplication argument and do all the assignments here.
        public modelRevitBridge(UIApplication uiapp)
        {
            UIAPP = uiapp;
            APP = UIAPP.Application;
            UIDOC = UIAPP.ActiveUIDocument;
            DOC = UIDOC.Document;
        }

        // This function will be called by the Action function in the view model, so it must be public.
        public List<string> GenerateParametersAndValues(int idIntegerValue)
        {
            List<string> resstr = new List();

            Element el = DOC.GetElement(new ElementId(idIntegerValue));
            if (el != null)
            {
                foreach (Parameter prm in el.Parameters)
                {
                    string str = prm.Definition.Name;
                    str += " : ";
                    str += prm.AsValueString();

                    resstr.Add(str);
                }
            }

            return resstr.OrderBy(x => x).ToList();
        }
    }

Below is the view model that we integrate with the view later. Name this as viewmodelRevitBridge.cs. This class has a Dictionary that holds all the wall types’ name and id in integer value. It also has a variable of type int that holds the selected value from the combo box, as well as a list of string declared as an ObservableCollection where we save all the parameter information of the selected wall type. Also, we create a pair of command variable and action function to be connected to the button that will generate the parameters in strings.

        private Dictionary<string, int> _dicWallType;
        private int _selectedWallType;
        private ObservableCollection<string> _listParameters;

        // Declare the Revit model class here.
        // Consequently, create a get-set variable representing this.
        private modelRevitBridge _revitModel;

        public Dictionary DicWallType
        {
            get
            {
                return _dicWallType;
            }

            set
            {
                SetProperty(ref _dicWallType, value);
            }
        }

        public int SelectedWallType
        {
            get
            {
                return _selectedWallType;
            }

            set
            {
                SetProperty(ref _selectedWallType, value);
            }
        }

        public ObservableCollection<string> ListParameters
        {
            get
            {
                return _listParameters;
            }

            set
            {
                SetProperty(ref _listParameters, value);
            }
        }

        //  Commands
        // This will be used by the button in the WPF window.
        public ICommand RetrieveParametersValuesCommand
        {
            get
            {
                return new DelegateCommand(RetrieveParametersValuesAction);
            }
        }

        // The get-set variable
        internal modelRevitBridge RevitModel
        {
            get
            {
                return _revitModel;
            }

            set
            {
                _revitModel = value;
            }
        }

        // The action function for RetrieveParametersValuesCommand
        private void RetrieveParametersValuesAction()
        {
            if (SelectedWallType != -1)
            {
                ListParameters = new ObservableCollection(RevitModel.GenerateParametersAndValues(SelectedWallType));
            }
        }

        // Constructor
        public viewmodelRevitBridge()
        {

        }

The following, on the other hand, is a sample of a view of our sample project, coded in XAML. Let us name this viewRevitBridge.xaml. This view will have a combo box and a list box.

Edit the corresponding xaml.cs file as well so that we make this window disposable and we hide the minimize and maximize button.

<Window x:Class="RevitBridgeSample.viewRevitBridge"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:RevitBridgeSample"
        mc:Ignorable="d"
        Title="Revit Bridge Sample" Height="456" Width="279" ResizeMode="NoResize" WindowStartupLocation="CenterScreen" ShowInTaskbar="False" SourceInitialized="Window_SourceInitialized">
    <Window.DataContext>
        <local:RevitBridgeViewModel/>
    </Window.DataContext>
    <Grid>
        <ComboBox x:Name="comboBox" Height="35" Margin="10,10,10,0" VerticalAlignment="Top" ItemsSource="{Binding DicWallType}" DisplayMemberPath="Key" SelectedValuePath="Value" SelectedValue="{Binding SelectedWallType}"/>
        <ListBox x:Name="listBox" Margin="10,95,10,77" ItemsSource="{Binding ListParameters}"/>
        <Button x:Name="bOk" Content="OK" HorizontalAlignment="Right" Height="37" Margin="0,0,93,10" VerticalAlignment="Bottom" Width="105" IsDefault="True" Click="bOk_Click"/>
        <Button x:Name="bCan" Content="Cancel" HorizontalAlignment="Right" Height="27" Margin="0,0,10,10" VerticalAlignment="Bottom" Width="78" IsCancel="True"/>
        <Button x:Name="bProp" Content="Retrieve Parameters and values" Height="30" Margin="10,50,10,0" VerticalAlignment="Top" Command="{Binding RetrieveParametersValuesCommand, Mode=OneWay}"/>
    </Grid>
</Window>

Lastly, this will be the sample code for the Revit command, which will run the WPF window.

class RevitBridgeCommand : IExternalCommand
{
   public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
   {
       UIApplication uiapp = commandData.Application;
       UIDocument uidoc = uiapp.ActiveUIDocument;
       Application app = uiapp.Application;
       Document doc = uidoc.Document;

       try
       {
           // Get all the wall types in the current project and convert them in a Dictionary.
           FilteredElementCollector felc = new FilteredElementCollector(doc).OfClass(typeof(WallType));
           Dictionary<string, int> dicwtypes = felc.Cast().ToDictionary(x => x.Name, y => y.Id.IntegerValue);
           felc.Dispose();

           // Create a view model that will be associated to the DataContext of the view.
           viewmodelRevitBridge vmod = new viewmodelRevitBridge();
           vmod.DicWallType = dicwtypes;
           vmod.SelectedWallType = dicwtypes.First().Value;

           // Create a new Revit model and assign it to the Revit model variable in the view model.
           vmod.RevitModel = new modelRevitBridge(uiapp);

           System.Diagnostics.Process proc = System.Diagnostics.Process.GetCurrentProcess();

           // Load the WPF window viewRevitbridge.
           using (viewRevitBridge view = new viewRevitBridge())
           {
               System.Windows.Interop.WindowInteropHelper helper = new System.Windows.Interop.WindowInteropHelper(view);
               helper.Owner = proc.MainWindowHandle;

               // Assign the view model to the DataContext of the view.
               view.DataContext = vmod;

               if (view.ShowDialog() != true)
               {
                   return Result.Cancelled;
               }
           }

          return Result.Succeeded;
       }
       catch (Exception ex)
       {
           message = ex.Message;
           return Result.Failed;
       }
   }
}

Running this code will turn out like this:

The image below will be the initial screen, which gets all the wall types inside your Revit project.

rmw01

When you press the “Retrieve Parameters and values” button, all the parameters and their values of the selected wall type will be listed up inside the listbox.

rmw02

You can also create a function that writes something in your Revit project, like editing parameters of renaming the wall types. In this case though, we have to wrap the writing part of the code inside a Transaction class, or else the program will fail.
When you do this, every time you execute the writing function, it registers a command in the undo-redo mechanism. So if you want to run this function multiple times and want to undo or redo these process just once, you need to wrap the WPF window loading inside a TransactionGroup and just before the end of it, use the TransactionGroup.Assimilate() function.
 

Using Celery.2DGraphItemSelector in Dynamo

celery.2dgraphitemselector

Since I cannot produce a YouTube tutorial on how to use Celery’s 2DGraphItemSelector due to some hindrances in my surroundings, I rather post a blog to explain its usage. Sorry to keep you waiting for too long.

Anyway, as you can see in this node, there are two inputs and two outputs. The first of the two inputs expects a two-dimensional list of numbers; that means a list of lists of numbers. The second input is a list of string as headers that will be shown in the node through the two combo boxes. The number of lists connected to the input must the equal to the number of headers, and the headers must match the corresponding list of numbers they represent.

In this example I created three sets of numbers generated by number series. The first set is a simple sequence of numbers from 1 to 20, the second set contains the values of the equation x^2 where x is the set of numbers from the first set. The last set is the logarithmic values of the numbers in the first set to base 10.

So we also need to create a list of three headers for these sets of numbers. Let us name them “linear”, “squared”, and “log10”.

2dgraphitemselector

Connect this number series to the number[][] input with the help of List.Create node, and the headers to the header[] input.

Once everything is connected, the graph automatically generates graphical points, each representing the set of numbers at each assigned index, and their positions in the graph will depend on the headers set in both X-axis and Y-axis. (As of this writing I discovered a bug wherein the combo box does not respond very well in Dynamo Studio, so I will be posting an update of Celery soon.)

The graph will also determine the minimum and maximum values in each axis, and it shall become the bounds for each axis. They will also be shown in the graph for reference. Also, try to change the X-axis and Y-axis headers using the combo boxes at the bottom part of the node. The graph should respond to the selected headers as well as the limits and axis headers.

2dgrapthitemsel-2.PNG

To know what are the values that each point represents in this 2D graph, hover over a point in the graph, and to get its values into the outputs, just click on the point, and the output ports will obtain the X and Y values each. These values shall be used for further checking and more extensive operations succeeding this node.

The Best Application

Now, the bottom-line of the usage of this node is, where is it applied?

If you are familiar with the Optimo package in Dynamo, the NSGA II node has an output of two-dimensional list of numbers, but we have to do something in order to pick the better values we need from this result. While it is obvious that the first items represent the best answer the NSGA II node offers, oftentimes we need options, and those options will come from another set of values coming from the NSGA II node. So how would we know easily what values will satisfy the criteria we have? Here is where 2DGraphItemSelector comes in.

If you have Optimo installed, try to open Optimization Example-02-SCH.dyn file from the Optimo example files.

Create a new Celery.2DGraphItemSelector node and position it just after the code block node that has an a[1] value in it (after the LoopWhile node). Try to connect this node to the NSGA II node in one of the existing samples from Optimo, and add a code block that contains a list of three strings (in this example, it is “X”, “Y” and “Z”). Connect the code block of numbers and headers to the proper inputs, and see for yourself.

2dgrapthitemsel-2

Be noted, however, that when reinstating the solution to a custom node (that represents an objective) that produces geometry or model changes in Revit, you need to add a new node of that objective and connect the outX and outY ports to the missing inputs of the objective node. This will eventually reveal the result of the model whenever you select a point from the graph node.

View-ViewModel-ing for your WPF Windows (Revit API with WPF Series 3/3)

This is a long overdued post. Apologies for the very long wait. If I would be allowed to make an excuse, I became busy working on a Dynamo package named Celery, which is still on a pre-beta release due to much time dedicated to my company work and physical workouts.

I will still process some of the codes and images that this post needs. So please stay tuned for the update.

Anyway, let’s go to the real topic.

In this part of the series, we will tackle how to associate values between the plugin class and the view model class, and between the view model class and the WPF window.

First, create some controls within the window like the one I did below.

RevitAPIWPF3-01
We have a combo box, three radio buttons, a text box, two checkboxes, and an OK button. These are some of the controls that are frequently used in windows. We need to associate some variables to the each of the controls, except for the radio buttons, which shares one variable.
To associate them, we need to create a definition class that we will call the view model class.

Before creating a view model class, let us download first the “Prism” library from nu-get. We will be using the Prism library as our base library for our MVVM classes.

Now, we create a new class file for our new view model class. Let’s name it “MyViewModel.cs”. Open the newly created file and modify the file so that it inherits function we need for class to become a view model class:

using Prism.MVVM;

namespace MyRevitWPFProgram
{
class vmodThisWindow : BindableBase
{

}
}

The BindableBase base class is just a simplified class derived from INotifyPropertyChanged interface class, and has the event declaration and function already contained in it.

Now we create the variables we need. All the controls have their corresponding types of variable so we must know what to assign to which. When gathering a string of text from a TextBox, we need a string variable. When we need a number for a TextBox, we need a number variable with a string converter. When we need something to monitor if an item is checked or not, a boolean for each of them, or an integer with converter and parameters for radio buttons. WPF beginners can get more details regarding these on the internet.
But one thing I would like to recommend: DO NOT USE ANY OF THE REVIT CLASSES WITHIN THE VIEW MODEL. Using these classes will not let you use the capabilities of WPF automation functions in Visual Studio, and eventually the window will not display in the Design Screen disabling you to modify the contents of your window graphically. I have just verified this even very lately with the Visual Studio Community 2015.

So I declare my variables for my window something like below.

 

private ObservableCollection<string> _StringSelection;
private string _selectedString;
private int _radioOption;
private bool _isSelectedOption3;
private bool _isSelectedAddMe;
private bool _isSelectedAddMeToo;
private Dictionary<string, int> _dicIdString;

public ObservableCollection<string> StringSelection
{
get
{
return _StringSelection;
}

set
{
_StringSelection = value;
}
}

public string SelectedString
{
get
{
return _selectedString;
}

set
{
_selectedString = value;
RaisePropertyChanged("SelectedString");
}
}

public int RadioOption
{
get
{
return _radioOption;
}

set
{
_radioOption = value;
RaisePropertyChanged("RadioOption");
}
}

public bool IsSelectedOption3
{
get
{
return _isSelectedOption3;
}

set
{
_isSelectedOption3 = value;
RaisePropertyChanged("IsSelectedOption3");
}
}

public bool IsSelectedAddMe
{
get
{
return _isSelectedAddMe;
}

set
{
_isSelectedAddMe = value;
RaisePropertyChanged("IsSelectedAddMe");
}
}

public bool IsSelectedAddMeToo
{
get
{
return _isSelectedAddMeToo;
}

set
{
_isSelectedAddMeToo = value;
RaisePropertyChanged("IsSelectedAddMeToo");
}
}

public Dictionary<string, int> DicIdString
{
get
{
return _dicIdString;
}

set
{
_dicIdString = value;
}
}

 

Here are the major tips that I would suggest when declaring variables in creating a view model class:

a. ) When you have a combo box in your window, know its purpose. For example, if you want to put data of some of the family types and select only one of them, use a Dictionary class and assign the names as keys, and the IntegerValue of the Ids as values. However, if the content seem to be complex (e.g. materials with name, id and color as you primary info), create a unique class for that content and include only the variables that you need. In this case, you may use either the simple List<> class or a more sophisticated IObservableCollection<> class (but since most of the time the contents of the combo box do not change, List<> will be enough).
When you create a Dictionary, never forget to input the word “Key” in the DisplayMemberPath property, and the word “Value” in the SelectedValuePath property. This will make the combo box propagate only the keys to show in the combo box, and when one is selected, return its corresponding integervalue of the id.
On the other hand, when using list of classes, use the Binding procedure to bind a class item to the DisplayMemberPath what to show in the combo box, and another class item to the SelectedValuePath property.
On both cases, bind a variable to the combo box’s SelectedValue property, with the type the same as what you bind in the SelectedValuePath property. This variable must be independent, and you may not need to add an RaisePropertyChanged() function to it.

b.) When declaring variable with get-set and takes part of the user input handling from any of the controls inside the window, always call the RaisePropertyChanged(“<name of the public variable>”) on the last part of the ‘set’ segment.

c.) Do not include arguments in the constructor. Do not also create overloads of the constructor. Do all the variable assignments after creating the view model class.

 

Automating the Binding Procedure
When all the variables are declared, COMPILE FIRST THE PROGRAM AND MAKE SURE IT IS FREE OF ERRORS. Otherwise, you cannot use the interactive approach of binding contexts to the WPF window.

Now we will use this interactive approach to bind the view model class to our XAML window.

Select the window frame in the design view of the XAML, and from the Properties window, go to the DataContext property and click the “New” button adjacent to it. This will bring you to the window further below.

RevitAPIWPF3-03
Select “MyViewModel” class from the nested list and press OK. This will automatically integrate your view model to your WPF window.

RevitAPIWPF3-04

RevitAPIWPF3-05

In the image above, you can verify that Visual Studio made us bind our view model class to the WPF window by enclosing our view model class with the <Window.DataContext>.

 

Next we bind our variables to the window controls the similar way.

First, select the TextBox from the design view, and from the properties, click on the small box on the right (which is shaded black; it indicates it has a set value).

RevitAPIWPF3-06

 

Then from the context menu, select “Create Data Binding…”

RevitAPIWPF3-02

This will bring you to another window like the one below.

Make sure the Binding Type is set to Data context, so that you can see the view model class we created on the right side of the window. Expand MyViewModel, and there comes all the variables we declared. Select the SelectedString : (String) and press OK.

RevitAPIWPF3-07

Now we can check from the XAML code if it made us the binding. It should be like I have below. You can see that in the Text property of the TextBox SelectedString is bound to it.

RevitAPIWPF3-08

 

 

Binding the Radio Buttons

 

One complicated area here is binding three radio buttons in a single variable that is RadioOption. We will be using a converter and integrate converter parameters in each of the radio buttons.

Below is how I wrote my Converter class. I created a new class named MyConverterRadioButton, which is derived from the IValueConverter interface.

 

public class MyConverterRadioButton : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (parameter.ToString() == value.ToString());
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? parameter : Binding.DoNothing;
}
}

 

Since we are using the automation features in the Properties window of Visual Studio, we will associate the converter class to all the radio buttons.

In the properties window, click the square on the right of the IsChecked property to reveal the window below. On the right hand side of the window, selected RadioOption variable (1), and then pull down the combo box in the Converter option below (2), and then select “<Add value converter…>” (3)

RevitAPIWPF3-09

This will bring you to a child window like the one below. If the converter class you created appears (in this case, MyConverterRadioButton), then select the class and press OK. Otherwise, revert and compile the program first before returning to this window.

RevitAPIWPF3-10

When the converter class is selected, we can now assign a converter parameter in each of the radio buttons using the previous window. In the ConverterParameter field, enter “0” for the first radio button, “1” for the second, and “2” for the third one (4). Press OK when done (5). The result will reflect in the XAML code.

RevitAPIWPF3-11

Next we associate the text box to be enabled for editing once the third radio button is selected. Select the Text Box of the window, and in the Properties panel, press the square on the right side of the IsEnabled property. Then from the context menu, select Create Data Binding.

RevitAPIWPF3-12

From the window, select the variable IsSelectedOption3 (1) and press OK (2).

RevitAPIWPF3-13

Then we will just update our view model class with the code below inside the RadioOption set function in order to activate it every time the third radio button is selected, and deactivated otherwise.

 

public int RadioOption
{
get
{
return _radioOption;
}

set
{
_radioOption = value;

IsSelectedOption3 = value == 2;

RaisePropertyChanged(“RadioOption”);
}
}

 

Binding the Checkboxes

 

Binding the Checkboxes is straightforward as the method is similar with others mentioned above. But this time, we have to click the square of the IsChecked properties of the two checkboxes, and create the data bindings. Use IsSelectedAddMe and IsSelectedAddMeToo correspondingly.

RevitAPIWPF3-14

 

Binding a Button Command

 

Although this area does not play a significant role in our example, it is essential to learn how to bind them and how they function when the program is executed.

Add this code inside the view model class and re-compile the program.

 

#region Commands and Actions
public ICommand OKCommand
{
get
{
return new DelegateCommand(OKAction);
}
}

private void OKAction()
{
// This function will be executed after the button events.
}
#endregion

 

We simply give an action to the OK button whenever it is pressed. To give the event an action compatible with MVVM, first we create a variable OKCommand out of an ICommand class. Then inside its get function, the code just returns a new declaration of DelegateCommand that has an argument of OKAction, which will be a void function to be invoked. DelegateCommand is a function from Prism, so be sure Prism is installed in your project to be able to use it.

Once the command is called, the function invoked will execute, however, as mentioned in the comment above, OKAction will be executed after its event when pressed. So for example, you make an event from the Button named OKButton_Click(). The code inside OKButton_Click() will be executed first, before OKAction(). You can always verify it by debugging the program and see for yourself.

Now you know the sequence of the execution of the command events defined by the usual events and MVVM events, plan the program into its importance; what must come ahead and behind, or maybe, the necessity of each other. In this case, since the OK button is the only button, we may not add code inside the OKAction, but when it comes to other buttons, this trick may come handier.

Now we finished defining our Command events for our button. Compile it and see if there are errors. If there are none, let us bind it from the Properties window.

In the Miscellaneous group of the Properties window, there is an item there named Command. This item accepts any variable of type ICommand, which will be made clear further below. Click the square on its right and from the context menu, select Create Data Binding.

RevitAPIWPF3-15

In the window, select the OKCommand variable (1) and press OK (2). If you notice, OKCommand is variable of ICommand, which is recognized by WPF.

RevitAPIWPF3-16

 

…to be continued.

Essential items for creating a WPF window for Revit API plugins (Revit API+WPF Series 2/3)

In this second part of the series, we will discuss essential items to apply when creating WPF windows.

In order to proceed, make sure your plugin project is capable of creating WPF windows. If not, you better create a project template with WPF functionality and create another new project. Then create a WPF window. In this case, make a window and name it viewThisWindow.xaml.

To make a WPF window work on its stable condition inside Revit, the following minimum recommendations are to be followed.

The window must be disposable

WPF windows are not disposable by default, so I recommend every WPF window be disposable. To do that, in the viewThisWindow.xaml.cs, set this on the line of the class declaration:

public partial class viewThisWindow : Window, IDisposable

 

The IDisposable interface will enable us to use the using() statement later on.
Implement the IDisposable interface to be able to create the Dispose() function inside the class, then input this line inside the function:

public void Dispose()
{
     this.Close();
}

 

This will force the window to close when the using() statement has done its work, especially when the operation is cancelled.

 

The window does not need maximize and minimize buttons

When working on a modal window, for most instances, it must only have an executing button (i.e. OK button) and a canceling button (i.e. Exit button on the upper right hand side of the window), and the minimize and maximize buttons are disabled.

Create a button inside the window and label it “OK”. Then make this button the default button by ticking the button’s IsDefault property from the properties window.

Addendum: After creating the OK button, double-click it to create an event, and in the event function that was created (somewhat like Button1_Click()), insert this line:

this.DialogResult = true;

This will help closing the window and tell the program that the window is successfully closed.

To disable the minimize and maximize buttons, first copy the following code and paste it inside the viewThisWindow class above the viewThisWindow() constructor.

[DllImport("user32.dll")]
internal extern static int SetWindowLong(IntPtr hwnd, int index, int value);

[DllImport("user32.dll")]
internal extern static int GetWindowLong(IntPtr hwnd, int index);

internal static void HideMinimizeAndMaximizeButtons(Window window)
{
      const int GWL_STYLE = -16;

      IntPtr hwnd = new System.Windows.Interop.WindowInteropHelper(window).Handle;
      long value = GetWindowLong(hwnd, GWL_STYLE);

      SetWindowLong(hwnd, GWL_STYLE, (int)(value & -131073 & -65537));
}

 

Then in order for this to work, create a SourceInitialized() event function.
To create this event function, select first the window itself in the XAML design window, then in the Properties window, click on the Event Handler window (the lightning-like icon located on the upper right). Go to the SourceInitialized event and double-click on the space adjacent to it. It will automatically create an event function with the _SourceInitialized inside the .xaml.cs file.

Inside this function insert the following line:

HideMinimizeAndMaximizeButtons(this);

 

This will hide the minimize and maximize buttons before the window shows up on the screen.

Note: There is another event function that is Loaded() but SourceInitialized() event function runs in between the constructor and the Loaded() function. Loaded() is dedicated to other things when the window is loaded and it is not the best location to execute HideMinimizeAndMaximizeButtons().

 

It must be always on top of the Revit application

To be able to be on top of the Revit application, we must also insert these lines to our plugin class:

using (viewThisWindow view = new viewThisWindow())
 {
      // the following two lines let the Revit application be this window's owner, so that it will always be on top of the Revit screen even when
      // the user tries to switch the current screen.
      System.Windows.Interop.WindowInteropHelper helper = new System.Windows.Interop.WindowInteropHelper(view);
      helper.Owner = proc .MainWindowHandle;
     ...
 }

 

Notice that we just used the using() method here. As you all know, using() can only be used when the class that you want to create is disposable; that is, the class is derived from the IDisposable interface and there is a Dispose() method inside that class. Anything done inside the Dispose() function of the class will be executed once using() has done its job.

And as an option, we un-tick the ShowInTaskbar property so that the icon of this window won’t show up in the taskbar.

Alternatively, you may just use these lines when you are loading just one WPF window inside your plug-in.

// System.Diagnostics.Process proc = System.Diagnostics.Process.GetCurrentProcess();

using (viewThisWindow view = new viewThisWindow())
 {
      System.Windows.Interop.WindowInteropHelper helper = new System.Windows.Interop.WindowInteropHelper(view);
      helper.Owner = System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle;
     ...
}

Create Project and Item Templates in Visual Studio (Revit API+WPF Series 1/3)

In this three-part series, I will be tackling about proper basic integration of Windows Presentation Foundation (WPF) in your Revit API plug-ins using C# .NET.

For the first part, we will discuss creating project and item templates that we will use to create new projects with WPF functionality. For the item template, however, the objective is to be able to create plug-in executable classes without repeatedly creating lines common to your coding workflow.

 

Creating a Project Template

Visual Studio does not provide a class library project template that we can use for our WOF-ed plugins. So we need to create it. We will be starting to create a project template from a WPF application.

1.) From the File menu, create a new project. On the left panel, select either C# or Visual Basic, and from the center panel, select WPF Application (see figure below). 

Name your project appropriately, (in my case, it’s Revit API with WPF), and then click the OK button.

revitapiwpf1-1

Initially, the new project is created like the figure below. There are files created here that we really do not need, so we have to remove them.

revitapiwpf1-2

2.) Delete the files MainWindow.xaml, App.xaml and App.config. All subsequent files will be deleted automatically.

revitapiwpf1-3

3.) Let us add the reference files we need for creating new Revit API plugins.

From the Solution Explorer window, right-click on References, and click Add Reference…

revitapiwpf1-4

4.) The Reference Manager window as shown below will open. Click on the Browse… button.

revitapiwpf1-5

5.) Browse to your Revit application location, and select the files RevitAPI.dll and RevitAPIUI.dll. Click on the Add button when done. Then on the Reference Manager window, click OK.

revitapiwpf1-6

Try to expand the References item from the Solution Explorer window, and we can confirm that the files were added.

Everytime we create a new Revit plugin project, these files will be included in our project. But most of the time, these files post a warning saying that their location is not found.

To enable the location of these files to be always found, let us proceed to the next step.

revitapiwpf1-7

6.) In the Solution Explorer window, right-click on the project name, and select Properties… and in the properties window, select the Reference Paths item.

revitapiwpf1-9

7.) Click the ellipsis button on the upper right hand of the window, and browse for the folder where the reference files we just added are located.

revitapiwpf1-10

Click the Add Folder button. When we add the folder, Visual Studio will recognize the location of the reference files, and the warning icon will not show up anymore.

In addition to this, whenever we update these reference files to newer versions, we will just re-locate the folder (if in case we update the files to a version say Revit 2018, we can just change the text 2017 to 2018 and click the Update button).

revitapiwpf1-11

Let us set the output type of this project template. Since we are creating a project template which is a class library, let us set it to a class library.

8.) In the same window, switch to the Application tab. Set the Output Type to Class Library.

Close the window if you wish.

revitapiwpf1-12

9.) Compile the project. The compile must be successful.

Next, we need to change one property from the project, but we will not do this inside Visual Studio.

10.) Open Windows Explorer and locate the project. Right-click on the project file (the one with a .csproj extension), and open the file with Notepad or any text editor you have (mine is with Notepad++).

revitapiwpf1-13

11.) From the text editor, look for the line with a <AssemblyName> entry. The entry must be the same as the name of our project.

revitapiwpf1-14

Change the name with $safeprojectname$.

We do this so that whenever we create a new project, the assembly name will always be the name of our project. If we do not, the assembly name will always be the name of the project template.

revitapiwpf1-15

12.) Save the file in your text editor.

When we do that, Visual Studio will prompt like the window show below.

Click on Reload to reload and update the project file.

revitapiwpf1-16

Now the necessary basic settings are done, we can already export the project to a template.

13.) From the File menu, select Export Template…

revitapiwpf1-17

14.) Make sure the template type is set to Project Template. Then click the Next> button.

revitapiwpf1-18

15.) In the Select Template Options window, give an appropriate template name and template description.

Optionally, you can attach icon and preview images for the template.

Leave all other options as is.

revitapiwpf1-19

16.) Click on the Finish button to create the project template.

The File Explorer will open and show us fhe location of the created template. Close this window when done.

revitapiwpf1-20

17.) Try to create a project using the project we just created.

Create a new project from the File menu, and from the template panel, check if the project template is included. If included, select the template and press the OK button.

revitapiwpf1-21

18.) Look at the Solution Explorer window and check the reference files are added and without warnings.

Also, try to open the Properties window of the project, and from the Applications tab, check if the assembly name is the name of our project name.

When things gone well, we need to check one more thing.

Let us try to add a WPF window.

revitapiwpf1-22

19.) Right-click on the project name in the Solution Explorer, and then from the Add item, select Window…

revitapiwpf1-23

20.) The template panel must show templates with the (WPF) text.

Select Window (WPF), give it a name, and press the Add button.

revitapiwpf1-24

Now our template is also capable of creating WPF windows.

revitapiwpf1-25

 

Creating an Item Template

Although this has nothing to do with WPF, we take advantage of this procedure because we also need to create new plugin classes compliant with our coding workflow.

1.) Reopen the project template project we created earlier.

2.) Right-click on the project name from the Solution Explorer. From the Add item, select New Item…

revitapiwpf1-26

3.) From the middle panel, select Class. Give the new class an appropriate file name, and click on the Add button.

revitapiwpf1-27

3.) Copy the code on the figure below.

To reduce the typing effort, we do the following sub-steps.

Since the class derives from IExternalCommand interface class, input IExternalCommand after the class name separated by a comma. Press Ctrl+. (period) and select Autodesk.Revit.UI to add the reference class.

Right-click over the IExternalCommand base class, and from the context menu, implement the IExternalCommand interface.

This procedure will add the Execute function inside the class.

In the Execute function, add the other set of code as shown below. Also, add the other lines with the using statement and the Transaction attribute above the class declaration.

The code inside the Execute function may differ to your liking.

revitapiwpf1-28

4.) Let us change the class name to comply with the template settings. Select the class name and replace it with $safeitemname$ as shown below.

revitapiwpf1-29

revitapiwpf1-30

5.) Save the file. Then from the File menu, select Export Template…

6.) From the window, select Item Template, then click Next >.

revitapiwpf1-31

7.) In the Select Item to Export window, check on the file we just created. In this case, the Revit API Executable.cs file.

Click Next > afterwards.

revitapiwpf1-32

8.) In the next window, we can add reference files necessary for our item template. Since we do not need any, just click Next >.

revitapiwpf1-33

9.) In the next window, enter the appropriate template name, description and images if needed.

Click the Finish button when done.

revitapiwpf1-34

Let us see if the item template will appear in the item templates list.

Before we proceed, I recommend to re-open Visual Studio in order for the new item template to be loaded properly.

10.) Open the test project we created earlier. Try to add a new item.

Once the window is opened, we can see that our item template is already loaded in Visual Studio.

Select the template, give an appropriate file name, and click Add.

revitapiwpf1-36

The new class file will contain all the code we entered during the creation. Also, we can see that the class name is the name of the file.

revitapiwpf1-37

 

So there you have it.

If you have any questions, feedback or comments regarding this post, please make sure to post them in the comment section below.

Celery: A new package for Dynamo

Just recently I published a new set of Dynamo nodes into a package called Celery.

While Rutabaga is a package of various useful nodes built with Zero Touch, Celery contains nodes built with custom user interface using WPF (Windows Presentation Foundation).

The reason for building these nodes in Celery is to provide you a better way to access data and manipulate outputs.

While as of this time there are only three nodes in the package, I will be expanding it later when I believe there are other needs to be addressed. For the meantime, here are the notes regarding their uses.

celerynodes20161118

List Item Selector

This node enables you to select an item from a list passed through its input. The type of input may be anything as long as it does not include a nested list. It can be a list of numbers, integers or strings. When the nodes is unable to determine the type of input, it will output the selected item as a string value.

Since the types of input that it can read are very limited, I will broaden this area and make sure it can provide output as how its type should be.
Revit Parameter Selector

This node is derived from the List Item Selector node. The only difference is that it only allows a specific type of input by a Revit Element.

It extracts all the instance parameters of the element in the input. The output is the name of the parameter selected from the drop-down.

This is very best to complement with the Revit.GetParameterValueByName and Revit.SetParameterValueByName nodes already provided in Dynamo.

The reason for creating this node is to enable you to select a specific parameter name when you do not have to type or provide a string input to the Revit.GetParameterValueByName and Revit.SetParameterValueByName, especially when you want change the values of these inputs.
Input-bounded Number Slider

Many of you might wonder why there is a need for this node. The need for this node came when I was testing a geometry that changes with a range of numbers that are limited from a number of items in a list, which changes most of the time.

The integer slider and the number slider come without inputs. You need to input yourself the lowest and highest numbers as ranges on the fields provided. While they are the basic things you need in a slider, sometimes you want inputs you are unable to control. You would want inputs that are coming from the outside. This is when this custom node comes in.

You provide numeric inputs regardless of which is bigger or not. When the left range is bigger that the right range, the output value decreases when you move the slider to the right. Also, it also lets you select which type of output you want to have, either as an integer or a floating-point number. When you select the floating-point number, it even lets you set the decimal places.

As of this writing I noticed that I forgot to provide a step value. I will be updating the node to avail this functionality on its future release.
Please comment for any bugs or suggestions regarding this package.

Thank you very much!

Rutabaga for Dynamo

This year I have just developed a new set of nodes in Rutabaga.

Majority of these nodes are made for use in our office, but it is a great thing to make it public and let Dynamo users use and evaluate them.

Rutabaga nodes concentrate on linked documents and interference checks. Nodes that extract elements from its ID are the most important ones in interference check because as of Rutabaga’s release time, nodes that are available in the Dynamo package did not work properly. So I created four nodes made for it.

The new update as of this writing time is in 2016.2.9 version. Although I released seven more new nodes, I don’t know why the extension could only read five of them. I will be carrying out this issue and hopefully release the updated version within this week.

Below are the details of the nodes and its uses.

 

Revit.ElementFromId(Document)

Just like I had stated earlier, the reason of creating this set of nodes is that other nodes of the same purpose did not work properly. The other reason is since interference checking nodes are also created, there must be ID reading nodes that come with it, otherwise it is not a interference checking package at all. All other nodes required with this package are already available in Dynamo.

There are four nodes related to this in the package. One is reading from an integer ID, another is from a unique ID. The other two function just the same as the first two, but it has another argument called Document.

The first two read the element Id from the current document, while the other two read the ID from the document connected to them.

Revit.LinkedDocuments

This node reports all the linked documents inside the current project, as well as their paths and their positions. The first two outputs report as a type output, while the last output is an instance output.

The two type outputs just report what are the links in the documents, and their paths. So each data output should be unique. The position output, however, as an instance output, reports position of any instances of the link to the project. There maybe two or more instance for every type linked, so the number of instances and types may not be equal, but the number of instances must not be less than the number of types.

(I am still working on the type of the instance per position to output, so please wait for the fix.)

Geometry.IsPointInsideLoop

This node lets you determine if a point is inside or outside a loop of points on a counter-clockwise direction. You can also tell the node if the loop of points represent a hole, and the points will be read at a clockwise direction.

Vectors.TurnLeft/Right/AtNormal

These group of nodes is pretty straightforward. It will just return a vector turned 90 degrees left or right rotating along Z-axis with reference to the direction of the input vector. When the nodes with AtNormal option is used, the output vector will be when rotating at the given axis vector.

X.O

Simply put, a carrier or memory node that is very useful for Dynamo scripts with enormous amount of nodes used. Use this node when you want to bridge some parts of  the script with outputs from nodes which you want to maintain, as well as tidying up the script. This node can also be used when you want to pass temporary outputs for tests.

 

Early morning at Murano 3201 December 3

I had my very first session at Autodesk University, yes it is 2015, and at this room inside The Venetian Hotel in Las Vegas, I had made a connection with the audience; of course they were those who pre-registered at my session. Although not all of them came, I was so delighted that were still about three-quarters of them came. I don’t know how will I thank them. I just hope that they learn something from me. And if ever I will be given a chance again to do the speaking at AU2017, I would like to do it again. This time, it will be better.

That Barker

But there is someone that I am most thankful for.

This man just did the best invites and calls outside Murano 3201 before my session time. I really appreciate his dedication to his job. I am somehow so grateful for his efforts to call the people who registered at my session. I can hear his voice from inside and somehow made myself do the best of me as he was just too active at his work.

I want to thank him to the fullest, but I just couldn’t remember his name.

To those who attended my session and the other sessions after mine, please tell me even his name if ever you know.

Thank you very much!

The Responses

I have just read all of the responses. Thank you to all those who respond.

I cannot make excuses here, but I admit I have to really work harder at my speaking, especially my English. I just hoped that I got to be exposed more often on this. But I have to start it first at a Japanese environment since I am working here in Japan and of course, the audiences are all Japanese. If there are even any small opportunities for me to have a presentation in total English and of course the audience are total English listeners and speakers, I would like to grab it.

And the other side of my feeling, I am also disappointed at the final result of my PowerPoint. Next time, I will make sure that I will present every detail on how to do things, if ever I will be entitled to a session that needs teaching. For instance, it is a Instructional Demo, right? I just can’t say what had happened during the final days of this presentation’s evaluation with my team.

To those who went away too early, I am just sorry for them. Although it always happens at AU…

Of course, there are many people who stayed ’til the end, and some of them commented positive ones. In fact, one of them said I had a great sense of humor. I really appreciate them and am so thankful to them. Nevertheless, my session was somewhat a success because of them. I would also like to collaborate with those people and share ideas with each other. I am not those guys like Marcello Sgambelluri or Nathan Miller who are total experts on both sides of presenting at AU. I could have my own style of talking; I just still have to discover that.

These sides benefit me to what I will become next AU.

 

.

Bridging Designer’s Template with Intuitive Families to your Working Template (AS9926)

As discussed in my session Revit++: More with Your Revit, More for Your Workflow session at Autodesk University 2015, when you use your designer’s template that contains intuitive families, you would want to use them in your project during the design development phase. But these intuitive families use instance parameters. Using instance parameters won’t be compatible enough in your schedules if you use schedules.

This post will provide you steps in converting your intuitive families to a more manageable “usual” families (those we are accustomed to) to be able to work smoothly with schedules, as well as eliminate discrepancies and inconsistencies that is presented in the intuitive families.

I would like to introduce here a program that is built for passing values from an instance parameter controlled family to its corresponding type-controlled twin family.

CopyParameterValues

Did I say “twin”? Yes. Those two families must be identical with each other, the only difference being the other twin uses type parameters.

The prerequisites for using this program are mentioned below, and with them are steps to do it.

Creating the Twin Family

First, it is just simple. You have to save a copy of your existing family that has the instance parameters.

Then just make the instance parameters into type parameters.

But be careful! Some parameters will mess up your family when to switch them into type parameters, eventually tell you to delete the family when it fails to load in your project. This is a well-known issue especially when you try to convert an array parameter or a dimension parameter. Input some values before pressing the Apply button, and test them before loading it to your project.

Parameter Pairing

An optional effort but is handy when you started to use the program above to convert your families.

To have an idea of what to do, just list up all the instance parameter you used particularly in one family. Then create a type parameter to pair with every instance parameter you have. You just have to put a distinctive character or letter for your type parameter to differentiate it with the instance parameter.

For example, if you have an instance parameter that is named “Opening Height”, you create a type parameter that has a name such as “T Opening Height”. Remember to keep the same variable types!

Then use these parameters instead of re-assigning the existing instance parameters to type parameters. This is somewhat safer.

Using the Tool

Before using this tool, make sure you loaded all the twin families of all the instance-parameter-based families you use in your model.

1.) Select any instance of an instance-parameter-based family in your model. Click “Finish” button on the left side of the option bar to continue.

2.) A window like the figure above will appear.

All the editable instance parameters of the family instance you selected will be listed in the Instance Parameters column.

Take note that the letters enclosed in parentheses indicate what kind of variable the parameter is. The meaning of these letters are written below:

i = integer

d = real number

e = element id

s = string of text

n = none

3.) Select the type of the twin family from the Base Replacement Type area. All the editable type parameters of the selected family type will be listed in the Type Parameters column, with the kind of variable similar to those in instance parameters.

4.) Select a pair of parameter by selecting one parameter from the instance parameter list and another from the type parameter list.

Make sure the “Pair->” button will be highlighted. If not highlighted, make sure that the parameters you selected have the same kind of variable.

5.) Click the “Pair->” button. The selected parameters will be listed on the Paired Parameters column, and they will be removed from their respective columns.

6.) Repeat 4 and 5 to create other pairs of parameters.

7.) To remove a pair item from the Paired Parameters column, click an item and click the “<-Unpair” button. This will remove the item from the list, as well as return the parameters to their respective columns.

8.) When you listed enough items in the Paired Parameters column, it is time to designate a type naming format.

Select an item in the column and click “Add to Naming Format”. This will automatically add a string of text to the text entry.

9.) To add a character in the naming format, add it between the } and { characters. Adding between the { and } will damage the format and will yield to unexpected results in your family type name.

10.) To save the pairing data for use later, click “Save Pair Data…” button on the lower left of the window.

11.) Click the OK button when all is done.

The Best Practice

When you are already decided to build and use your designer’s template and also want to switch to your usual working template for your design development phase and construction documents phase, it is better to create a new family derived from an existing family, so that later you can easily change the type parameters to instance parameters and test operate them without having the “unable to create” error message and eventually delete them.

On the other hand, if you don’t have that family you want to use, you can just create you own, but be sure you assign type parameters first to that properties of that family. Then you derive them and change to instance parameters.