External Event with User Inputs from a Modeless Form

External Event with User Inputs from a Modeless Form

sroswurm4GJQU
Advocate Advocate
2,246 Views
6 Replies
Message 1 of 7

External Event with User Inputs from a Modeless Form

sroswurm4GJQU
Advocate
Advocate

I'm working on an implementation of a modeless form that raises an external event in response to UI inputs from the user.  In concept, this is extremely similar to the SDK code example "ModelessForm_ExternalEvent".  I've been studying, testing, and debugging that example, and now feel like I understand 99% of what it's doing.  It's the final 1% that has me totally stumped.

 

As I understand it, the class for the RequestHandler contains a public property "Request" that tells the Execute() method which form control was clicked by the user.  As a result, the behavior of the Execute() method can be modified from outside the handler by setting the value of the RequestHandler.Request property.  This all makes perfect sense.

 

What I do not understand is how the ExternalEvent can reflect the effects of the user selection when it was created from an old instance of RequestHandler before the form was loaded.  Here is the ShowForm method:

 

public void ShowForm(UIApplication uiapp)
{
    // If we do not have a dialog yet, create and show it
    if (m_MyForm == null || m_MyForm.IsDisposed)
    {
        // A new handler is declared to handle request posting by the dialog
        RequestHandler handler = new RequestHandler();
 
        // External Event for the dialog to use (to post requests)
        ExternalEvent exEvent = ExternalEvent.Create(handler);
 
        // We give the objects to the new dialog;
        // The dialog becomes the owner responsible for disposing them, eventually.
        m_MyForm = new ModelessForm(exEvent, handler);
        m_MyForm.Show();
    }
}

 

The handler and external event are then passed into the form and read into the form's private properties:

 

 

public partial class ModelessForm : Form
{
   //Private properties of the form
   private RequestHandler m_Handler;
   private ExternalEvent m_ExEvent;
 
   public ModelessForm(ExternalEvent exEvent, RequestHandler handler)
   {
      InitializeComponent();
      m_Handler = handler;//Private form handler property is set to match the argument passed from application
      m_ExEvent = exEvent;//Private form event property is set to match the argument passed from application
   }

 

Finally, when the user clicks a button, the MakeRequest() method is invoked, which sets the properties of the private handler that belongs to the form and then the event is raised:

 

 

private void MakeRequest(RequestId request)
{
   m_Handler.Request.Make( request );//Set the handler property to reflect the user-selected option
   m_ExEvent.Raise();
   DozeOff();
}

 

So here is my question.  Why/How does the private form property m_ExEvent reflect the changes made to the private property m_Handler when it's raised?  There is technically no inheritance or any other relationship between m_Handler and m_ExEvent.  The ExternalEvent m_ExEvent is not created directly from the newly modified m_Handler.  It's essentially just a copy of the old original event that was created from the old handler BEFORE the form was launched. 

 

I've tested it and can see with my own eyes that it works properly, but I don't understand why  m_ExEvent.Raise() automatically adapts to the updated m_Handler.  Any suggestions as to why this is the case?

0 Likes
Accepted solutions (1)
2,247 Views
6 Replies
Replies (6)
Message 2 of 7

jlpgy
Advocate
Advocate

Hi:

Try understand it like this:

