Code Available on GitHub

Part 2 Specific Code on GitHub

If you search Google for Dynamics NAV item pictures you will find that there are a few examples out there for easily associating a picture with an item. However, there is not much on displaying multiple images for an item. This is a challenge I had to solve for my employer. Many items had anywhere from a single image to 10+ images.

I decided to come up with a .Net add-in control that was able to display multiple images for any given item and on any page (e.g. the item list, item card and beyond). I have decided to create a tutorial to show you how to create such a control. This will be quite lengthy, so I will split it up into smaller parts for you to digest more easily. The tutorial will be split into the following 7 part series:

  1. Part 1 – Introduction
  2. Part 2 – Creating the .Net image control model (this post)
  3. Part 3 – Creating the .Net image control viewmodel
  4. Part 4 – Creating the .Net image control view
  5. Part 5 – Creating the .Net add-in control wrapper class
  6. Part 6 – Creating the Dynamics NAV Item Images factbox page
  7. Part 7 – Hooking up the item Images factbox page

MVVM Disclaimer

Caution

I’d like to begin with a disclaimer on the Model-View-ViewModel (MVVM) architectural design pattern and this tutorial. First off, I am by no means an expert at implementing MVVM (Josh Smith, John Gossman and Sacha Barber are guys I would consider experts). I do believe however, that I have enough experience to consider myself adequately educated.

MVVM is a funny beast. The pattern itself seems to have no clear best practice for how it should be implemented. You will find arguments between View-first and ViewModel-first design. You’ll can find differing opinions on whether INotifyPropertyChanged should be implemented in the Model or ViewModel. Even whether or not a View should ever directly talk to a Model is up for debate. In the purest sense of MVVM, no it shouldn’t. But from a pragmatic sense, sometimes the juice isn’t worth the squeeze just for the sake of being architecturally correct or pure. Exposing 10 properties in the Model by duplicating them in the ViewModel is not always ideal. There are, of course, ways to handle these scenarios and frameworks that can be used to remove a lot of tedious boilerplate code.

You will notice I did not provide any links or references to these multiple ways to skin the proverbial MVVM cat (nor links to any frameworks). I have done this because this tutorial is not about MVVM (it just happens that I have used WPF and MVVM for the add-in control). If you wish to spend some time looking into MVVM, by all means do so (I encourage you in fact). All I am saying is that MVVM can be done many ways and my brand may vary from yours. I will even be doing an unorthodox Model-first approach for this series because it is easiest to start with the simplest objects. So, let’s focus on creating the add-in control and not so much on how the MVVM pattern has been implemented.

Now that we have that out of the way, let’s begin.

Part 2 – Creating the .Net Image Control Model

Initial Preparation Notes

We will begin with creating the model objects for this part of the series. This keeps the complexity down quite a bit as we ease into this tutorial since model objects are really just plain old C# classes. If you haven’t already, I suggest you grab the source code from GitHub, so you can follow along. This will also allow me to skip going through the folder structure and project setup and focus on the code itself.

I should mention that I created the project in Visual Studio 2013. If you are using a previous version of Visual Studio you will likely need to create a new project and just import all of the folders and code files. As long as you are using a version of Visual Studio that supports .Net 4.5, you should be ok (this includes Visual Studio 2015, though you will likely be asked to upgrade the solution when you open it up). If you are forced to create a new project, you’ll want to create a Class Library project. You will also need to add references that will not be there by default:

checklist_dark
Aaron Dodson
  • PresentationCore (v4.0)
  • PresentationFramework (v4.0)
  • System.Xaml (v4.0) (don’t confuse this with System.Xml which will already be referenced)
  • WindowsBase (v4.0)

Also worth noting is that I am creating this add-in control for Dynamics NAV 2013 R2. There should be no problem with it working in 2013, 2013R2 and 2015 (RTC only). I am also confident that 2009 R2 should be compatible, but I make no guarantees since I cannot easily test that specific version of NAV.

SmartObservableCollection<T> Class

When you have worked with WPF long enough you eventually create (or re-use somebody else’s) improved version of the ObservableCollection<T> class. This class provided by the base framework:

Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed. – MSDN

I have created class called SmartObservableCollection<T>. This class just makes some enhancements like waiting until a full range of objects has been added before notifying that the collection has changed (one notification rather than once per object). This is not really that important for this tutorial so I will glaze over it. It just needs to be mentioned because I have used this class throughout the project. You can just pretend that anywhere you see SmartObservableCollection<T>, that it is ObservableCollection<T>.

Feel free to take a look at the class if you have downloaded the source code.

IImageRepository Interface

The IImageRepository interface defines the main communication mechanism for retrieving item images from the image repository (or source if you like):

using System;
using System.Collections.Generic;
using System.Windows.Controls;
using Jason.Down.Blog.MutliImageAddinDemo.Collections;
namespace Jason.Down.Blog.MutliImageAddinDemo.Model
{
public interface IImageRepository
{
/// <summary>
/// Occurs when a request for item images is made.
/// </summary>
event EventHandler<ImageRequestEventArgs> RequestItemImages;
/// <summary>
/// Gets or sets the collection of item images.
/// </summary>
/// <value>
/// The collection of item images.
/// </value>
SmartObservableCollection<Image> GetImages();
/// <summary>
/// Sets the collection of item images.
/// </summary>
/// <param name="images">The images.</param>
void SetImages(IEnumerable<Image> images);
}
}

