Revit API Forum
Welcome to Autodesk’s Revit API Forums. Share your knowledge, ask questions, and explore popular Revit API topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Listing all views in a project on a Winform throwing an exception

9 REPLIES 9
SOLVED
Reply
Message 1 of 10
thomas
3910 Views, 9 Replies

Listing all views in a project on a Winform throwing an exception

 

I am attempting to create a WinForm which lists all the views in the project as a viewTree. The WinForm run command is then packaged into a Dynamo ZeroTouch node, however the views are not displayed when the form launches, and when I relaunch the form I get an error that line 53 (the foreach statement that attempts to use the view name to populate the viewTree) is "not set to an instance of an object". This is my first attempt to raise and consume events and I've done everything I can to get it to work; what is the cause of this error?

 

The form successfully launches via the dynamo node: 

Capture.JPG

 

 

The exception:

Capture2.JPG

 

 

The code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Autodesk.DesignScript.Runtime;
using Autodesk.DesignScript.Interfaces;
using Autodesk.DesignScript.Geometry;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;
using RevitServices.Persistence;
using RevitServices.Transactions;
using Revit.Elements;
using Revit.GeometryConversion;

namespace SelectSheetsAndViews
{
    /// <summary> The form class</summary>
    public partial class FormRevitSelect : System.Windows.Forms.Form
    {
        public FormRevitSelect()
        {
            InitializeComponent();
        }

        private void DynamoTreeListSelect_Activated(object sender, System.EventArgs e)
        {

        }

        private void textBox1_TextChanged(object sender, EventArgs e)
        {

        }

        private void tableLayoutPanel1_Paint(object sender, PaintEventArgs e)
        {

        }

        private void Form1_Load(object sender, EventArgs e)
        {
            List<Autodesk.Revit.DB.View> d1 = new ThresholdReachedEventArgs().Views;
            //List<string> d = new List<string> { "A", "B", "C", "D"};
            foreach (Autodesk.Revit.DB.View x in d1)
            //foreach (string x in d)
            {
                treeView1.Nodes.Add(x.ToString());
            }
        }

        private void checkedListBox1_SelectedIndexChanged(object sender, EventArgs e)
        {

        }

        private void button1_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
        {

        }
    }


    /// <summary>A Revit class to get all the sheets and views in the document</summary>
    public class RevitWinForm
    {
        private List<object> _sheetsAndViews;
        private List<object> sheetsAndViewsToDelete;


        internal List<object> GetSheetAndViewsToDelete { get { return sheetsAndViewsToDelete; } }

        private RevitWinForm(List<object> sheetsAndViews)
        {
            _sheetsAndViews = sheetsAndViews;

        }

        internal List<object> GetSheetAndViewList { get { return _sheetsAndViews; } }

        /// <summary>
        /// Function to collect all the views and sheets in the document
        /// </summary> 
        internal static List<Autodesk.Revit.DB.View> SheetsAndView()
        {
            Document doc = DocumentManager.Instance.CurrentDBDocument;
            FilteredElementCollector collector = new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Views);
            List<Autodesk.Revit.DB.View> viewList = collector.ToElements() as List<Autodesk.Revit.DB.View>;

            return viewList;
        }


