Add images dynamically to WPF DataGrid using IValueConverter in C#

I created a WPF project which included a DataGrid which present a Status. It looked liked this.

image

I didn’t really like the representation of the True, False, Null and decided to convert the value to either a Green, Yellow or Red image instead.

To do this, I first added a class called BoolToImageConverter which implements the IValueConverter interface, then an Images directory and then images to my project, as shown below.

using System.Windows.Data;
public class BoolToImageConverter : IValueConverter
{
 
}

image

The IValueConverter requires the implementation of 2 methods. The Convert() and ConvertBack() methods. These methods have a CultureInfo parameter which is part of the System.Globalization namespace. The ConvertBack() will not be used but is implemented like the below for this example.

using System.Globalization;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
     throw new System.NotImplementedException();
}

The Convert() method is the most important and is shown below.

using System.Data;
using System.Windows.Media.Imaging;
 
public object Convert(object value, Type targetType,
                  object parameter, CultureInfo culture)
{
   if (value is DataRowView)
   {
     DataRowView row = value as DataRowView;
     if (row != null)
     {
       if (row.DataView.Table.Columns.Contains("Status"))
       {
         Type type = row["Status"].GetType();
         string status = (string)row["Status"];
         if (status == "Null")
         {
           Uri uri =
             new Uri("pack://application:,,,/Images/yellow.jpg");
           BitmapImage source = new BitmapImage(uri);
           return source;
         }
         if (status == "True")
         {
           Uri uri =
             new Uri("pack://application:,,,/Images/green.jpg");
           BitmapImage source = new BitmapImage(uri);
           return source;
         }
         if (status == "False")
         {
           Uri uri =
             new Uri("pack://application:,,,/Images/red.jpg");
           BitmapImage source = new BitmapImage(uri);
           return source;
         }
        }
       }
      }
      return null;
    }

The above method will be called from XAML code which will pass a DataRowView as the value. It checks the Status column for the value and converts it to a green, yellow or red image.

The following updates needs to be made to the MainWindow.xaml file.

Add the below to the Window element.

xmlns:local="clr-namespace:FilterWPF"

Then, after the Window element and before the Grid element add the following Windows.Resources element.

<Window.Resources>
   <local:BoolToImageConverter x:Key="ConvertBoolToImage" />
   <DataTemplate x:Key="StatusImage" x:Name="mStatusImage">
     <Image Width="16" Height="16" Margin="3,0"
        Source="{Binding Converter={StaticResource ConvertBoolToImage}}" />
   </DataTemplate>
</Window.Resources>

Notice that the BoolToImageConverter class is identified and the Key value set to CovertBoolToImage. Within the DataTemplate element the Key is set to StatusImage and the source binds to Convertor which links to the BoolToImageConverter class via its Key value.

Next we need to override a method within the System.Windows.Controls.DataTemplateSelector class. This method is called SelectTemplate() and is overridden as below.

public override
      DataTemplate SelectTemplate(object inItem, DependencyObject inContainer)
{
     DataRowView row = inItem as DataRowView;
 
     if (row != null)
     {
         if (row.DataView.Table.Columns.Contains("Status"))
         {
             MainWindow w = GetMainWindow(inContainer);
             return (DataTemplate)w.FindResource("StatusImage");
         }
     }
     return null;
}

The above method calls another method existing within the same class I have called DataTemplateSelectorBase. The method is called GetMainWindow and is shown below.

protected MainWindow GetMainWindow(DependencyObject inContainer)
{
     DependencyObject c = inContainer;
     while (true)
     {
         DependencyObject p = VisualTreeHelper.GetParent(c);
 
         if (c is MainWindow)
         {
             return c as MainWindow;
         }
         else
         {
             c = p;
         }
     }
}

The final step is to create a method within the MainWindow.cs code-behind to insert the column. The code is below and should be called from the Window_Loaded() found in the MainWIndow.cs.

public void SetStatusColumn()
{
     DataGridTemplateColumn statusColumn = new DataGridTemplateColumn
     {
       CanUserReorder = false, Width = 85, CanUserSort = false
     };
     statusColumn.Header = "Status";
     statusColumn.CellTemplateSelector = new DataTemplateSelectorBase();
     dataGrid1.Columns.Insert(0, statusColumn);
}

image

This is good but now I have 2 columns called Status. I added the below code after binding the data to the DataGrid but before calling the SetStatusColumn() method.

dataGrid1.Columns[0].Visibility = System.Windows.Visibility.Hidden;

The above line of code prevents the first column in the data set from being displayed in the DataGrid. The result is a WPF program which now resembles the below.

image

Adding images instead of text make a program simpler to use and understand. Little things like this can really increase the acceptance or sale of a program. Use it well.

NOTE: I did initially set the Status value as a bool, however, due to the way I load the data for this example I needed to change it to a string. When the data is loaded from a real data source and a value is a bool, then it will work with a bool, simple change the boxing from string to bool. The fact that I made a bool into a string should NOT be reproduced. This was done only because of the way I load the data into the Datagrid.

Download the source