How to retrieve Modeless form's ComboBox's text in ExternalEvent? C#

How to retrieve Modeless form's ComboBox's text in ExternalEvent? C#

EATREVITPOOPCAD
Collaborator Collaborator
1,677 Views
14 Replies
Message 1 of 15

How to retrieve Modeless form's ComboBox's text in ExternalEvent? C#

EATREVITPOOPCAD
Collaborator
Collaborator

I found this great example on how to create a modeless form. One thing I cant figure out is how to retrieve text from the ComboBox in in the external event?

 

Problem: new Form1(x,y).show(); is declared inside the class FormShow : IExternalCommand and the Transaction is happening in the  class ExHandler : IExternalEventHandler

 

Is there a way to create a some sort of a global string variable, and when a user clicks a button it sets that variable to

 

 

 

ComboBox selection? How would I declare that so I could pull this variable in external event? My form, application, and the command are all on different C# files in the solution. This makes my brain hurt 🤕

 

 

[Transaction(TransactionMode.Manual)]
    class FormShow : IExternalCommand
    {
        public Result Execute(ExternalCommandData c, ref string m, ElementSet e)
        {
            ExternalEvent x = ExternalEvent.Create(new ExHandler());
            UIApplication u = c.Application;
            new Form1(x, u).Show();
            return Result.Succeeded;
        }
        class ExHandler : IExternalEventHandler
        {
            public void Execute(UIApplication ap) { }
            public string GetName() { "form1" }
        }
    }

    class Form1 : Form
    {
        ExternalEvent x;
        UIApplication u;
        Document d;
        public Form1(ExternalEvent e, UIApplication a)
        {
            InitializeComponent();
            x = e;
            u = a;
            d = u.ActiveUIDocument.Document;
        }
        private void Button1_Click(object sender, EventArgs e)
        {
            x.Raise();
            using (Transaction t = new Transaction(d, "command"))
            {
                t.Start();
                //do something
                t.Commit();
            }
            this.Close();
        }
    }

 

The definition of insanity is doing the same thing over and over again and expecting different results
0 Likes
Accepted solutions (1)
1,678 Views
14 Replies
Replies (14)
Message 2 of 15

joshua.lumley
Advocate
Advocate

Easy:

//Add this line to ExHandler
public Form1 form1 {get; set;}


//updated this line
x.form1 = new Form1(x, u).Show();

 

Remove the transaction from Form1 and put it inside the curvy braces of the ExHandler execute method.

Message 3 of 15

3dimdev
Enthusiast
Enthusiast

Hi EatRevitPoopCAD,

First and foremost, exceptional imagery in your username.  Well done!  Second, you can add a property to your ExHandler class and fill it with the selected item string prior to raising it.  And I would move your ExHandler class to its own file as well.

 

The following is 100% modified on the fly and untested:

 

[Transaction(TransactionMode.Manual)]
class FormShow : IExternalCommand
{
    public Result Execute(ExternalCommandData c, ref string m, ElementSet e)
    {
        var x = ExternalEvent.Create(new ExHandler());

        var myImportantForm = new Form1(x);

        myImportantForm.Show();

        return Result.Succeeded;
    }
    class ExHandler : IExternalEventHandler
    {
        public void Execute(UIApplication ap)
        {
            using (Transaction t = new Transaction(ap.ActiveUIDocument.Document, "command"))
            {
                t.Start();
                //do something with ImportantInfo
                t.Commit();
            }
        }

        public string GetName() { return "form1"; }

        public string ImportantInfo { get; set; }
    }
}

class Form1 : Form
{
    ExternalEvent MyEventNeedsAGoodName;

    public Form1(ExternalEvent e)
    {
        InitializeComponent();

        MyEventNeedsAGoodName = e;
    }

    private void Button1_Click(object sender, EventArgs e)
    {
        MyEventNeedsAGoodName.ImportantInfo = "<yourSelectedItemString>";

        MyEventNeedsAGoodName.Raise();

        this.Close();
    }
}

 

 

