Dynamics NAV .Net Add-in Control With Custom Events and EventArgs

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:
To be honest I could not think of an example that wouldn't be overly complicated for a blog post that was actually useful for NAV-related tasks, so I came up with a rather silly demo (and this blog post is still longer than I'd like). However, the point is to demonstrate how everything is wired up, rather than what the control itself does. I will show an example in a future blog post that demonstrates the types of things that are possible.

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).

Enough babble! Let's begin.

Basic Overview

The demo will focus on a user control that is given a list of pets from a Pet table in NAV. The Pet data populates a ListBox within the user control when the containing page is first opened. You can select one of the pets and click on a Speak button. This will fire an event back to NAV, packaging up a PetEventArgs event argument class. NAV will then display a message about the pet speaking, pulling information out of the event argument class. The user control also contains an area where information about a new pet can be entered, packaged up and sent to NAV to add a new pet to the table. Once the pet is added (and if no errors occur from a primary key violation etc.), NAV signals the user control to add the pet to the ListBox. Essentially what we have is a two way communication between the user control and NAV with a .Net add-in control acting as the messenger, so to speak.

See It In Action

First I will show some screenshots of the control in action and then in a later section, I will go through the relevant parts of the code.

To begin, I have pre-populated two Pet records to show that the initial loading of the control is done by sending data from the Pet table in NAV to the add-in control and then forwarded to the user control:

Initial Load
Initial Load - Pre-populated from the Database
Next, I have selected the pet "Chubbs (dog)" and clicked on the Speak button. This packages the selected Pet object into a PetEventArgs object and fires the Speak event from the user control. This event is captured by the add-in control and forwarded to the NAV page hosting the add-in control. The page grabs the PetEventArgs, extracts the Pet object and then displays a message using information from the Pet object:

Chubbs Speaking through an Event
NAV Responding to the Speak event

I have now entered information in the Add Pet section so that a new pet can be added to the database. We are adding Whiskers the cat:

Adding Whiskers the Cat
Adding Whiskers the Cat
After clicking the Add button, a new Pet object is created by the user control. The Pet object is then packaged up into a PetEventArgs object and sent along with the AddPet event that is fired by the user control. The AddPet event is picked up by the add-in control and forwarded to the NAV page that is hosting the add-in control. The page grabs the PetEventArgs, extracts the Pet object and then attempts to create a new Pet record in the database. If the record is added successfully, the NAV page sends a request back to the add-in control to add the Pet object to the user control. This request is then forwarded to the user control, which then adds the new Pet to the Pet ListBox. Then fields are then cleared in the Add Pet section:

Whiskers the Cat Added
Whiskers the Cat is now on the List
Now that Whiskers the cat has been added to the list, I can click the Speak button and have NAV display a message for him as well:

Whiskers Speaking through an Event
NAV Responds to the Speak Event
Just for completeness, I will show what happens when an error occurs when trying to add a new Pet record. In this case, I have tried to add a pet that violates the primary key (pet type and pet name). When an error occurs, the error message is shown, the pet is not added to the database, not added to the user control and the Add Pet section is left with the values intact so that the user can correct the mistake rather than typing in all the details again (Note: A keen observer will notice that the screenshot shows the pets listed in a different order than in the previous screenshots. This is because I had closed and then reopened the page, which goes to the database for the initial loading of the list. The records come back sorted by the primary key, which mean the pet type is sorted alphabetically):

Primary Key Violation
Chubbs the Dog Already Exists
Pet Not Added
Pet Not Added - Details Not Cleared
So there is the control in action. Again, not terribly useful, but it is simple enough to get the point across. In the next section I will break down and explain the relevant sections of code. The code is also available for download if you want to follow along or see all parts of the code.

How It Works

For this simple example, I created a table called Pet. It's very simple with 3 text fields and a primary key to guarantee you don't have two types of pets with the same name (that would be confusing to you and the pets!):

Pet Table and Key
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#
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#
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:

  1. The PetUserControl user control is created inside the PetUserControlAddin
  2. The PetUserControlAddin fires the AddInReady event
  3. 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.
  4. 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.
  5. The Pet Card page then passes the list of pets to the AddInitialPets method exposed by the PetUserControlAddin.
  6. The PetUserControlAddin sends the list of pets to the PetUserControl via the AddInitialPetsList method.
  7. The PetUserControl binds the list to the ListBox.
I will not show the entire process, but you can follow along if you download the source code. I will focus on the parts with the events:


AddInReady Event
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. Fields 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
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
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
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
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:

  1. The user selects a Pet from the PetUserControl in the ListBox.
  2. The user clicks the Speak button, which causes the PetUserControl to package up the selected Pet into a PetEventArg.
  3. The PetUserControl fires the Speak event and passes along the PetEventArg.
  4. The PetUserControlAddin handles the Speak event by forwarding it on via its own Speak event.
  5. The Pet Card page handles the Speak event fired from the PetUserControlAddin.
  6. The Pet Card page extracts the Pet from the PetEventArgs object and passes it to the Pet Utils codeunit.
  7. The Pet Utils codeunit displays a message, extracting information from the Pet object.
Again, I will not show the entire process, but I will show the event related code.

Speak Event and PetEventArgs
Speak Event and PetEventArgs
The first red highlighted section shows the Speak event. Notice that this event expects an event handler that takes a PetEventArgs object. The second red highlight shows the Speak event being fired. First, the PetUserControl is passed via the this keyword and second, a new PetEventArgs object is instantiated, passing in the selected Pet object (note: see this MSDN how-to for creating events that follow the .Net guideline).

In the PetUserControlAddin, we forward the event on. This looks strikingly similar to the example in the last section, with the exception of including the PetEventArgs.

Speak Event in PetUserControlAddin
Speak Event in PetUserControlAddin
Forwarding the Speak Event in PetUserControlAddin
Forwarding the Speak Event in PetUserControlAddin
Once again, our Pet Card page responds to this event. The Speak event is passed to the OnSpeak method. This looks slightly different than the AddInReady event because we are passing a Sender object and an EventArgs object. In our case we are not making use of the Sender object, but if we cared where the event came from, we could extract that information (it would be the PetUserControl object in this case).

PetUserControlAddin Speak Event Handler
PetUserControlAddin Speak Event Handler
OnSpeak Function
OnSpeak Function
The OnSpeak function receives the PetEventArgs and pulls out the Pet property. This is then passed to the Pet Utils codeunit and a message is displayed using information from the Pet object. I will not show this.

The AddPet event can be viewed in the sample code available for download, but I will not cover it in this blog post. It originates at the PetUserControl, flows all the way to NAV and then flows back to the PetUserControl and is probably the most complete example. In retrospect, that event may have been the best one to show rather than the two events I showed, which each do about half of the communication flow. Oh well.

Hopefully this post was helpful. Feel free to comment below with questions, improvements or even request examples for future posts.

Labels: , , , , , , ,