Code Available on GitHub

Part 3 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
  3. Part 3 – Creating the .Net image control viewmodel (this post)
  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

Part 3 – Creating the .Net Image Control ViewModel

Initial Preparation Notes

In this part of the tutorial we will be creating the ViewModel objects and a couple supporting 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. Any other preparation steps and notes can be found in Part 2 of the tutorial.

ViewModelBase Class

The first thing to consider for a ViewModel class is the need to notify the View when bound properties have changed. This is done through the INotifyPropertyChanged interface. Unfortunately this can be tedious when implemented in the standard manner as suggested by Microsoft because it requires you to implement it for every ViewModel class that requires it. It is also error prone because you have to pass a string representation of the property name to the OnPropertyChanged method. To avoid this, most people create a base class that implements the INotifyPropertyChanged interface and then have all of their ViewModels inherit from this class.

using System.ComponentModel;
using System.Runtime.CompilerServices;
using Jason.Down.Blog.MutliImageAddinDemo.Annotations;
namespace Jason.Down.Blog.MutliImageAddinDemo.ViewModel
{
/// <summary>
/// This class exposes the default behaviour for INotifyProperyChanged.
/// </summary>
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Called when a property in the ViewModel has been changed and wishes to send out a notification.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}

There are a couple things to note here. First, we can avoid the error prone string parameter for the property name by using the [CallMemberName] attribute as the default parameter to the OnPropertyChanged method. This will expose the calling properties name and pass it in for us by default (no string spelling errors!). Second, thanks to ReSharper, a handy annotations class has been created for us (this is where the [NofityPropertyChangedInvocator] attribute comes from). I will not delve into this, but you can check out more information about it here. The annotations file is in the sample code on GitHub if you want to look at it. It is conveniently named Annotations.cs and is found under Properties in the solution explorer.

ImageViewModel Class

Our first real ViewModel class is the ImageViewModel. This is a simple class that wraps an image and exposes it through a property.

using System;
using System.Windows.Controls;
namespace Jason.Down.Blog.MutliImageAddinDemo.ViewModel
{
/// <summary>
/// This class represents an item image.
/// </summary>
[Serializable]
public class ImageViewModel : ViewModelBase
{
private Image _itemImage;
/// <summary>
/// Initializes a new instance of the <see cref="ImageViewModel"/> class.
/// </summary>
/// <param name="itemImage">The item image.</param>
public ImageViewModel(Image itemImage)
{
ItemImage = itemImage;
}
/// <summary>
/// Gets or sets the item image.
/// </summary>
/// <value>
/// The item image.
/// </value>
public Image ItemImage
{
get { return _itemImage; }
set
{
if (!Equals(_itemImage, value))
{
_itemImage = value;
OnPropertyChanged();
}
}
}
}
}

There is not much else to say about this class except that it inherits from the ViewModelBase class and you can see that it calls OnPropertyChanged (not needing to pass in the property name via a string) if the ItemImage property is changed.

RelayCommand Class

Another class that most people end up creating is some sort of command base class that Implements the ICommand interface. The ICommand interface simply says you must define a command’s execution behaviour (what executes when the command is invoked) and a predicate that returns whether or not a command is allowed to execute in it’s current state. There are many ways to implement this behaviour, but I lean towards Josh Smith’s RelayCommand (with some very slight differences, such as Func<bool> instead of Predicate<object> for CanExecute; this is just a personal preference).

