by Tony Phan, January 2021

Basic Dropdown - Javascript Control Add-in

This is a step-by-step tutorial on how to create a simple dropdown, Javascript Control Add-in for Dynamics NAV, utilizing Javascript and C#.  This version was written and tested in NAV 2018 but, I believe, it should be backward compatible as far as NAV 2013 R2.

Description

In case you wonder, why do this when we can easily turn a field into a dropdown selection by setting up table relations?  Simply putted, not all fields have or can have relations thus this will supplement that.  The field I've chosen for this example is the Purchase Order - On Hold field.  It's a simple, 3 character field that's meant to put the PO on hold, for any number of reasons.  It can be someone's initial, it can be any abbreviation, but it can only have a maximum of 3 characters.  Let's say we want to use abbreviations here and they should be PSA = Pending Supervisor Approval, PCA = Pending Credit Approval, OCL = Over Credit Limit.  That's easy enough to remember but what if there were more, like 10 different abbreviations?  It would be hard to remember them all and a typo anywhere would make it hard to discern.  This is where the dropdown add-in can come in handy.  It will enforce a uniformed code and will be easier to recognize.  In addition, if ever you need to obsolete an option, it would be much easier to do.

>> Download the complete project here. <<
Step 1. Create a new Class Library (.Net Framework) project.

Call it DropdownAddin.

Zoom In
Step 2. Add a reference to the Extensibility UI

This depends on your version of NAV.  For NAV 2018, this DLL can be located here:
C:\Program Files\Microsoft Dynamics NAV\110\Service\Microsoft.Dynamics.Framework.UI.Extensibility.dll

Zoom In
Step 3. Add relevant folders.

Add a Script and Stylesheet folder.

Zoom In
Step 4. Add an interface and an event handler class.

Add a public interface class and name it IDropdownAddin.  This is where we will define our interactions with NAV.  We give the add-in a name by using the ControlAddInExport attribute.  In order to define this attribute we add a "using Microsoft.Dynamics.Framework.UI.Extensibility;" statement.  When you define the add-in under NAV's Control Add-ins window, the Add-in Name must match the ControlAddInExport attribute of the interface.  Anytime you make a change to this interface, you must recompile the DLL and copy it to the appropriate folder on the server, overwriting the existing DLL, if any.

Now add a regular class and name it EventHandler.  This is where we will define handlers for special events.

We need a way to pass data back to NAV but we cannot communicate directly with NAV thus we have to create an event and pass data along with it.  We do this by defining a SelectionChanged delegate under the EventHandler and we just need 1 parameter.  We take advantage of this delegate in the interface by defining it as an event with the same name.

The interface is what our control will use to interact with NAV and vice versa.  The control needs to be created each time the parent page is opened but it cannot be created until the page is ready for it to be created.  One way to find out when the page is ready is to create and invoke an AddInReady event.  We can invoke this event from the Manifest.xml file.  The script will look like this:

  <Script>
    <![CDATA[ 
      Microsoft.Dynamics.NAV.InvokeExtensibilityMethod("AddInReady", null);
        ]]>
  </Script>
Step 5. Add a Javascript file to define our control.

Add a Javascript file under the Script folder and call it DropdownAddin.js.  Also add a version of the jquery library which you can grab from your NAV's IIS instance or downloading the latest from jquery.com.

For each place that we define an add-in control, NAV actually creates an iframe to host it.  It will include a div element with the attribute id="controlAddIn".  This is where we will want to define our control thus we can reference it via JQuery like this: 

navControlContainer = $("#controlAddIn");

The $ indicates we want to use JQuery and the # indicates a reference to an element by it's id attribute.

We then append the control with a "select" element which is used for creating a dropdown control:  navControlContainer.append("<div><select id='dropdown' onchange='SelectionChanged(this);'></select></div>");

Each time the selection changes it will fire an onchange event thus we create and call our SelectionChanged function here which we will use to inform NAV.

Step 6. Adding option items.

Once the control has been initialized/created, we can start adding items to it.  To do this, we add an AddItem function to the IDropdownAddin.cs as well the DropDownAddin.js file.  This function takes 2 parameters, Value and Text.  Value is usually the abbreviated, hidden value and is what we will store while Text is the displayed or visible value.

Step 7. Selecting an item.

