When interacting with custom controls on your pages from C/AL, you must be absolutely sure that the control has been instantiated. If it is not, you’ll get an error such as this:
The reason why this happens is that C/AL code gets to execute before the page has been rendered, and thus also before the custom controls have been instantiated.
Your first idea might be to use the ISNULL system function and check if control is instantiated, for example, like this:
This will compile, but it will fail because the controls will never be NULL. In my opinion it is a flaw in the .NET Interoperability implementation, but it could be argued the other way around (like, for example, that the CurrPage.Name is just a placeholder for a control, but that explanation would not hold up against the whole concept of how you are accessing and interacting with the controls). Whatever it is, a bug or a feature, it does not work and you need to do something else.
That something else is creating an event, which I like to call ControlAddInReady (it may have any name you like). This is how the control add-in code could look with this event added:
This will expose a new event trigger in your C/AL code, which you can use to change the code like this:
This makes sure that you only access the control add-in if it has been instantiated.
Now, the only thing that remains is to raise this ControlAddInReady event from your C# control code. And here comes a tricky part, because if you think the following piece of code solves the problem, you are wrong:
This is a standard event-rising pattern in C#, and it compiles, and executes nicely, however, when this code is hit, the ControlAddInReady is indeed null, and no event handlers are attached to it, therefore, the event never fires.
Let me first explain why it never fires. When the extensibility framework calls the CreateControl method, it may not yet have completed the rendering of the page, and therefore the C/AL event triggers for the control events (such as this ControlAddInReady event trigger) on the page have not yet been bound as event handlers for events. This binding may happen at a later stage, after the CreateControl has already completed its work. Now, I said “may” two times, because depending on how exactly you implement your control (which class you inherit from or which interfaces you implement), it may or may not be true. Again – this could just be another bug in the extensibility framework, or another feature which you have to cope with. In any case, just checking if ControlAddInReady is assigned will not do the trick, as in most situations the event handlers are just never assigned when this block of code is hit.
And now let me explain how to implement it properly.
When the extensibility framework calls the CreateControl method, it has not yet completed the rendering of the page and it has not yet placed the control onto the page. If you want, the control exists only in memory. When the rendering of the page completes, the control you created in the CreateControl method is placed onto the page, and in terms of Windows Forms terminology, it’s parent has changed. At this time, all the event triggers have been properly bound as event handlers in your control, so the only thing you need to do is to respond to the ParentChanged event of the control you instantiated. In effect, this is what your code should look like:
Here, I’ve used a lambda syntax, but if you are not yet on friendly terms with lambdas in C#, this is what it effectively translates to:
And when you do it like this, you fire the ControlAddInReady trigger when the parent of the _textBox control (or whichever other control you instantiate) changes, which is at the time the control is actually placed on the page.
This Post Has 19 Comments
Very helpful! Thanks!!
Thank you Vjeko, this works.
Now I only have to find out how to resize the panel dynamically.
@Gunnar: what do you mean by this? Responding to the resize event of the page, or being able to programmatically define a new size and then have the page resize accordingly?
If you subscribe to the parentchanged of the parent (few times), you have access to the whole page and you can go through all controls on it and do whatever you want and are allowed too… 😉 (e.g. getting the FORM from the TopMostParent and set it to maximized state etc.)
Thanks for explaining this Vjeko – I’ve used this code for a while (I think it’s part of Christian’s VS Templates http://blogs.msdn.com/b/cabeln/) but not really understood the ParentChanged bit.
Now, can you explain why sometimes ControlAddInReady is fired twice?
I don’t know the exact sequence of events that may cause the ParentChanged event to fire multiple times. However, Vjekoslav mentioned at Nav Techs Days last week that it is better to subscribe to the HandleCreated event. This is guaranteed to fire only once and to will not throw an exception because the object has not been instantiated.
I also don’t know the sequence of events that can result in this, but HandleCreated is only going to be fired once per control. This is a hint from Arend-Jan Kauffmann, by the way – not my idea, so credit goes to him.
Thanks for reminding me of the developer who you mentioned in the NAV Tech Days session about this. I will update my blog post (just reblogged about this here.)
Pingback: Adding a ControlAddInReady event to custom controls – 10/23, Navigate Into Success |
Pingback: My first dynamic Windows Client Add-in | Gunnar's Blog
Pingback: Tips & Tricks – .Net Control Add-in Ready Event | …I digress
This was a life saver.
Pingback: Adding a ControlAddInReady event to custom controls | PA
I have checked so many documentations on this and they say NOTHING about this strange events behavior. I believe I lost a good amount of my hair while trying to understand what was wrong. You, sir, are my hero.
Absolutely. Take a look at my TechDays sessions of 2015 and 2016, and stay tuned for the demos of TechDays 2018, there will be a lot of these examples.
Pingback: NAV TechDays 2018 Demos: Customer Star Rating - Vjeko.com