  1. var m_Handler = new YourHandlerType( "anyParamYouNeed" ); You get yourself a new <Handler> object
  2. var m_Event = ExternalEvent.Create(m_handler); // Now Revti will know that THIS EVENT IS REALTED TO THIS HANDLER
  3. You call m_Event.Raise() method. Revit then receives a <Raise> singal.
  4. Revit calls the method m_Handler.Execute(uiApp);  // uiApp is gemerated by Revit
  5. The program controling runs into your codes. You now have full control of the program.
  6. The <YourHandlerType> is totally defined by yourself, You can certainly add some C# Properties or methods .

For example:

You defined an enum member property in <YourHandlerType>

 

public class YourHandlerType : IExternalEventHandler
{
    public ... Execute(...)  // sorry for being lazy
    {
        switch (MethodMember)
        {
            case MyMethod.Method_1:
            // do something 1
            break;
            case MyMethod.Method_2:
            // do something 2
            break;
    }
    
    public MyMethod MethodMember {get;set;} = MyMethod.Method_1;
}

public enum MyMethod
{
    Method_1,
    Method_2,
}

 

However the handler does, is fully depending on your own demands.

You can do it like this before calling Raise() method:

 

if (someCondition_1)
{
    m_Handler.MethodMember = MyMethod.Method_1;
    // something similar to m_Handler.Request.Make(...)
}
else if (someCondition_2)
{    
    m_handler.MethodMember = MyMethod.Method_2;
}
m_Event.Raise();

 

Passing some data to the m_Hander object can fully control how you <handler> works

单身狗;代码狗;健身狗;jolinpiggy@hotmail.com
0 Likes
Message 3 of 7

Kennan.Chen
Advocate
Advocate

The reference of IExternalEventHandler instance you created is held by the ExternalEvent instance which is returned by ExternalEvent.Create() method. When you call Raise() method, The ExternalEvent instance just send the IExternalEventHandler instance to Revit. Revit adds the IExternalEventHandler  instance to a queue where it waits to be executed by Revit synchronously.

 

Here's a piece of code to show how things are working logically (more complicated actually).

public class ExternalEvent
{
    //reference to the handler you created
    private IExternalEventHandler m_Handler;

    private ExternalEvent(IExternalEventHandler handler)
    {
        this.m_Handler = handler;
    }

    public static ExternalEvent Create(IExternalEventHandler handler)
    {
        return new ExternalEvent(handler);
    }

    public ExternalEventRequest Raise()
    {
        //Send the m_Handler to Revit to be executed
    }
}

 

Since it's all about reference, anything you have done with the IExternalEventHandler instance before or after Raise() method will be there when the IExternalEventHandler instance finally get executed.

 

You can also refer to a library wrapping around ExternalEvent to get better understanding by visiting https://github.com/KennanChan/Revit.Async

0 Likes
Message 4 of 7

sroswurm4GJQU
Advocate
Advocate

Thanks for your answer.  I think that makes pretty good sense.  I suppose my only question is how can it be that the private property of the form "m_Handler" contains a reference to the other private property of the from "m_exEvent".  Since the event was not directly created from the handler, how does the reference relationship occur?

 

In the following code block, do the lines in bold create copies of the arguments passed into the form, or do they create references to the original arguments?

 

public partial class ModelessForm : Form
  {
     //Private properties of the form
     private OpenFileHandler m_Handler;
     private ExternalEvent m_ExEvent;
 
     public ModelessForm(ExternalEvent exEvent, OpenFileHandler handler)
     {
        InitializeComponent();
        m_Handler = handler;//Private form handler property is set to match the argument passed from application
        m_ExEvent = exEvent;//Private form event property is set to match the argument passed from application
     }

 

 

0 Likes
Message 5 of 7

sroswurm4GJQU
Advocate
Advocate

Your explanation makes good sense and definitely aligns with my understanding of what is happening.  What throws me off is that the declaration you show in line 2 never actually occurs directly.  There is no declaration that explicitly relates m_Event to m_Handler.  Instead, they are each assigned via equality to the arguments passed into the form.  So why does m_Event.Raise() call the new handler instead of the old handler?

 

Or does the equality create a reference, meaning that changing the private m_Handler also changes the original Handler argument passed in from outside the form?

0 Likes
Message 6 of 7

Kennan.Chen
Advocate
Advocate
Accepted solution

Instance of a class is always used as reference in C#.

0 Likes
Message 7 of 7

sroswurm4GJQU
Advocate
Advocate

Aaaaaaaaaaaah.  That is what I was missing.  Fundamentals of .NET.  Everything is pointing back to the original class instance declarations.

 

"A class is a reference type. When an object of the class is created, the variable to which the object is assigned holds only a reference to that memory. When the object reference is assigned to a new variable, the new variable refers to the original object. Changes made through one variable are reflected in the other variable because they both refer to the same data."

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/ 

 

Thanks again!!!!

 

Thanks again!!!!!

 
0 Likes