using System;
using System.Windows.Input;
namespace Jason.Down.Blog.MutliImageAddinDemo.Command
{
/// <summary>
/// The <see cref="RelayCommand" /> simply invokes delegates for given command functionality.
/// <see cref="CanExecute" /> returns true by default.
/// </summary>
[Serializable]
public class RelayCommand : ICommand
{
private readonly Func<bool> _canExecute;
private readonly Action _execute;
private readonly Action<Object> _executeParam;
/// <summary>
/// Initializes a new instance of the <see cref="RelayCommand" /> class.
/// This command can always execute (no predicate to control execution logic).
/// </summary>
/// <param name="execute">The execute.</param>
public RelayCommand(Action<Object> execute)
: this(execute, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="RelayCommand" /> class.
/// </summary>
/// <param name="execute">The execute command.</param>
/// <param name="canExecute">A predicate that controls whether or not the command can execute.</param>
/// <exception cref="System.ArgumentNullException">execute</exception>
public RelayCommand(Action<object> execute, Func<bool> canExecute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
_executeParam = execute;
_canExecute = canExecute;
}
/// <summary>
/// Initializes a new instance of the <see cref="RelayCommand" /> class.
/// </summary>
/// <param name="execute">The execute command.</param>
public RelayCommand(Action execute)
: this(execute, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="RelayCommand" /> class.
/// </summary>
/// <param name="execute">The execute command.</param>
/// <param name="canExecute">A predicate that controls whether or not the command can execute.</param>
/// <exception cref="System.ArgumentNullException">execute</exception>
public RelayCommand(Action execute, Func<bool> canExecute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
_execute = execute;
_canExecute = canExecute;
}
/// <summary>
/// Defines the method that determines whether the command can execute in its current state.
/// </summary>
/// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param>
/// <returns>
/// <c>true</c> if this command can be executed; otherwise, <c>false</c>.
/// </returns>
public bool CanExecute(object parameter)
{
if (_canExecute != null)
{
return _canExecute();
}
return true;
}
/// <summary>
/// Defines the method to be called when the command is invoked.
/// </summary>
/// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param>
public void Execute(object parameter)
{
if (_execute != null)
{
_execute();
}
else if (_executeParam != null)
{
_executeParam(parameter);
}
}
/// <summary>
/// Occurs when changes occur that affect whether or not the command should execute.
/// </summary>
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
}
view raw RelayCommand.cs hosted with ❤ by GitHub

PageableImageControlViewModel Class

This is the main ViewModel class (inherits from the ViewModelBase class) that will serve as the driver and glue between the main View and the various Model and supporting ViewModel classes. This is quite a large class so I will focus on certain sections, but not all.

Image Collection

The image collection portion of this class focuses on a SmartObservableCollection (discussed in Part 2) of ImageViewModels and the IImageRepository (also discussed in Part 2) used to populate the collection.

First off we have our collection of ImageViewModels, which notify the client whenever the collection has been changed. We also initialize our PageableImages, which will be discussed shortly.

private SmartObservableCollection<ImageViewModel> _images;
/// <summary>
/// Gets or sets the image collection.
/// </summary>
/// <value>
/// The image collection.
/// </value>
public SmartObservableCollection<ImageViewModel> Images
{
get
{
if (_images == null)
{
_images = new SmartObservableCollection<ImageViewModel>();
_images.CollectionChanged += (s, e) => OnPropertyChanged();
}
return _images;
}
set
{
if (_images != value)
{
_images = value;
OnPropertyChanged();
InitPageableImages();
}
}
}
private IImageRepository _repository;
/// <summary>
/// Sets the image repository.
/// </summary>
/// <param name="repository">The repository.</param>
public void SetImageRepository(IImageRepository repository)
{
_repository = repository;
Images = new SmartObservableCollection<ImageViewModel>();
var imageList = _repository.GetImages();
if (imageList == null)
{
UseDefaultImage();
return;
}
foreach (var image in imageList)
{
Images.Add(new ImageViewModel(image));
}
if (_images.Count > 0)
{
InitPageableImages();
MainImageSource = new BitmapImage(new Uri(PageableImages[0].ItemImage.Source.ToString()));
}
else
{
UseDefaultImage();
}
}

The bottom half of the code shows the IImageRepository logic. This is set from outside of the ViewModel (it will be set from NAV) and provides the collection of images (which are then wrapped into ImageViewModels for display). Once the ImageViewModels are created, the PageableImages are initialized (again more on this shortly) and the main (large) image is set to display the first image on the list. As a fallback for if a collection of images does not exist for the current item, UseDefaultImage is called. In this tutorial, it will just create a blank image, but you could easily have a default image in the project resources or perhaps a file location.

Pageable Image Collection

We have a collection of images, but we don’t want to display them all at once. The approach I have taken is to create another collection that will hold the current page of images (page size can be configured).

private SmartObservableCollection<ImageViewModel> _pageableImages;
public SmartObservableCollection<ImageViewModel> PageableImages
{
get
{
if (_pageableImages == null)
{
_pageableImages = new SmartObservableCollection<ImageViewModel>();
_images.CollectionChanged += (s, e) => OnPropertyChanged();
}
return _pageableImages;
}
}
private int _pageSize;
/// <summary>
/// Gets or sets the number of images for a single page.
/// </summary>
/// <value>
/// The number of images for a single page.
/// </value>
public int PageSize
{
get { return _pageSize; }
set
{
if (_pageSize != value)
{
_pageSize = value;
OnPropertyChanged();
}
}
}

We’ve seen calls to initialize the PageableImages collection a couple times in preceding code examples. I’ll show that now.

/// <summary>
/// Initializes the pageable images and related properties.
/// </summary>
public void InitPageableImages()
{
CanPageNext = _images.Count > PageSize;
CanPagePrevious = false;
CurrentPage = 0;
PageableImages.Reset(GetImages());
OnPropertyChanged("PageableImages");
CommandManager.InvalidateRequerySuggested();
}

This method sets a couple properties that control whether or not we can move to the next page (CanPageNext) or to the previous page (CanPagePrevious). Since we are initializing the collection, we cannot move to the previous page (we are on the first page, hence CurrentPage = 0;). Whether or not we can move to the next page depends if we have more images than images allowed per page (PageSize). GetImages is a function that grabs images from the full Image collection and pulls out as many images as possible, but not exceeding the PageSize limit. We’ll get to that in a minute.

One final note on the InitPageableImages method. There is a call after the PageableImages are updated (via the Reset method) to OnPropertyChanged to notify the View that we have updated the images. However, since we are not calling this method from within the PageableImages property setter, we need to specify explicitly which property the View should be notified about. This is why the call to OnPropertyChanged has the PageableImages string as a parameter.

private IEnumerable<ImageViewModel> GetImages()
{
if (_images != null && _images.Count > 0)
{
return _images.Skip(CurrentPage * PageSize).Take(PageSize);
}
return Enumerable.Empty<ImageViewModel>();
}

GetImages ensures we have an image collection (neither null nor empty). If is does not, an empty collection is returned. If we do have images, it does the following:

  • Multiplies the current page number by the number of images per page to get our starting point in the image collection for the current page.
  • Skips all images before that point.
  • Takes x number of images from that point (where x is the images per page or page size) and returns them in a collection.

For example, if we have a page size of 3 and our current page is 2 (0 based, so we are actually on the third page of images):

  • Skip 2 * 3 images (first six images)
  • Take next 3 images
  • In other words, for the third page, we are displaying images 7, 8 and 9.
Paging Commands

Time for the paging commands. These commands allow you to move forward and backward through the multiple images, a page at a time.

First up we have the CanPageNext property. This is just a boolean property with a backing field that is used to track whether or not PageNextCommand is allowed to be executed.

private bool _canPageNext;
/// <summary>
/// Gets or sets a value indicating whether move to the next page.
/// </summary>
/// <value>
/// <c>true</c> if you can move to the next page; otherwise, <c>false</c>.
/// </value>
public bool CanPageNext
{
get { return _canPageNext; }
set
{
if (_canPageNext != value)
{
_canPageNext = value;
OnPropertyChanged();
}
}
}
private ICommand _pageNextCommand;
/// <summary>
/// Gets the page next command.
/// </summary>
/// <value>
/// The page next command.
/// </value>
public ICommand PageNextCommand
{
get
{
return _pageNextCommand ??
(_pageNextCommand = new RelayCommand(PageNext, () => CanPageNext));
}
}
private void PageNext()
{
CurrentPage++;
PageableImages.Reset(GetImages());
CanPageNext = _images.Count > (CurrentPage + 1)*PageSize;
CanPagePrevious = true;
OnPropertyChanged("PageableImages");
}

The PageNextCommand is actually an ICommand property with a backing field that is initialized as a RelayCommand, only if it is needed. In the getter, we check if _pageNextCommand (the backing ICommand field) is null. If not, we just pass it to the caller (or as you will see in the next part of the series, the button which is bound to the ICommand). If the backing field is null, we instantiate it by creating a new RelayCommand. The RelayCommand is passed the method to execute (PageNext) and a function to determine if the executing action is allowed to be invoked (CanPageNext). Don’t worry too much if syntax seems strange to you. This is just a way to wrap the function into a parameterless Func delegate that takes no parameters and returns a boolean.

The PageNext method itself is not too complicated. It increments the CurrentPage property, gets the next page of images, checks if there are enough images remaining to allow another PageNext execution, sets the CanPagePrevious to true (we just moved forward a page, so we can definitely move back a page) and notifies the View that the images have been updated.

The PagePreviousCommand is the inverse of the PageNextCommand so I will not cover it in the tutorial. You can look at the source code on GitHub if you are interested.

Image Display

The final piece I will cover in this tutorial is the image display logic. This is the code used to display the main (large) image and allows switching that image to another one.

private ICommand _displayLargeImage;
/// <summary>
/// Gets the command to display the large (main) image.
/// </summary>
/// <value>
/// The command to display the large (main) image.
/// </value>
public ICommand DisplayLargeImage
{
get
{
return _displayLargeImage ??
(_displayLargeImage = new RelayCommand(SwitchImage));
}
}
private void SwitchImage(object o)
{
var img = o as Image;
if (img != null)
{
MainImageSource = new BitmapImage(new Uri(img.Source.ToString()));
}
}
private BitmapImage _mainImageSource;
/// <summary>
/// Gets or sets the main image source.
/// </summary>
/// <value>
/// The main image source.
/// </value>
public BitmapImage MainImageSource
{
get { return _mainImageSource; }
set
{
if (!Equals(_mainImageSource, value))
{
_mainImageSource = value;
OnPropertyChanged();
}
}
}

This code is pretty much the same as what you have seen in the previous code examples. There is only one difference here that is worth noting:

In the SwitchImage method, we are passed an object, which we try to cast to an Image. If the cast succeeds (see using the as operator), we use it as a bitmap for the MainImageSource property. You’ll notice that the DisplayLargeImage command creates a RelayCommand like so:

_displayLargeImage = new RelayCommand(SwitchImage)

So where is this Object parameter coming from? It’s actually coming from the view as a CommandParameter binding (it will be the thumbnail image that was clicked in the which will then be displayed in the larger image). We will cover that in the next tutorial.

That’s it for Part 3. This was a bit longer than the previous parts, but this is where the main logic in the program is located. Next up: Part 4 – Creating the .Net image control view.

4 thought on “Dynamics NAV .Net Multi-Image Add-In Control Part 3 – Creating the .Net Image Control ViewModel”
    1. Thanks Arnaud! I’ll be posting the next part tomorrow night and will likely pump out the remaining parts a little quicker (finally a little more time available).

      1. Great ! It’s awesome what you did ! I didn’t realize it’s possible to do such things and integrate it into Nav. Just checked the forth part it’s great thanks 🙂

Leave a Reply to Jason Down Cancel 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.