3rd Dimension Developer
YouTube.com/@3DimDev
Message 4 of 15

EATREVITPOOPCAD
Collaborator
Collaborator

Thank you thank you for the username compliments 😎

 

I get the idea of how its supposed to work now, I just cant compile some of the lines of code.

 

var myForm = new Form1(x, u).Show();

Error	CS0815	Cannot assign void to an implicitly-typed variable

 

My form is a public partial class Form1 : System.Windows.Forms.Form instead of class Form1 : Form

Don't think that makes a difference though.

 

Another problem: When I add properties to the ExHandler class, I cant pull that property out of variables declared with ExternalEvent class. When I do that I get this error:

Error	CS1061	'ExternalEvent' does not contain a definition for 'OccupancyType' and no accessible extension method 'OccupancyType' accepting a first argument of type 'ExternalEvent' could be found (are you missing a using directive or an assembly reference?)

 

 

On ExternalEvent class variables I can get the following methods and properties: Dispose, Equals, GetHashCode, GetType, IsPending, Raise, and ToString

The definition of insanity is doing the same thing over and over again and expecting different results
0 Likes
Message 5 of 15

3dimdev
Enthusiast
Enthusiast

The Show() method does not return anything, i.e. void, and void cannot be assigned to a variable.  The fix, create an instance of your form and show it on two separate lines.

 

var myForm = new Form1(x, u);

myform.Show();

 

And my bad on the other error; the theory of it was ok, but I did not show a correct implementation.  You need to track both the ExternalEvent instance and the instance of your handler.  Pass them both into your form, your original code only passed the ExternalEvent.  Then set the property on the handler just before you raise the event.

 

Again written on the fly and untested:

    public Result Execute(ExternalCommandData c, ref string m, ElementSet e)
    {
        var eventHandler = new ExHandler();
        
        var myImportantForm = new Form1(eventHandler);

        myImportantForm.Show();

        return Result.Succeeded;
    }

 

class Form1 : Form
{
    ExternalEvent _myEventNeedsAGoodName;
    ExEvent _myEventHandler;

    public Form1(ExEvent myEventHandler)
    {
        InitializeComponent();
        _myEventHandler = myEventHandler;
        _myEventNeedsAGoodName = ExternalEvent.Create(myEventHandler);
    }

    private void Button1_Click(object sender, EventArgs e)
    {
        _myEventHandler.ImportantInfo = "<yourSelectedItemString>";

        _myEventNeedsAGoodName.Raise();

        this.Close();
    }
}

 

 

3rd Dimension Developer
YouTube.com/@3DimDev
Message 6 of 15

joshua.lumley
Advocate
Advocate

Excellent work! ...i'll accept the burden of being the accepted answer if you wish.

Message 7 of 15

EATREVITPOOPCAD
Collaborator
Collaborator

This is very helpful! I am very close but now I cant figure out how to pull the property from within the handlers' Execute() .... 

 

I tried it with the dumb string test = OccupancyType; and its showing up blank, which I get why it is... But I cant pass the created handler as a parameter as that same handler cast. 

 

I pasted the code I am working with now so there is less confusion with the variable names...

 

 

namespace ApplyOccupancy
{
    [Transaction(TransactionMode.Manual)]
    public class Command : IExternalCommand
    {
        public Result Execute(
          ExternalCommandData commandData,
          ref string message,
          ElementSet elements)
        {
            var eventHandler = new ExHandler();
            ExternalEvent myExternalEvent = ExternalEvent.Create(new ExHandler()); 
            UIApplication uiapp = commandData.Application;
            var myForm = new Form1(eventHandler,myExternalEvent);
            myForm.Show();

            return Result.Succeeded;
        }

