C# Objects remain in memory even after an Add-in exits

C# Objects remain in memory even after an Add-in exits

sragan
Collaborator Collaborator
559 Views
5 Replies
Message 1 of 6

C# Objects remain in memory even after an Add-in exits

sragan
Collaborator
Collaborator

I thought the Garbage Collector automatically erased objects from memory after a Revit Macro or Add-in finished running.   However, in my limited programming experience, i have seen times when a particular Macro or Add-In seemed to keep some variables from a previous run.

 

In particular, I've recently written a Macro with a Windows Form, and some textboxes I use to edit Family Parameters.  If a particular family doesn't include a particular parameter, I disable that particular textbox.   What I have found is that if I run the macro twice, once for a family that is missing one parameter, and again for a family that does have that parameter, the textbox remains disabled on the second run.

 

It was an bug that was fairly easy to fix - I just needed to enable each textbox when it finds a valid parameter.  But I really didn't think I should have to do that, since Textboxes are enabled by default.   

 

Can anyone shed any light on why a textbox would remain disabled for the second run of a Macro?  Even if say  Textbox1 remained in memory between Macro Runs, wouldn't the constructor;

 

 System.Windows.Forms.TextBox TextBox1 = new System.Windows.Forms.TextBox(); 

 

recreate a new TextBox1 that doesn't have any connection to a previous Macro Run?

 

I think I have also seen other variables and things that seem to persist between Macro or Add-In runs, but I don't have any other specific examples.

0 Likes
Accepted solutions (2)
560 Views
5 Replies
Replies (5)
Message 2 of 6

Sleepingfish_Kuo
Advocate
Advocate
Accepted solution

Can you upload a part of your code to show how your Windows Form initialize?

0 Likes
Message 3 of 6

sragan
Collaborator
Collaborator

Yes, and now that I think about it, could it have anything to do with declaring the textbox as a "static public textbox" at the start of the code?  

 

If you run the code, select a light fixture first.   You can either add the shared parameter in the attached file, or change the code to use a parameter in your light fixtures.

 

Line 72 is the line I had to add to re-enable the textbox after  running code on a light that doesn't have that particular parameter.  (It's commented out below, so you should be able to recreate the issue.) 

using Autodesk.Revit.UI;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI.Selection;
using System.Collections.Generic;
using System.Linq;


//add reference to System.Windows.Forms via Project-Add Reference....
using System.Windows.Forms;
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Collections;


