Display Images in WPF Data-Grid

Scenario

We have a WPF window which displays list of tasks in a data grid. The goal is to display image icon representing priority of the task at the start of each row.

Something like this…

image

Where

VSO_Download_16x For Low Priority Tasks
StatusInvalidOutline_16x For High Priority Tasks
  For Normal Priority Tasks

 

The application uses a typical MVVM architecture in which the data grid is bound to a view model which contains list of tasks.

 

The View Model

The TaskItem view model has the following properties;

  • TaskTitle (Type: String)
  • Description (Type: String)
  • CreatedOn (Type: DateTime)
  • DueOn (Type: DateTime)
  • Priority (Type: Enum –> TaskPriority)
  • PriorityImg (Type: Image derived from enum values for Priority)

Let’s quickly look at the code for PriorityImg property;

public Image TaskPriorityImg
{
	get
	{
		Image retValue = null;

		switch (_priority)
		{
			case TaskPriority.Normal:
				break;
			case TaskPriority.High:
				retValue = Resources.StatusInvalidOutline_16x;
				break;
			case TaskPriority.Low:
				retValue = Resources.VSO_Download_16x;
				break;
			default:
				break;
		}

		return retValue;
	}
}

As you can see, it is a simple Get property which returns images based upon the TaskPriority enum value. The images are stored within Application resources.

 

The View

With the ViewModel in place and PriorityImg returning Image representing the Priority of Task, you could bind the ViewModel to DataGrid on the view to display the information. Typical XAML would look something like this…

<DataGrid Name="dgTaskList" AutoGenerateColumns="False" >
	<DataGrid.Columns>
		<DataGridTemplateColumn Width="SizeToCells" IsReadOnly="True">
			<DataGridTemplateColumn.CellTemplate>
				<DataTemplate>
					<Image Source="{Binding TaskPriorityImg}" Stretch="None"/>
				</DataTemplate>
			</DataGridTemplateColumn.CellTemplate>
		</DataGridTemplateColumn>
		<DataGridTextColumn Header="Title" Binding="{Binding TaskTitle}"/>
		<DataGridTextColumn Header="Due On" Binding="{Binding DueOn}" />
	</DataGrid.Columns>
</DataGrid>

If you run this, you will see that no images are displayed within the first column. This is because the view model is returning Image as System.Drawing.Image, where as WPF Image control requires of type System.Windows.Media.ImageSource. To solve this you will need to use a Image Converter within the binding.

 

Image Converter

The converter needs to be of type IValueConverter to be able to use within the binding. Here is the full code for the converter…

[ValueConversion(typeof(System.Drawing.Image), typeof(System.Windows.Media.ImageSource))]
public sealed class ImageConverter : IValueConverter
{
	public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
	{
		if (value == null) return null;

		var image = (System.Drawing.Image)value;
		var bitmap = new System.Windows.Media.Imaging.BitmapImage();

		bitmap.BeginInit();
		MemoryStream memoryStream = new MemoryStream();
		image.Save(memoryStream, ImageFormat.Png);
		memoryStream.Seek(0, SeekOrigin.Begin);
		bitmap.StreamSource = memoryStream;
		bitmap.EndInit();

		return bitmap;
	}

	public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
	{
		return null;
	}
}

 

You can probably guess that it is doing a one-way conversion (Image to ImageSource). The idea is to read the image within MemoryStream and then write it into BitmapImage (which is inherited from ImageSource).

 

Referencing the converter

To reference the converter;

  • If the converter is in a different namespace to the window, add namespace reference in XAML.

(I had defined the converter in SimplePlan.ViewHelpers namespace, so the reference definition will be as below.)

xmlns:vh="clr-namespace:SimplePlan.ViewHelpers"
  • Create a static resource within the window.
<Window.Resources>
      <vh:ImageConverter x:Key="ImageConverter" />
</Window.Resources>
  • Change the Image Column binding within the Data Grid to use the converter.
<Image Source="{Binding TaskPriorityImg, Converter={StaticResource ResourceKey=ImageConverter}}" Stretch="None"/>

Now run the application and you will see images within the datagrid.

Leave a comment