The basic flow will be as follows:

  1. Request the images via the RequestItemImages event.
  2. The image repository implementation will take care of setting the image collection via SetImages.
  3. The images can then be grabbed for display via GetImages.

ImageRequestEventArgs Class

You’ll notice in the RequestItemImages event that the event handler is of type ImageRequestEventArgs. This is the class that will pass any necessary information needed by the image repository to find the proper images:

using System;
namespace Jason.Down.Blog.MutliImageAddinDemo.Model
{
/// <summary>
/// This class represents details for requested item images.
/// </summary>
[Serializable]
public class ImageRequestEventArgs : EventArgs
{
private readonly string _itemNumber;
private readonly int _maxNumberOfImages;
/// <summary>
/// No Maximum is 0
/// </summary>
public const int NoMaximum = 0;
/// <summary>
/// Initializes a new instance of the <see cref="ImageRequestEventArgs"/> class.
/// </summary>
/// <param name="itemNumber">The item number.</param>
/// <param name="maxNumberOfImages">The maximum number of images. Default is <see cref="NoMaximum"/></param>
public ImageRequestEventArgs(string itemNumber, int maxNumberOfImages = NoMaximum)
{
_itemNumber = itemNumber;
_maxNumberOfImages = maxNumberOfImages;
}
/// <summary>
/// Gets the item number.
/// </summary>
/// <value>
/// The item number.
/// </value>
public string ItemNumber
{
get { return _itemNumber; }
}
/// <summary>
/// Gets the maximum number of images to retrieve from the image source.
/// </summary>
/// <value>
/// The maximum number of images to retrieve from the image source.
/// </value>
public int MaxNumberOfImages
{
get { return _maxNumberOfImages; }
}
}
}

I have decided to pass two pieces of information:

  1. The item number (which in NAV is a code field, hence I have used the string type as opposed to int).
  2. An optional parameter to request a maximum number of images. It defaults to 0 (which will be interpreted as no maximum).

If you were to implement an image repository that retrieved images from a server over the internet, you would likely want to limit how many images are returned in a single request. I won’t be doing this in the tutorial, but the framework will be there if you want to extend the functionality of this add-in control.

NavQueryObjectImageRepository Class

The NavQueryObjectImageRepository class is quite a mouthful, I’ll admit. However, it clearly explains the intent of the image repository. I will be using a NAV query object to retrieve the images. This will be more evident when we start working on the NAV side in Part 6 of this tutorial.

using System;
using System.Collections.Generic;
using System.Windows.Controls;
using Jason.Down.Blog.MutliImageAddinDemo.Collections;
namespace Jason.Down.Blog.MutliImageAddinDemo.Model
{
[Serializable]
public class NavQueryObjectImageRepository : IImageRepository
{
private readonly string _itemNumber;
[field: NonSerialized]
private SmartObservableCollection<Image> _images;
/// <summary>
/// Initializes a new instance of the <see cref="NavQueryObjectImageRepository"/> class.
/// </summary>
/// <param name="itemNumber">The item number.</param>
public NavQueryObjectImageRepository(string itemNumber)
{
_itemNumber = itemNumber;
}
/// <summary>
/// Occurs when a request for item images is made.
/// </summary>
[field: NonSerialized]
public event EventHandler<ImageRequestEventArgs> RequestItemImages = delegate { };
/// <summary>
/// Gets or sets the collection of item images.
/// </summary>
/// <returns></returns>
/// <value>
/// The collection of item images.
/// </value>
public SmartObservableCollection<Image> GetImages()
{
RequestItemImages(this, new ImageRequestEventArgs(_itemNumber));
return _images ?? (_images = new SmartObservableCollection<Image>());
}
/// <summary>
/// Sets the collection of item images.
/// </summary>
/// <param name="images">The images.</param>
public void SetImages(IEnumerable<Image> images)
{
_images = new SmartObservableCollection<Image>(images);
}
}
}

First of all, you can see that this class implements the IImageRepository interface. This means it must implement all methods, properties and events that are defined in the interface.

The basic workflow for this image repository implementation goes something like this:

  1. A new repository will be created for each item (with the item number being passed into the constructor).
  2. I have actually removed the need to manually call the RequestItemImages event. Simply calling GetImages will automatically call the event for you.
  3. When calling RequestItemImages a new ImageRequestEventArgs object is created, passing in the item number and leaving the default of no maximum limit on the number of images returned.
  4. After the request is made, it is assumed that the SetImages class will get called from the NAV side, updating our SmartObservableCollection of images.
  5. If for some reason the collection is empty (i.e. SetImages was not called or no images were found) a blank collection is created to avoid having a null collection.
  6. The image collection (empty or populated) is passed to whoever called GetImages.

Final Notes

Some classes you will notice are marked as [Serializable]. This is required for any classes that will be passed back and forth between NAV and .Net. However, not all things can be serialized (e.g. events and some types of collections). For any classes that are marked with the [Serializable] attribute, the non-serializable members are marked as such via the [field: NonSerialized] attribute.

That’s it for Part 2. Just a few, relatively simple .Net classes. Next up: Part 3 – Creating the .Net image control viewmodel.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.