[CE U14 C#] What is the correct way to use ModelChangeEvent and NewDesignFileEvent?

Hello, 

I'm developing a C# Addin for Microstation CE as a drawing border control tool for adding stamps and revision control.

The program needs auto run on drawing open and also run if the user changes to another file.

From my research it looks like I will need to use Addin.NewDesignFileEvent and Addin.ModelChangedEvent methods.

The former works perfectly however I can not get the latter method to run correctly when changing to another file.

I've tried two methods below, 

Method 1: Routine appears to run but it runs before the model has finished loading and I get the following exception.

Method 2: No error message however NewDesignFileEvent does not get triggered.

Method 1:

----------------------------------------------------------------------------------------------------------------------------------

protected override int Run(string[] commandLine)
        {
            // save a reference to our addin to prevent it from being garbage collected.
            s_Addin = this;

            // Subscribe to OpenFile Event
            s_Addin.NewDesignFileEvent += Addin_NewDesignFileEvent;

            // Subscribe to new ModelChange Event
            //s_Addin.ModelChangedEvent += S_Addin_ModelChangedEvent;

            return 0;
        }
        
        private void Addin_NewDesignFileEvent(MPN.AddIn sender, NewDesignFileEventArgs eventArgs)
        {
            InitaliseDictionaries();
            ResetMSAttributes();
            RunBorderCheck();

            // Subscribe to new ModelChange Event
            s_Addin.ModelChangedEvent += S_Addin_ModelChangedEvent;

            // Unsubscribe to OpenFile Event
            s_Addin.NewDesignFileEvent -= Addin_NewDesignFileEvent;

        }
        private void S_Addin_ModelChangedEvent(MPN.AddIn senderIn, ModelChangedEventArgs eventArgsIn)
        {
            InitaliseDictionaries();
            ResetMSAttributes();
            RunBorderCheck();
        }

Method 2:

----------------------------------------------------------------------------------------------------------------------------------

        protected override int Run(string[] commandLine)
        {
            // save a reference to our addin to prevent it from being garbage collected.
            s_Addin = this;

            // Subscribe to OpenFile Event
            s_Addin.NewDesignFileEvent += Addin_NewDesignFileEvent;

            // Subscribe to new ModelChange Event
            //s_Addin.ModelChangedEvent += S_Addin_ModelChangedEvent;

            return 0;
        }
        
        private void Addin_NewDesignFileEvent(MPN.AddIn sender, NewDesignFileEventArgs eventArgs)
        {
            InitaliseDictionaries();
            ResetMSAttributes();
            RunBorderCheck();

            // Subscribe to new ModelChange Event
            s_Addin.ModelChangedEvent += S_Addin_ModelChangedEvent;

            // Unsubscribe to OpenFile Event
            s_Addin.NewDesignFileEvent -= Addin_NewDesignFileEvent;

        }
        private void S_Addin_ModelChangedEvent(MPN.AddIn senderIn, ModelChangedEventArgs eventArgsIn)
        {
            // Subscribe to OpenFile Event
            s_Addin.NewDesignFileEvent += Addin_NewDesignFileEvent;
        }
        
        

I'm new to programming, is there something obvious I'm missing here?

Any help will be greatly appreciated.

Regards

Jai 

  • It's not a good idea to modify the event handlers inside an existing event handler.  You should leave them alone, so they're ready for the next file/model open/close event. Look in my answer to this question for more detail.

    You should check the information you're given in ModelChangedEventArgs.ChangeType Enumeration and NewDesignFileEventArgs.When.  Perform your initialisation when you receive NewDesignFileEventArgs.When.AfterDesignFileOpen.

    private void Addin_NewDesignFileEvent(MPN.AddIn sender, NewDesignFileEventArgs eventArgs)
    {
        if (eventArgs.WhenCode == NewDesignFileEventArgs.When.AfterDesignFileOpen)
        {
            InitaliseDictionaries();
            ResetMSAttributes();
            RunBorderCheck();
        }
    }

     
    Regards, Jon Summers
    LA Solutions

  • Hi Jai,

    as Jon wrote, to change event handlers inside event handler is (nearly) always very bad idea. Moreover, I do not see any reason to do that.

    You wrote that you want to monitor NewDesignFileEvent and ModelChangedEvent. At first, I recommend to check whether you need to monitor both. Without writing test code I cannot say, but isn't ModelChangedEvent raised every time, so maybe NewDesignFileEvent is duplication?

    You have to check the reason of the event (expressed by its argument), because the same event is raised several times (in the discussed situation, e.g. before and after file is opened). Typically code has to ensure an activity is started when everything is finished, so they there will be no collision between MicroStation operations and what code want to do.

    I do not know why you want to unregister NewDesignFileEvent (especially inside the same event). When you are not temporarily interested in the event, often it's simpler to use internal semaphor and to early escape from the function, instead to constantly register/unregister event handlers.

    With regards,

      Jan

  • Hi Jon 

    Thanks for the reply,

    I have tried your solution however microstation will crash when I load a new file.

    it appears to be getting the error on view 1 open?

            private void Addin_NewDesignFileEvent(MPN.AddIn sender, NewDesignFileEventArgs eventArgs)
            {
               if (1 == Convert.ToInt32(NewDesignFileEventArgs.When.AfterDesignFileOpen))
                {
                    InitaliseDictionaries();
                    ResetMSAttributes();
                    RunBorderCheck();                
                }
                return;
            }
            private void S_Addin_ModelChangedEvent(MPN.AddIn senderIn, ModelChangedEventArgs eventArgsIn)
            {
                if (Convert.ToString(ModelChangedEventArgs.ChangeType.Name) == "Border Sheet")
                {
                    InitaliseDictionaries();
                    ResetMSAttributes();
                    RunBorderCheck();
                }
            }

    Exception 0xe0434352 at Wed Jan 20 16:15:48 2021
    
    MicroStation version 10.14.02.01 Windows x64 
    
    Input queue history:
        {   0} 16:15:048.460 	view 1 open
        {   0} 16:15:048.370 <RIBBON SETTITLE "C:\Users\jai.hensgen\OneDrive - Rio Tinto\Projectwise\MS Test - Copy.dgn [2D - V8 DGN] - Miߥ0nj> taskId=[RIBBONVIEW] from MDL
        {   0} 16:15:048.369 <RIBBON SETTITLE "C:\Users\jai.hensgen\OneDrive - Rio Tinto\Projectwise\MS Test - Copy.dgn [2D - V8 DGN] - Miߥ0Ų> taskId=[RIBBONVIEW] from MDL
        {   0} 16:15:048.280 	Activate: C:\Users\jai.hensgen\OneDrive - Rio Tinto\Projectwise\MS Test - Copy.dgn[Border Sheet]
        {   0} 16:15:048.234 	view 1 close
        {   0} 16:15:048.208 <RIBBON CUSTOMIZE CLOSE > taskId=[RIBBONVIEW] from MDL
        {  19} 16:15:048.124 <NEWFILE "C:\Users\jai.hensgen\OneDrive - Rio Tinto\Projectwise\MS Test - Copy.dgn"> from MDL

    It's not a good idea to modify the event handlers inside an existing event handler. 

    Noted, it was added in to stop microstation crashing, however i couldn't get it to start up again.

    Regards
    Jai 

  • Crashing...
    if (1 == Convert.ToInt32(NewDesignFileEventArgs.When.AfterDesignFileOpen))
    

    That weird test doesn't test anything! AfterDesignFileOpen is an enum member that is defined to be 1, so your test is always true.

    Test the passed argument eventArgs.WhenCode as I suggested.

     
    Regards, Jon Summers
    LA Solutions

  • I got a type error

    You have type there I guess, it's not When, but WhenCode.

    What about this?

    private void AddinMain_NewDesignFileEvent(AddIn sender, NewDesignFileEventArgs eventArgs)
    {
        switch (eventArgs.WhenCode)
        {
            case NewDesignFileEventArgs.When.BeforeDesignFileClose:
                break;
            case NewDesignFileEventArgs.When.AfterDesignFileOpen:
                break;
        }
    }

    Regards,

      Jan

  • I got a type error

    See corrections above.

    Another event you could handle is...

    Session.Instance.OnMasterFileStart += OnMasterFileStart;

    Here's an implementation...

    private static void OnMasterFileStart(DgnFile dgnFile)
    {
        string s = null;
        switch (s_AddIn.ApplicationType)
        {
            case MdlApplicationType.DesignApp:
                s = $"Opening DGN file '{dgnFile.GetFileName()}' as DGNAPP";
                break;
            case MdlApplicationType.User:
                s = $"Opening DGN file '{dgnFile.GetFileName()}' as USER";
                break;
            case MdlApplicationType.InitApp:
                s = $"Opening DGN file '{dgnFile.GetFileName()}' as INITAPP";
                break;
        }
        //  Enable debug messages in MicroStation's Message Center to see MessageType.Debug
        MessageCenter.Instance.ShowMessage(MessageType.Debug, s, s, MessageAlert.None);
        // Initialise stuff here
    }

     
    Regards, Jon Summers
    LA Solutions

  • I have tried both the switch and if arguments however microstation is still crashing.


    Here's an implementation...

    What do i reference DgnFile to? It wants Dgn.PlatformNET however i have already got that referenced and Bentley.DgnPlatform.DgnFile exists in two references.

  • I have tried your solution however microstation will crash when I load a new file.

    Did you try to debug the project from Visual Studio? It should help to identify better what code raised the exception.

    Alternatively, you should configure MicroStation to create full memory dump including CLR information, so it can be analyzed using e.g. WinDbg tool.

    But because your code is wrong and not functional (see below), it's probably too early to try to debug what's happening.

    it was added in to stop microstation crashing

    Sorry, but it does not make sense and is completely against all software development practices. When a code does not work, the solution for sure is not to add another code to ignore the wrong one.

    Did you try to (when probably you did not debug the code) remove your calls from the events to check whether the exception is raised by calling the even handler itself or by calling your next code in a middle of event processing?

    if (1 == Convert.ToInt32(NewDesignFileEventArgs.When.AfterDesignFileOpen))

    You mentioned you are new to programming, but when I look at your code, I would recommend to stop write code for MicroStation and to attend and finish complete C# development course.

    The quoted condition is nonsense, because always evaluated to true, because 1 == 1 condition is tested. You do not test eventArg argument, but enum member value. Moreover, to test any enum argument against hard coded value is serious bug.

    It should be:

    if (eventArgs.WhenCode == NewDesignFileEventArgs.When.AfterDesignFileOpen)
    {
    
    }

    if (Convert.ToString(ModelChangedEventArgs.ChangeType.Name) == "Border Sheet")

    It's similar situation as the previous one, the condition is just bad.

    You compare ModelChangedEventArgs.ChangeType.Name, which is enum type with value 10, with some string. Never can happen. Plus, to compare strings is nearly always wrong idea, and not only because it's terribly slow (not many operations are slower than string operations), but also because of localization sensitivity.

    I do not know what event type you want to monitor, but I am sure you have to both check event type and also what model type is active. Probably something similar to:

    if (eventArgsIn.Change == ModelChangedEventArgs.ChangeType.Active)
    {
        if (eventArgsIn.DgnModelRef.GetDgnModel().ModelType == DgnModelType.Sheet)
        {
    
        }
    }

    Regards,

      Jan

  • It looks like my dictionaries were causing the problem, I just needed to clear all three before FileClose and away we go.

            private void Addin_NewDesignFileEvent(MPN.AddIn sender, NewDesignFileEventArgs eventArgs)
            {
                switch (eventArgs.WhenCode)
                {
                    case NewDesignFileEventArgs.When.BeforeDesignFileClose:
                        dCell.Clear();                    
                        dTagSet.Clear();                  
                        dSheetSize.Clear();               
                        break;
                    case NewDesignFileEventArgs.When.AfterDesignFileOpen:
                        Initalise();               
                        break;
                }
            }

    You mentioned you are new to programming, but when I look at your code, I would recommend to stop write code for MicroStation and to attend and finish complete C# development course.

    Yes this is the first program I have written, I started just before Christmas break and will be signing up to a course in February.

    The rest of the code I have written for this project does not have these nonsense conditions.


    Thank you both for your time.

    Regards

    Jai