        public class ExHandler : IExternalEventHandler
        {
            public void Execute(UIApplication uiapp)
            {
                using (Transaction t = new Transaction(uiapp.ActiveUIDocument.Document, "command"))
                {

                    t.Start();

                    Selection sel = uiapp.ActiveUIDocument.Selection;
                    RoomPickFilter roomPickFilter = new RoomPickFilter();
                    IList<Reference> Rooms = sel.PickObjects(ObjectType.Element, roomPickFilter, "Select rooms to apply the occupancy to:");






                    // NEED TO GET OCCUPANCY TYPE HERE

                    string test = OccupancyType;
                    TaskDialog.Show("test", test);

                    // NEED TO GET OCCUPANCY TYPE HERE







                    t.Commit();
                }
            }

            public string OccupancyType { get;set; }
            public string GetName() { return "form1"; }
        }


        public class RoomPickFilter : ISelectionFilter
        {
            public bool AllowElement(Element e)
            {
                return (e.Category.Id.IntegerValue.Equals((int)BuiltInCategory.OST_Rooms));
            }

            public bool AllowReference(Reference r, XYZ p)
            {
                return false;
            }
        }
    }




    public partial class Form1 : System.Windows.Forms.Form
    {
        private string OccupancyType;

        ExternalEvent _MyExternalEvent;
        Command.ExHandler _MyEventHandler;

        public Form1(IExternalEventHandler MyEventHandler, ExternalEvent MyExternalEvent)
        {
            InitializeComponent();
            _MyEventHandler = MyEventHandler as Command.ExHandler;
            _MyExternalEvent = MyExternalEvent;
        }

        private void Apply_Button_Click(object sender, EventArgs e)
        {
            _MyEventHandler.OccupancyType = OccupancyType;
            _MyExternalEvent.Raise();
            this.Close();
        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.OccupancyType = comboBox1.Text;
        }
    }



}

 

 

The definition of insanity is doing the same thing over and over again and expecting different results
0 Likes
Message 8 of 15

3dimdev
Enthusiast
Enthusiast

I think you're getting very close!  In your command method you've created two instances of the handler.  Just create the one instance and you should be golden.

 

        public Result Execute(
          ExternalCommandData commandData,
          ref string message,
          ElementSet elements)
        {
            var eventHandler = new ExHandler();
            ExternalEvent myExternalEvent = ExternalEvent.Create(eventHandler ); 
            UIApplication uiapp = commandData.Application;
            var myForm = new Form1(eventHandler,myExternalEvent);
            myForm.Show();

            return Result.Succeeded;
        }

  

3rd Dimension Developer
YouTube.com/@3DimDev
Message 9 of 15

EATREVITPOOPCAD
Collaborator
Collaborator

This is a silly question by I still cant pull the OccupancyType value inside the ExHandler class.  

 

Is this the right way to do it? Because I get a blank string for OccupancyType with the code below, after setting _MyEventHandler.OccupancyType = OccupancyType; in the Form1 class

                    // NEED TO GET OCCUPANCY TYPE HERE

                    string test = OccupancyType;
                    TaskDialog.Show("test", test);

                    // NEED TO GET OCCUPANCY TYPE HERE

 

Thank you for being patient with my ape brain 😁 

 

 

 

The definition of insanity is doing the same thing over and over again and expecting different results
0 Likes
Message 10 of 15

3dimdev
Enthusiast
Enthusiast

That is the correct way to access the OccupancyType property, so the error must be somewhere else.  I would set a breakpoint, or points as needed, and start debugging.  My top two suspicions are, are you absolutely sure you're dealing with the same single instance of your ExHandler class everywhere, and, is the form properly setting the ExHandler.OccupancyType when the combobox selection changes.

3rd Dimension Developer
YouTube.com/@3DimDev
Message 11 of 15

EATREVITPOOPCAD
Collaborator
Collaborator

Got it! This is a new concept for me and hard to wrap my head around, but I think I got it now. I will play with the code tonight and post an update

The definition of insanity is doing the same thing over and over again and expecting different results
0 Likes
Message 12 of 15

EATREVITPOOPCAD
Collaborator
Collaborator

This is sad. Everything seems good, and in the form class everything works properly, such as 

_MyEventHandler.OccupancyType = OccupancyType; and 
this.OccupancyType = comboBox1.Text;
 
