WindowsForm using Revit API

WindowsForm using Revit API

Anonymous
Not applicable
11,860 Views
5 Replies
Message 1 of 6

WindowsForm using Revit API

Anonymous
Not applicable

-- sorry for bad english, it's not my native language --

 

I've been trying to create a plugin for Revit 2017 with Visual Studio 2015 with windows Form. Unfortunately I've not found much useful documentation online for doing so (if you have links, i'll be happy to give them a look).

 

I've built a simple form using a Listbox and a select button

  • The Listbox shows all the doors in a Revit project
  • The select button selects all the selected doors from the Listbox and selects them in the Revit project (that's a lot of selects ...)

It's a test solution, to see how it all works.

WeWillSee class (sorry, i didn't know how to name the class) is the class implementing the main RevitAPI function Execute:

 using System; 
 using Autodesk.Revit.UI; 
 using Autodesk.Revit.Attributes; 
 using Autodesk.Revit.DB; namespace Test2 {

 [Transaction(TransactionMode.Manual)]
 class WeWillSee : IExternalCommand
 {
     public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
     {
         UIApplication uiapp = commandData.Application;
         /*UIDocument uidoc = uiapp.ActiveUIDocument;
         Document doc = uidoc.Document;*/

         try
         {
             System.Windows.Forms.Application.EnableVisualStyles();
            System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false);
             System.Windows.Forms.Application.Run(new Form(commandData));
             //System.Windows.Forms.Form wf = new Form1(uiapp);
         }
         catch (Exception e)
         {
             TaskDialog.Show("Error", e.ToString());
             return Result.Failed;
         }

         return Result.Succeeded;
     }
 } 
 }

The Form I want to open (the rest in not important):

namespace Test2
{
    public partial class Form : System.Windows.Forms.Form
    {
        private UIApplication uiapp;
        private UIDocument uidoc;
        private Document doc;

        public Form(ExternalCommandData commandData)
        {
            InitializeComponent();            uiapp = commandData.Application;            uidoc = uiapp.ActiveUIDocument;            doc = uidoc.Document;
        }

And finally the Program.cs file (the one causing me problems):

namespace Test2
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1(/*Can't call ExternalCommandData on static class*/));
        }
    }
}

Thanks for any help you can offer! 🙂

0 Likes
Accepted solutions (1)
11,861 Views
5 Replies
Replies (5)
Message 2 of 6

matthew_taylor
Advisor
Advisor

Hi @Anonymous,

You need to use the Revit API with a valid context. That is, from within Revit. A ribbon button etc.

So forget about the 'Program.cs' part.

Using a Visual Studio add-in wizard as a template will help you.

Jeremy's blog has examples: http://thebuildingcoder.typepad.com/blog/wizard/

 

Cheers,

 

-Matt

 


Cheers,

-Matt
_______________________________________________________________________________
Marking a post as a 'solution' helps the community. Giving a post 'Kudos' is as good as saying thanks. Why not do both?
Message 3 of 6

Anonymous
Not applicable

Hi @matthew_taylor,

Thank you for answering swiftly, I'll give it a try.

 

ALl the best,

 

Elliott

0 Likes
Message 4 of 6

Anonymous
Not applicable

Hi again @matthew_taylor,

 

I've managed to execute the Revit Add-in thanks to the template provided by Jeremy Tammik.

At first I thought that everything worked fine, until I realized the contrary.

 

The code below should Select a door shown in the ListBox of the windows form.

The ListBox successfully loads all the doors included in the revit project, only when i press the Select button, nothing happens.

 

Here is the code:

 

namespace Test3
{
    [Transaction(TransactionMode.Manual)]
    class WinForm: System.Windows.Forms.Form
    {
        private System.Windows.Forms.ListBox lb_doors;
        private System.Windows.Forms.Button bt_select;
        private ExternalCommandData commandData;
        private UIApplication uiapp;
        private UIDocument uidoc;
        private Document doc;

        public WinForm(ExternalCommandData commandData)
        {
            InitializeComponent();
            this.commandData = commandData;
        }


        private void InitializeComponent()
        {
            this.lb_doors = new System.Windows.Forms.ListBox();
            this.bt_select = new System.Windows.Forms.Button();
            this.SuspendLayout();
            //
            // lb_doors
            //
            this.lb_doors.FormattingEnabled = true;
            this.lb_doors.Location = new System.Drawing.Point(12, 12);
            this.lb_doors.Name = "lb_doors";
            this.lb_doors.Size = new System.Drawing.Size(260, 199);
            this.lb_doors.TabIndex = 0;
            //
            // bt_select
            //
            this.bt_select.Location = new System.Drawing.Point(197, 226);
            this.bt_select.Name = "bt_select";
            this.bt_select.Size = new System.Drawing.Size(75, 23);
            this.bt_select.TabIndex = 1;
            this.bt_select.Text = "Select";
            this.bt_select.UseVisualStyleBackColor = true;
            //
            // WinForm
            //
            this.ClientSize = new System.Drawing.Size(284, 261);
            this.Controls.Add(this.bt_select);
            this.Controls.Add(this.lb_doors);
            this.Name = "WinForm";
            this.Load += new System.EventHandler(this.WinForm_Load);
            this.ResumeLayout(false);

        }