Now that we've created the control and added option items to it, we need to set which is the active selection.  To do that we can either select by value or we select by text.  We, normally, just need the select by value but I included the select by text in the project for those who are curious.

Create a function SelectByValue with a single Value parameter under the IDropdownAddin interface and the DropdownAddin script.  Within the javascript function, we use this command to get the value option that matches:

let $dropdown = $('#dropdown option[value="' + Value + '"]');

This command will check to see if there's a matching value, before selecting it:

if ($dropdown.length) {
    $dropdown.prop("selected", true);
}

If you were to select by text, you will do it like this:

let $dropdown = $('#dropdown option:contains("' + Text + '")');

Note how the command is option:contains which is not very reliable.  For example, if we have the following 3 options the selection can get confused and select the wrong option.  Can you guess which one?

  • We went to the Aquarium and the Zoo
  • We went to the Aquarium
  • We went to the Zoo
Step 8. Saving user selections.

Now that we've created the control, we need a way to inform NAV when the user makes a selection.  To do that, we create a SelectionChanged event under the IDropdownAddin interface and, although it doesn't have to have the same name, it's better to keep it that way for sake of consistency, a SelectionChanged function under the DropdownAddin script.  Within the javascript function, we read from the selection like this:

let Value = $(sender).val();

and we pass the value to NAV by invoking the SelectionChanged event with this:

Microsoft.Dynamics.NAV.InvokeExtensibilityMethod('SelectionChanged', [Value]);

Note how the Value is enclosed in [ ].  Regardless if it was a single parameter or multiple parameters (separated by a comma), we must enclose the parameter value within the [ ] brackets.

Step 9. Adding styles with a simple CSS stylesheet.

We can control how our dropdown looks by using CSS.  Add a stylesheet named Styles.css under the Stylesheet folder.  In our case, we just want our control to look a little bigger than the standard style so we define it like this:

#dropdown {
  height:24px;
}

The # is the method in CSS to reference an element by it's id attribute.  By this token, #dropdown references the id="dropdown" element.  If you gave your dropdown a different id, use that id here instead.

Step 10. Add a Manifest.xml file

NAV will initialize the add-in by reading from this file.  It should look somewhat like this.

<?xml version="1.0" encoding="utf-8"?>
<Manifest>
  <Resources>
    <StyleSheet>Styles.css</StyleSheet>

    <Script>DropdownAddin.js</Script>
    <Script>jquery-3.4.1.min.js</Script>
  </Resources>

  <Script>
    <![CDATA[ 
      Microsoft.Dynamics.NAV.InvokeExtensibilityMethod("AddInReady", null);
        ]]>
  </Script>

  <RequestedHeight>24</RequestedHeight>
  <RequestedWidth>300</RequestedWidth>

  <AllowCaption>true</AllowCaption>
</Manifest>


Keep in mind that NAV will add the control under an iframe, therefore, we must define everything that we need here.  There is a way to reference some of the resources used by NAV but that's outside the scope of this tutorial.

The Resources are the files that NAV will include with the add-in control, when it's created.

The <Script></Script> area is what NAV will execute once the control has been created.  Here, we invoke the AddInReady method.  From within NAV, when the AddInReady is invoked, that's when we can call our initialization routine to define our control.

Zoom In
Step 11. Digitally signing our assembly.

NAV will require that our add-in be digitally signed but that's a very simple task.  We just need to open the project's Properties window, go to the Signing tab, check the "Sign the assembly", selecting New for the "Choose a strong name key file", and giving it a name.  Choose whether or not you want to protect it with a password and click OK.  That's it, that simple!

Zoom In
Zoom In
Step 12. Compile the project, get the public key token, and move the DLL to the Add-ins folder.

Compile a Released version of the project and it should create a DropdownAddin.dll under the project's bin\Release folder. 

Once the DLL has been created, we will need to get its public token but, unless we change our digital signature, we only need to do this once so we can skip this step when recompiling the project.

Open the Visual Studio - Developer Command Prompt and switch to the folder where our DLL resides.
Then run this command: sn -T DropdownAddin.dll

Note that the -T is case sensitive.  If it runs correctly we should get something like this: Public key token is xxxxxxxxxxx.  Record this value for we will need to reference it when adding the add-in to NAV.