OccupancyType inside ExHandler stays null...
 
I pasted the current code. Now that I know at least how to retrieve properties outside of the  ExHandler definition, I believe I can ghetto rig, maybe by making a second external event handler and have it looping to check if the first one finished doing its thing... But this is running away from the problem, which is not healthy... But better than my last resort: create a temporary file and read / write data to it 🤠
 

 

namespace ApplyOccupancy
{
    [Transaction(TransactionMode.Manual)]
    public class Command : IExternalCommand
    {
        public Result Execute(
          ExternalCommandData commandData,
          ref string message,
          ElementSet elements)
        {
            var eventHandler = new ExHandler();
            var myForm = new Form1(eventHandler);
            myForm.Show();

            return Result.Succeeded;
        }

        public class ExHandler : IExternalEventHandler
        {
            public void Execute(UIApplication uiapp)
            {
                using (Transaction t = new Transaction(uiapp.ActiveUIDocument.Document, "command"))
                {

                    t.Start();

                    Selection sel = uiapp.ActiveUIDocument.Selection;
                    RoomPickFilter roomPickFilter = new RoomPickFilter();
                    IList<Reference> Rooms = sel.PickObjects(ObjectType.Element, roomPickFilter, "Select rooms to apply the occupancy to:");




                    // NEED TO GET OCCUPANCY TYPE HERE

                    string test = OccupancyType;
                    TaskDialog.Show("test", test);

                    // NEED TO GET OCCUPANCY TYPE HERE





                    t.Commit();
                }
            }

            public string OccupancyType { get;set; }
            public string GetName() { return "form1"; }
        }


        public class RoomPickFilter : ISelectionFilter
        {
            public bool AllowElement(Element e)
            {
                return (e.Category.Id.IntegerValue.Equals((int)BuiltInCategory.OST_Rooms));
            }

            public bool AllowReference(Reference r, XYZ p)
            {
                return false;
            }
        }
    }


    public partial class Form1 : System.Windows.Forms.Form
    {
        private string OccupancyType;

        ExternalEvent _MyExternalEvent;
        Command.ExHandler _MyEventHandler;

        public Form1(IExternalEventHandler MyEventHandler)
        {
            InitializeComponent();
            _MyEventHandler = MyEventHandler as Command.ExHandler;
            _MyExternalEvent = ExternalEvent.Create(new Command.ExHandler());
        }

        private void Apply_Button_Click(object sender, EventArgs e)
        {
            _MyEventHandler.OccupancyType = OccupancyType;
            _MyExternalEvent.Raise();
            this.Close();
        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.OccupancyType = comboBox1.Text;
        }
    }

}

 

The definition of insanity is doing the same thing over and over again and expecting different results
0 Likes
Message 13 of 15

3dimdev
Enthusiast
Enthusiast
Accepted solution

No need to be sad.  Learning something new is an experience to relish.  It's still an issue of creating multiple instances of the event handler.  Try this edit to the form constructor.  

 

        public Form1(Command.ExHandler MyEventHandler)
        {
            InitializeComponent();
            _MyEventHandler = MyEventHandler;
            _MyExternalEvent = ExternalEvent.Create(_MyEventHandler);
        }

 

Also, not related to the current issue, but moving the ExHandler class definition outside of your command class will remove the need to access it through the Command.

3rd Dimension Developer
YouTube.com/@3DimDev
Message 14 of 15

EATREVITPOOPCAD
Collaborator
Collaborator

It worked! I'm in there like swim wear... Thank you so much. My ability to make magic happen in Revit just got exponentially increased thanks to you sir. 

The definition of insanity is doing the same thing over and over again and expecting different results
Message 15 of 15

3dimdev
Enthusiast
Enthusiast

Please keep making magic Mr. PoopsCAD, I can say with 100% sincerity I can't wait to hear from you again!  I think this will be the topic of my next video.  Thank you for the inspiration!

3rd Dimension Developer
YouTube.com/@3DimDev