        /// <summary>
        /// The MultiReturn attribute can be used to specify
        /// the names of multiple output ports on a node that 
        /// returns a dictionary. The node must return a dictionary
        /// to be recognized as a multi-out node.
        /// </summary>
        /// <param name="refresh">Refresh</param>
        /// <returns>DynamoTreeListSelect</returns>
        public static string CompactDocument(bool refresh)
        {
            System.Windows.Forms.Application.Run(new FormRevitSelect());
            return "Process Complete";
        }

    }
        
    class Program
    {
        static void Main(string[] args)
        {
            RevitSheetsAndViews v = new RevitSheetsAndViews();
            v.GetRevitViews += v_ViewsOUT;
        }

        static void v_ViewsOUT(object sender, ThresholdReachedEventArgs e)
        {
            List<Autodesk.Revit.DB.View> d1 = e.Views;
        }
    }

    class RevitSheetsAndViews
    {

        private List<Autodesk.Revit.DB.View> views;

        /*public RevitSheetsAndViews(List<Autodesk.Revit.DB.View> viewsOUT)
        {
            views = viewsOUT;
        }
        */
        public RevitSheetsAndViews()
        {
            Document doc = DocumentManager.Instance.CurrentDBDocument;

            FilteredElementCollector collector = new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Views);
            views = collector.ToElements() as List<Autodesk.Revit.DB.View>;
            ThresholdReachedEventArgs args = new ThresholdReachedEventArgs();
            args.Views = views;
            RevitViewsEventHandler(args);
        }

        protected virtual void RevitViewsEventHandler(ThresholdReachedEventArgs e) //raise the EventHandler delegate and associate GetRevitViews to it
        {
            EventHandler<ThresholdReachedEventArgs> handler = GetRevitViews;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        public event EventHandler<ThresholdReachedEventArgs> GetRevitViews; // declare an event named GetRevitViews. The event is associated with the EventHandler delegate and raised in a method named OnThresholdReached.
    }

    public class ThresholdReachedEventArgs : EventArgs
    {
        public List<Autodesk.Revit.DB.View> Views { get; set; }
    }
}
9 REPLIES 9
Message 2 of 10
jeremytammik
in reply to: thomas

Dear Thomas,

 

The answer to every question in the universe is identical:

 

KISS!

 

Unfortunately, that often raises a follow-up question:

 

How?

 

How can I simplify my problem?

 

In programming, simplification can often be achieved by separating separate tasks.

 

That was one of the main goals of object oriented programming OOP when it was invented or at least started emerging six (!) decades ago.

 

In this case, you are talking with the Revit API to obtain information about views, e.g., their names, or whatever other information you wish to display.

 

That is one task.

 

Another issue, completely separate, is to display that information.

 

I suggest you separate the two completely.

 

In other words, talk with the Revit API, obtain the information you require, store it, and stop communicating with Revit.

 

Then, and only then, proceed with other things, such as displaying your stuff.

 

I hope this helps.

 

Good luck!

 

Cheers,

 

Jeremy

 



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 3 of 10
Revitalizer
in reply to: thomas

Hi,

 

the cause is:

When you create a new ThresholdReachedEventArgs instance, its Views property is null, initially.

It must be set before accessing it via a foreach loop.

 

Revitalizer

 

 




Rudolf Honke
Software Developer
Mensch und Maschine





Message 4 of 10
jeremytammik
in reply to: Revitalizer

Hi Rudi,

 

Thank you very much for taking a closer look than I did  🙂

 

And your succinctness 🙂

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 5 of 10
Revitalizer
in reply to: jeremytammik

Hi Jeremy,

 

Happy New Year to you - in response to your latest blog posting.

I like the forum, too 🙂

 

 

Rudi




Rudolf Honke
Software Developer
Mensch und Maschine





Message 6 of 10
thomas
in reply to: Revitalizer

Thanks Rudi and Jeremy 

 

Very helpful advice. I realised one of the exceptions was caused by problems with the way I'd written the element collector. Once I fixed that, I acted upon Jeremys advice and did away with extending the Event Handler class, and simply implemented the collector and the rest of my function within the Load event of the form. All now works as expected:

 

Bimorph WinForm.JPG

Message 7 of 10
jeremytammik
in reply to: thomas

Dear Thomas,

 

Thank you for your confirmation and congratulations on getting it working!

 

I promoted this thread to a blog post for better legibility and future reference:

 

http://thebuildingcoder.typepad.com/blog/2017/01/distances-switches-kiss-ing-and-a-dino.html#4

 

Cheers,

 

Jeremy

 



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 8 of 10
thomas
in reply to: jeremytammik

Cheer Jeremy

 

That's great! If you/those who visit your article are interested, here's the working 'after' code, demonstrating the simplification:

 