        private void WinForm_Load(object sender, EventArgs e)
        {
            uiapp = commandData.Application;
            uidoc = uiapp.ActiveUIDocument;
            doc = uidoc.Document;

            ShowInListBox(CreateLogicAndFilter(doc));

        }

        private void bt_select_Click(object sender, EventArgs e)
        {
            List<Element> doors = new List<Element>();

            foreach (Element door in lb_doors.Items)
            {
                doors.Add(door);
            }

            InAppDoorSelection(doors);
        }

        private void InAppDoorSelection(ICollection<Element> doors)
        {
            List<ElementId> doorsID = new List<ElementId>();

            foreach (Element door in doors)
            {
                doorsID.Add(door.Id);
            }

            uidoc.Selection.SetElementIds(doorsID);
        }

        private void ShowInListBox(ICollection<Element> elements)
        {
            lb_doors.Items.Clear();

            foreach (Element element in elements)
            {
                lb_doors.Items.Add(element.Id + " " + element.Name);
            }
        }

        //CreateLogicAndFilter source:
        //http://help.autodesk.com/view/RVT/2017/ENU/?guid=GUID-5231B53E-DD08-4F05-8BF2-E23E11C314D4
        private ICollection<Element> CreateLogicAndFilter(Document document)
        {
            // Find all door instances in the project by finding all elements that both belong to the door
            // category and are family instances.
            ElementClassFilter familyInstanceFilter = new ElementClassFilter(typeof(FamilyInstance));

            // Create a category filter for Doors
            ElementCategoryFilter doorsCategoryfilter = new ElementCategoryFilter(BuiltInCategory.OST_Doors);

            // Create a logic And filter for all Door FamilyInstances
            LogicalAndFilter doorInstancesFilter = new LogicalAndFilter(familyInstanceFilter, doorsCategoryfilter);

            // Apply the filter to the elements in the active document
            FilteredElementCollector collector = new FilteredElementCollector(document);
            ICollection<Element> doors = collector.WherePasses(doorInstancesFilter).ToElements();

            return doors;
        }
    }
}

and the command.cs file:

 

namespace Test3
{
    [Transaction(TransactionMode.Manual)]
    public class Command : IExternalCommand
    {
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            WinForm wf = new WinForm(commandData);

                wf.ShowDialog();
                wf.Close();

            return Result.Succeeded;
        }
    }   
}

I debugged the script, and had some errors/warning in them, some are probably unimportant, but here they are anyways (they are all recurrent so I only included them once):

 

...
System.Windows.Data Error: 40 : BindingExpression path error: 'Current' property not found on 'object' ''QuickAccessUndoButton' (HashCode=123050)'. BindingExpression:Path=Current; DataItem='QuickAccessUndoButton' (HashCode=123050); target element is 'ContentPresenter' (Name=''); target property is 'NoTarget' (type 'Object')
...
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.Border', AncestorLevel='3''. BindingExpression:Path=ActualHeight; DataItem=null; target element is 'ContentPresenter' (Name=''); target property is 'NoTarget' (type 'Object')
...
System.Windows.Media.Animation Warning: 6 : Unable to perform action because the specified Storyboard was never applied to this object for interactive control.; Action='Stop'; Storyboard='System.Windows.Media.Animation.Storyboard'; Storyboard.HashCode='30643766'; Storyboard.Type='System.Windows.Media.Animation.Storyboard'; TargetElement='Autodesk.Private.Windows.DropDownToolTip'; TargetElement.HashCode='7358445'; TargetElement.Type='Autodesk.Private.Windows.DropDownToolTip'
...
System.Windows.Data Error: 23 : Cannot convert '{DisconnectedItem}' from type 'NamedObject' to type 'Autodesk.Windows.RibbonItem' for 'en-US' culture with default conversions; consider using Converter property of Binding. NotSupportedException:'System.NotSupportedException: TypeConverter cannot convert from MS.Internal.NamedObject.
0 Likes
Message 5 of 6

matthew_taylor
Advisor
Advisor
Accepted solution

Hi @Anonymous,

The context of the macro is still with the dialog. It is a modal dialog. You need to close the dialog, then pass the doors back to the external command, then select the items.

Basically, the dialog still has focus, therefore Revit can't take over.

 

Cheers,

 

-Matt


Cheers,

-Matt
_______________________________________________________________________________
Marking a post as a 'solution' helps the community. Giving a post 'Kudos' is as good as saying thanks. Why not do both?
Message 6 of 6

Moustafa_K
Advisor
Advisor

Hi Addin

You need 1st need to get an idea of how Revit Threadworks. In brief, Revit only works on a stat thread. The multiple thread is not an option, however, there is a workaround using external Events. Try consuming these 2 posts and let us know if you need further help.

 

 

Multi-threading with Revit

 

Triggering Immediate External Event Execution

 

Moustafa Khalil
Cropped-Sharp-Bim-500x125-Autodesk-1