Download Sample Project and NAV Objects
There are some decent blogs out there about Dynamics NAV .Net add-in controls, but I have not seen many that focus on using custom events and using custom event arguments (EventArgs in .Net). Many of the more advanced controls I’ve made for my employer require constant communication back and forth between the control and NAV. I thought it would be nice to throw up an example that includes the following:
- A custom .Net user control (for this demo I used a WinForms UserControl).
- A .Net add-in control to act as a mediator between the .Net user control and a page on NAV.
- A custom event arguments class to package up the relevant data sent by the event.
What this blog post will not be covering is how to create a control add-in, sign it, register it in NAV and add it to a page. The MSDN documentation covers that information well enough.
Also note that I have done this example in NAV 2013 R2. As far as I know, this will work in NAV 2009 R2 and 2015. I don’t know if versions prior to 2009 R2 will work (I believe that even 2009 R1, which did have the ability to create add-in controls, is missing some big improvements that allowed exposing public methods and events from the add-in control to the RTC).
![]() |
Initial Load – Pre-populated from the Database |
![]() |
NAV Responding to the Speak event |
![]() |
Adding Whiskers the Cat |
![]() |
Whiskers the Cat is now on the List |
![]() |
NAV Responds to the Speak Event |
![]() |
Chubbs the Dog Already Exists |
![]() |
Pet Not Added – Details Not Cleared |
![]() |
Pet Table and Primary Key. |
On the .Net side, I wrote some C# code to create an object that represents a Pet record. It takes the three fields as constructor arguments, which are stored in read-only fields and exposed through public properties. I also created an additional property called Display that formats the pet type and pet name for use in the ListBox (see screenshots in the See It In Action section):
![]() |
Pet Class in C# |
One thing of note about this class is the serializable attribute (marked above the class name definition as [Serializable]). This attribute is required for any classes that you will pass as objects between NAV and .Net because serialization is how things are transferred between the two domains.
Next, I created the PetEventArgs class. This is used to package up information about the Pet object that I wish to pass between NAV and .Net. I chose to store the entire Pet object as a single field and exposed it as a property within the PetEventArgs class. However, I could have stored each Pet property as a separate PetEventArgs property just as easily. The advantage to the single object approach is that a change to the Pet class doesn’t require changes to the PetEventArgs class to expose the new property later. The disadvantage to this approach is that accessing the properties from the NAV side requires doing PetEventArgs.Pet.PropertyName rather than just PetEventArgs.PropertyName (the latter is cleaner, but the former is a small price to pay when only digging one level deeper in the object chain):
![]() |
PetEventArgs Class in C# |
Notice that this class also required the serializable attribute. This class will wrap the Pet object and it is this class itself that will actually be passed around. You must serialize all the way through the object chain or you will get an error from NAV.
The next step was creating the user control. I chose to go with a .Net Windows.Forms.UserControl so that I could package the entire control into one UI element and add it to container. I could have created each piece of the control directly in the add-in control, but this takes away the advantage of using the UI designer in Visual Studio and requires hardcoding the locations.
Rather than show the PetUserControl code in it’s entirety, I will focus on showing parts relevant to the actions taken above in the See It In Action section.
Initial Loading From NAV Table
When you open up the page controlling the PetUserControlAddin, the following sequence of events occurs:
- The PetUserControl user control is created inside the PetUserControlAddin
- The PetUserControlAddin fires the AddInReady event
- The NAV Pet Card page responds to the AddInReady event by asking the Pet Utils codeunit to get the Pet records in the Pet table.
- The Pet Utils codeunit creates a .Net List, cycles through the Pet table, creates a Pet object for each record and then adds them to the list.
- The Pet Card page then passes the list of pets to the AddInitialPets method exposed by the PetUserControlAddin.
- The PetUserControlAddin sends the list of pets to the PetUserControl via the AddInitialPetsList method.
- The PetUserControl binds the list to the ListBox.
![]() |
AddInReady Event |
Above is the definition for the AddInReady in the PetUserControlAddin class (the delegate { }; part is a trick to avoid having to check if the event has been registered before firing, which I have talked about here). It is marked with the ApplicationVisible attribute to ensure NAV will have access it when the add-in is bound to the NAV page. Also note that the event is marked as field:NonSerialized. This is to override the serialized attribute I have put on the entire add-in class. Events cannot be serialized and so you must mark them as non-serialized if you have marked the containing class as serialized.
![]() |
Register and Fire AddInReady |
The AddInReady event is fired inside of the CreateControl method of the PetUserControlAddin class once the panel containing the PetUserControl has been created. I’m using a simple event forwarding trick here that I have mentioned in a previous blog post.
When the event fires, it is handled by the Pet Card page hosting the PetUserControlAddin (I named it PetAddin on the page).
![]() |
PetUserControlAddin AddInReady Event Handler |
You’ll notice I simply forwarded the call to an OnAddInReady function within the page itself. The reason I have done this is very important. If you were to add something new to the add-in control (such as another publicly exposed event or method), it will not show up on the page unless you removed the control and then rebind it (unfortunately there is no other way to refresh the page). The problem with doing this is that all code that was directly inside the triggers is deleted. The good news is that replacing a single line for each event is not too much work (still not ideal, but what can you do). I always follow the pattern of creating an event handling function called On[EventName] and passing the parameters that were packaged up with the event itself. You will see this more when I show the Speak event in a later section.
![]() |
OnAddInReady Function |
The OnAddInReady function passes a .Net List to a codeunit, which populates the list and returns it (passes it by reference since NAV has no way to return a .Net object from a function). This is straight forward records looping code, so I won’t show it. Once the list is populated, the AddInitialPets method found in the PetUserControlAddIn is passed the list of pets.
![]() |
AddInitialPets Method |
A few things to note here. The ApplicationVisible attribute has been applied to the method to ensure that the method is exposed to NAV. Next, you’ll notice that the pets are a list of Objects not a list of Pets. I have done this since by default NAV does not work directly with .Net generics. To keep the code simpler on the NAV side (in the code I did not show for populating the list of pets in the Pet Utils codeunit). If you are looking at that code in the downloaded sample and you want to see how to create a list of a specific .Net type, take a look at Vjeko’s blog post (he recently changed his blog formatting and this appears to have messed up the way the code displays (in Chrome at least)… if you want to see an example of how to create generic objects in .Net in C/AL code, just leave a comment and I’ll post a downloadable sample). I always use a variation of this in production code. To combat the pets being Objects, I use a LINQ Cast extension method which then returns all objects that are able to be cast to the type Pet. Next, the list of pets is passed to the PetUserControl, which binds them to the ListBox. I will leave that part out or this blog post will become a novel.
Speak Event and PetEventArgs
I will show one more example from the user control that involves the PetEventArgs class. The speak event follows the same basic flow as the last section, except that it originates directly from the PetUserControl (rather than at the add-in) and does not return anything back from NAV to the PetUserControl. The entire process is as follows:
- The user selects a Pet from the PetUserControl in the ListBox.
- The user clicks the Speak button, which causes the PetUserControl to package up the selected Pet into a PetEventArg.
- The PetUserControl fires the Speak event and passes along the PetEventArg.
- The PetUserControlAddin handles the Speak event by forwarding it on via its own Speak event.
- The Pet Card page handles the Speak event fired from the PetUserControlAddin.
- The Pet Card page extracts the Pet from the PetEventArgs object and passes it to the Pet Utils codeunit.
- The Pet Utils codeunit displays a message, extracting information from the Pet object.
![]() |
Speak Event and PetEventArgs |
![]() |
Speak Event in PetUserControlAddin |
![]() |
Forwarding the Speak Event in PetUserControlAddin |
![]() |
PetUserControlAddin Speak Event Handler |
![]() |
OnSpeak Function |