After getting the public key token, copy or move the DLL to NAV's add-ins folder.  This path is based on the version of NAV you're on.  For NAV 2018, it should be:
C:\Program Files (x86)\Microsoft Dynamics NAV\110\RoleTailored Client\Add-ins

Tip: To be efficient, create a shortcut to the path and place it under the project's bin\Release folder.  Each time that you have to recompile the project it would be much easier and quicker to use the shortcut to get to this folder.  You can use the clipboard to copy/paste the DLL or, even better, just drag the DLL and drop it onto the shortcut.
Zoom In
Step 13. Creating the Add-in Resource zip.

Compiling the DLL is not enough, now we must create a compressed (zipped) folder of relevant files for our add-in.  The files that we need to include is the Manifest.xml file itself and the files that are referenced by it.  The quick and easy way to do this is to hold the Ctrl key down, selecting and highlighting the Manifest.xml, Stylesheet, and Script folder; right-clicking and choosing Send to > Compressed (zipped) folder.

Tip: While the above step is simple it can be time consuming if we have to do this often, when debugging and testing our control.  To be efficient, if you are using Windows 10, create a batch file and use this command to do the equivalent:

tar -a -c -f addin.zip Script Stylesheet Manifest.xml

I made a MakeZip.bat file to do this.  Each time I make changes to the Dropdown.js file or the Stylesheet, I just run this batch to create a new zip, overwriting the old, and importing it into NAV.
Step 14. Add the control add-in to NAV.

Before you can reference the control add-in under the Development Environment, you must define it as a Control Add-in in NAV.  Now that we've created our DLL and the relevant zipped folder, we are ready to add the add-in to NAV.  Open the Control Add-ins page and add a new control.  Make sure the Add-in Name is exactly like the value in the ControlAddInExport attribute.  Now enter the Public Key Token recorded earlier.  The Category should be JavaScript Control Add-in.  Give it a description then click Import and select the zip file that was created earlier.  The NAV Development Environment can now reference our control.

Zoom In
Step 15. Add the control to a page.

In this example, I pick the Purchase Order page.  I'm going to use it to add dropdown options for the On Hold field.

Open the NAV Development Environment, Page 50, and add a new On Hold field above the existing On Hold field.  For this example, we're going to keep the old field just to see NAV at work.  Since 2 fields on the same page cannot have the same name, we will name ours OnHold.  Now open the Properties window (Shift + F4) for the OnHold field.  Near the very bottom is the ControlAddIn property.  Bring up the window for that property and select the add-in control we created.

Zoom In
Zoom In
Step 16. Adding C/AL codes to utilize the control.

Once we reference our add-in, the events that are defined with an [ApplicationVisible] attribute are automatically added to the page in the form of ControlName::EventName. 

Add an InitDropdown function and add the following commands:

This will call our InitControl function to build our control.
CurrPage.OnHold.InitControl();

Now we add option items to it.  The 1st option is a blank option.
CurrPage.OnHold.AddItem('','');
CurrPage.OnHold.AddItem('PSA','Pending Supervisor Approval');
CurrPage.OnHold.AddItem('PCA','Pending Credit Approval');
CurrPage.OnHold.AddItem('OCL','Over Credit Limit');

The last step is to make the corresponding selection the active selection by retrieving the value from the On Hold field.
CurrPage.OnHold.SelectByValue("On Hold");

When the page is created and is ready, NAV will invoke the AddInReady event.  This is where we call our InitDropdown function to create and populate our control.

When the user changes the selection, a javascript onchange event will occur on the dropdown element which will, in turn, call our javascript SelectionChanged function.  This function then extracts the value and pass it via an event, to NAV.  The function will invoke the SelectionChanged event in the IDropdownAddin interface which is the equivalent of the OnHold::SelectionChanged event in our NAV page.  This is where, from C/AL, we take the Value parameter and save it to the On Hold field.

Zoom In
Step 17. Finish

That's it, we are done and ready to use our control.  If ever we need to add more options, we just edit the page and add more items under the InitDropdown function.  If ever we need to obsolete an item, the best way to do this is to add logic to prevent a selection but never remove it from the option list unless it's not referenced anywhere.  The reason is because NAV will still need to reference the value when opening the page.

Zoom In
Bonus: How to find where a Control Add-in is used.
Let's not forget how easy it is to use the NAVCore Objects Manager to transfer the object to the Test and Live environments.