namespace Simplified_Edit_Lights
{
    [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
    [Autodesk.Revit.DB.Macros.AddInId("897A2706-4C47-45C0-B91E-6F7DB36BD6AE")]
	public partial class ThisApplication
	{
		         
        public static Document doc;       
        public static Parameter p;
        public static System.Windows.Forms.TextBox TextBoxScheduleFix = new System.Windows.Forms.TextBox ();
        public static String sScheduleFix;
		public static Element elemType;

		private void Module_Startup(object sender, EventArgs e)
		{

		}

		private void Module_Shutdown(object sender, EventArgs e)
		{

		}

		#region Revit Macros generated code
		private void InternalStartup()
		{
			this.Startup += new System.EventHandler(Module_Startup);
			this.Shutdown += new System.EventHandler(Module_Shutdown);
		}
		#endregion
		
		        public class formEditLights : System.Windows.Forms.Form
        {
            public formEditLights()
            {
                #region SetUpFormLabels&Textboxes
                                
                this.Text = "Edit Lights";
                this.Width = 780;
                this.Height = 1000;                
                
                //set up SCHEDULE FIXTURE Textbox
                             
                
                TextBoxScheduleFix.Location = new System.Drawing.Point(200, 20);
                TextBoxScheduleFix.Width = 100;
                TextBoxScheduleFix.Height = 20;
                TextBoxScheduleFix.Anchor = (AnchorStyles.Left|AnchorStyles.Right|AnchorStyles.Top|AnchorStyles.Bottom);
                try
                {
                
                p = elemType.get_Parameter(new Guid("e72f1b73-8aa3-4db7-b80f-98d2040c7bd2"));
                sScheduleFix = p.AsInteger().ToString ();
                TextBoxScheduleFix.Text = sScheduleFix;
                //TextBoxScheduleFix.Enabled = true;
                }
                catch
                {
                    TextBoxScheduleFix.Text = "";
                    TextBoxScheduleFix.Enabled = false;
                }                
                #endregion
                #region FormButtonDefs
                //setup the "OK" button 
                Button button1 = new Button ();
                button1.Text = "OK";
                button1.Location = new System.Drawing.Point(215, 920);
                button1.Anchor = (AnchorStyles.Bottom);
                button1.Click += new System.EventHandler(button1_Click);

                //setup the "Cancel" button 
                Button button2 = new Button();
                button2.Text = "Cancel";
                button2.Location = new System.Drawing.Point(340, 920);
                button2.Anchor = (AnchorStyles.Bottom);
                button2.Click += new System.EventHandler(button2_Click);
                
                //setup the "Apply" button 
                Button button3 = new Button();
                button3.Text = "Apply";
                button3.Location = new System.Drawing.Point(465, 920);
                button3.Anchor = (AnchorStyles.Bottom);
                button3.Click += new System.EventHandler(button3_Click);
        
                this.CancelButton = button2;
                // Set the start position of the form to the center of the screen.
                this.StartPosition = FormStartPosition.CenterScreen;
        
                #endregion
                
                // Add the controls to the form.
               #region AddFormControls 
                
                
                GroupBox groupbox1 = new GroupBox ();
                 groupbox1.Text = "General:  ";
                groupbox1.Location = new System.Drawing.Point(15, 50);
                groupbox1.Width = 720;
                groupbox1.Height = 390;
                groupbox1.Anchor = (AnchorStyles.Left|AnchorStyles.Right|AnchorStyles.Top); 
                 
                groupbox1.Controls.Add(TextBoxScheduleFix);
                this.Controls.Add (groupbox1);
                this.Controls.Add(button1);
                this.Controls.Add(button2);
                this.Controls.Add(button3);
                
                #endregion
            }  // end public form EditLights
            
            // Event Handler for the OK button 
        private void button1_Click(object sender, System.EventArgs e)
                {
                 
                 UpdateFixtureParameters();
                 this.Close();
                                      
                }
         
        //  EVent Handler for the Cancel Button 
        private void button2_Click(object sender, System.EventArgs e)
                {
                    
                    this.Close();
                }
        
         private void button3_Click(object sender, System.EventArgs e)
                {
                    
                     UpdateFixtureParameters();
             
                } 
         //routine to update any changed parameters
         private void UpdateFixtureParameters()
             
             //this is a routine "Edit Lights" uses to update parameters when either the "OK" or "Apply" buttons are pressed.
             //Each textbox is compared to the parameter value, and if they are different, the textbox value is written to the parameter
             
         { using (Transaction trans = new Transaction (doc, "Edit Light Fixture Parameters"))
                        
                        {
                            trans.Start();

                        //Set new vaule for Schedule Fixture
                        if (TextBoxScheduleFix.Enabled == true && sScheduleFix != TextBoxScheduleFix.Text)
                                 {
                                     //TaskDialog.Show("Revit","Update Schedule Fixt");
                                     int i;
                                     
                                     if(int.TryParse(TextBoxScheduleFix.Text, out i))
                                        {
                                     
                                         elemType.get_Parameter (new Guid ("e72f1b73-8aa3-4db7-b80f-98d2040c7bd2")).Set(int.Parse(TextBoxScheduleFix.Text));
                                         sScheduleFix = TextBoxScheduleFix.Text;

                                         
                                        }
                                      
                                  else
                                  {
                                      string msg = @"Can't Set ""Schedule Fixture"" to a non-integer value.";
                                      TaskDialog.Show("Revit Error.", msg);
                                      
                                      }
                                 }
                 
                        
                                             
                        
                        
                            trans.Commit();
                                                        
                        
                        }
             
         }
            
        }

		public void Simplified_Edit_Lights()
		{             //Pick a light and run macro
            //This edits the Type Parameters for a particular light fixture type (similar to editing in a fixture schedule)
            UIDocument uidoc = this.ActiveUIDocument;
            doc = this.ActiveUIDocument.Document;
               // Get the element selection of current document
            Selection selection = uidoc.Selection;
            //store element id's
            ICollection<ElementId> selectedIds = uidoc.Selection.GetElementIds();
             
                        
            if (selectedIds.Count == 1)
            {
                   try
                       {
                           foreach (ElementId id in selectedIds)

                                {

                                Element LightFamilyType  = doc.GetElement(id);
                                //TaskDialog.Show("Element ID", LightFamilyType.Name.ToString());
                    
                                elemType = doc.GetElement(LightFamilyType.GetTypeId ());
                                
                                Element et = doc.GetElement(LightFamilyType.GetTypeId());
                                
                              
                                break;
                                
                    
                                   }
           
                       }        
                       catch
                       {
                           TaskDialog.Show("Error!!", "Please Select one and only one Light Fixture before running this program.");
                           return;
                       }
            }
            else
            {
                TaskDialog.Show("Error!!", "Please Select one and only one Light Fixture before running this program.");
                return;
            }
           
            
            System.Windows.Forms.Form myForm = new formEditLights ();
            myForm.ShowDialog();
            

		}
	}
}

 

 

 

0 Likes
Message 4 of 6

ricaun
Advisor
Advisor
Accepted solution

You are using 'static' meaning your TextBoxScheduleFix is like a global variable, only gonna exist one in your assembly. The next time your code is executed the same reference is used, because you are using 'static'.

 

If you remove 'static' should work like you expected, a new TextBox should be created each time you run the Macro.

 

Luiz Henrique Cassettari

ricaun.com - Revit API Developer

AppLoader EasyConduit WireInConduit ConduitMaterial CircuitName ElectricalUtils

0 Likes
Message 5 of 6

sragan
Collaborator
Collaborator

Removing the "static" designation causes a compile error:

 

Cannot access a non-static member of outer type 'Simplified_Edit_Lights.ThisApplication' via nested type 'Simplified_Edit_Lights.ThisApplication.formEditLights' (CS0038) - C:\ProgramData\Autodesk\Revit\Macros\2023\Revit\AppHookup\Simplified_Edit_Lights\Source\Simplified_Edit_Lights\ThisApplication.cs:69,17

 

It doesn't seem like it's a good idea to put all the code to update the parameters within the form, and this is the only way I have found to make any kind of variable accessible both within the form,  and in the subroutine to edit the parameters. (That's not completely true, I am aware that parameters can be passed to a form or a subroutine, but that seems like a lot of extra work, especially since I have a large number of textboxes that would have to be passed.)

 

If I leave the "static", is there a way to erase the variable and release the memory at the end of the program?

I've tried things like setting it equal to null, but I no matter what I try it seems to create an error message.

 

0 Likes
Message 6 of 6

ricaun
Advisor
Advisor

I see your code is entangled with the static TextBoxScheduleFix.

 

You could reassign a new TextBox inside the form constructor.

TextBoxScheduleFix = new System.Windows.Forms.TextBox ();

This gonna force to create a new TextBox in the static reference.

 

If you are using static is not gonna clear itself, that's the whole point of using static, the reference gonna stay there and you can reuse the same reference in any place inside your code.

 

Luiz Henrique Cassettari

ricaun.com - Revit API Developer

AppLoader EasyConduit WireInConduit ConduitMaterial CircuitName ElectricalUtils