namespace Revit
{
    /// <summary> The form class</summary>
    internal partial class FormRevitSelect : System.Windows.Forms.Form
    {
        public List<int> _viewId;

        public List<int> GetViewIds
        {
            get { return _viewId; }
            set { _viewId = value; }
        }

        public FormRevitSelect()
        {
            InitializeComponent();
        }

        private void textBox1_TextChanged(object sender, EventArgs e)
        {

        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Document doc = DocumentManager.Instance.CurrentDBDocument;

            BrowserOrganization browserOrg = BrowserOrganization.GetCurrentBrowserOrganizationForViews(doc); //Get the browser item from the document

            FilteredElementCollector collector = new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Views);

            List<Autodesk.Revit.DB.Element> viewList = (List<Autodesk.Revit.DB.Element>)collector.ToElements().ToList();
            foreach (Autodesk.Revit.DB.View v in viewList)
            {
                if (v.Name != null & v.ViewType.ToString() != "") //v.ViewType.ToString() != "" is used to remove views which are present in templates by default but only picked up by the element collector. They need to be removed from the list
                {
                    List<FolderItemInfo> folderInfo = browserOrg.GetFolderItems(v.Id).ToList(); //Get the folder information for the view
                    treeView1.Nodes.Add(v.Id.ToString(), v.Name);
                }
            }
        }
    }
}

 

ZeroTouch code for Dynamo library import:

    /// <summary>A Revit class to get all the sheets and views in the document</summary>
    public class DocumentUtilities
    {
        private List<object> _sheetsAndViews;

        internal List<object> GetSheetAndViewsToDelete { get { return _sheetsAndViews; } }

        private DocumentUtilities(List<object> sheetsAndViews)
        {
            _sheetsAndViews = sheetsAndViews;

        }

        /// <summary>
        /// Work in progress
        /// </summary>
        /// <param name="refresh">Refresh to reopen the form</param>
        /// <returns>Revit View Elements</returns>
        public static List<Revit.Elements.Element> ExporttDocument(bool refresh)
        {
            Document doc = DocumentManager.Instance.CurrentDBDocument;

            FormRevitSelect form1 = new FormRevitSelect();
            System.Windows.Forms.Application.Run(form1);

            List<Revit.Elements.Element> vList = new List<Revit.Elements.Element>();
            foreach (int i in form1.GetViewIds)
            {
                Autodesk.Revit.DB.Element v = doc.GetElement( new Autodesk.Revit.DB.ElementId(i) ) as Autodesk.Revit.DB.Element;
                vList.Add( v.ToDSType(true) );
            }
            return vList;
        }
    }

 

Run in Dynamo:

Bimorph Select.PNG

 

 

Result:

Result.PNG

 

Cheers!

 

Message 9 of 10
rvtquestions
in reply to: thomas

@thomas

 

Hi Thomas,

 

I am tackling a similar issue and wanted to ask you a few questions about your code here. First off, I'd like to thank you for starting the topic and providing a follow up.

 

Here's what I know:

- I am familiar with winforms and getting them to function as an addin but not really for Zerotouch.

- I have successfully replicated something similar to your treeview form as part of a larger addin with the main difference being how the Document doc is being called and passed around.

 

The issue:

- I have some knowledge about ZeroTouch but not advanced. I know which assemblies should be included and called.

- I am having difficulty correctly incorporating the winform in Dynamo.

        - How is your VS proj structured? Currently I've tried a basic Class file (containing the zerotouch code) and adding a winform (containing the form code). When I build and import that .dll into Dynamo, it imports a whole bunch of stuff on top of the static class, like all of the window forms classes. When I run the static class node, it opens the winform but it is empty and then goes into a dynamo node error.

 

Any ideas why this is happening? Again I can confirm I could get the form to work standalone as an addin but trying to incorporate it in Zerotouch ends up being messy. I'd appreciate your help! Thank you.

Message 10 of 10
rvtquestions
in reply to: rvtquestions

@thomas

 

nvm. I may have solved the issue.

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Rail Community