There are situations when you want to exchange data between different objects in NAV, and there is no simple way to do it. One of those, that a friend stumbled upon a couple of days ago goes like this: you have a page which shows a subpage, that is linked to a factbox. Depending on the situation in the lines, you the factbox shows content from one table or another.
Your instincts may yell “ProviderID” at this moment, but there are some problems with it. ProviderID is used to set the link between different part controls on the same page. But if the link from the provider control results in no record being selected in the target part control, then the OnAfterGetRecord trigger in it does not fire, and you cannot update the content.
Another example may be this: depending on the line selected in the lines part, you may want to show either of two factboxes. Imagine – on a sales order, if you are on a line that sells a resource, you want to show the Resource factbox; and if you are on a line that sells an item, you want to show the Item factbox. You get the gist. There are not many ways you can achieve this.
As a matter of fact, when helping my colleague, I couldn’t think of any.
Except for an old trick which, unfortunately, does not work at all in the RTC. In the good old days of the Classic client, when heroes roamed the Earth, you could pass the CurrForm as a reference onto a function in a subform, which would then store the reference to the main form in its own Form variable. Then, when something happened in the lines, and you wanted to let the master page know, you called the function on the stored reference. It worked like charm. However, in NAV 2013 (and possibly 2009 – can’t bother to check) you cannot assign one page variable to another. Furthermore, the CurrPage variable has page ID in some funny range, and does not even correspond to the actual page number. All in all, no way you can pass, or retain, a reference to a page object, let alone the current page reference.
So, how do you have two, three, or more pages (or page parts, for that matter) talk to each other to pass some information when needed, in all those situations when ProviderID simply doesn’t get the job done (I could bore you to death listing those situations)?
.NET interoperability, what else.
If a page reference cannot be shared, some other reference can: a reference to an instance of a .NET object. So, imagine you had an object that has a method, and an event. You call the method, and the method raises the event. If multiple objects use the same reference of the object, the event is raised in multiple objects in NAV.
So, imagine this code:
If your imagination fails you, I’ve provided the download. Just click here and use the objects. I promise, they don’t bite.
I’ll give you two use cases here.
1. Calling a method in a master page from a subpage
In a master page (e.g. Sales Order) and a subpage (e.g. Sales Order Subform) declare a global DotNet variable of subtype EventManager.Dispatcher. In the subpage, declare a function SetDispatcher that receives EventManager.Dispatcher as a parameter. In this function assign the parameter object to the global variable of type Dispatcher. Then instantiate (construct) the Dispatcher in the OnOpenPage trigger of the master page, and call the SetDispatcher method on the subpage, passing this object to it. Make sure that the Dispatcher object on the master page has the WithEvents set to Yes.
When you need to call the master page from the subpage, simply call the Dispatcher.Dispatch method, and the event will fire on the master page.
You can use the Source, Target, and Data parameters on the Dispatch method to pass any data to event subscribers. Source should somehow indicate who is sending the data, target should indicate which (of possible many) subscriber should respond to the event, and Data should contain, well – give it a guess All of them are of type System.Object, so you can pass just about anything.
2. Dispatching data to multiple independent objects across the application
This is more of a tipsy and tricksy scenario than of a real-life one, but still, if you ever feel an urge to go bananas, use this trick.
Create one single-instance codeunit that has one global variable of type Dispatcher. Have one global function GetDispatcher that receives a parameter of type Dispatcher by reference. Within this method, check if the Dispatcher variable of the codeunit is null, and if it is, then first instantiate it. Then assign this global variable to the by reference parameter.
Then, from whichever object is interested in whoever is dispatching whatever (my late English teacher, may she rest in peace, would just love this sentence ) you call this GetDispatcher method to get the reference to this global dispatcher that rules them all. In each object interested in the chatter, have the Dispatcher variable subscribe to the events, so you get the OnDispatch event in that object.
Then, whenever an object feels like tweeting some news to the crowd, call the Dispatcher.Dispatch method from there. In each OnDispatch event write code that listens only to those dispatch events (source) that the target is interested in.
Sit back, grab a bag of popcorn, and watch the show. Simple, clean, efficient, and most important of all – it gets the work done.
Please share your thoughts about this trick. If you love it, or hate it, or couldn’t care less, I’d still like to know. And if you put this to practical use, please just let the world know by leaving a comment.