In the previous blogs on ValueConverters - here and here - the conversion was from Integer type to Brush. In this blog I will cover the situation where you want to include a small image or icon in the ListView.
There are various alternatives here. You could hard code the image path into the collection, but the problem may be that you don't know the exact image paths at the time the collection is created. The approach I want to use is where you parse the data as it feeds through the binding and you select a stored image from the hard drive, the selection being based on the value of a specific field. This decouples the details of the image from the collection itself, which may be useful in many situations.
The DrinkProduct class looks like this:
Public Class DrinkProduct
Enum MaterialType
Granules
Leaf
Liquid
Paste
Powder
Other
End Enum
Sub New(ByVal ID As String, ByVal Name As String, ByVal PackType As String, _
ByVal BaseMaterial As MaterialType, ByVal Sales As Integer, ByVal Qty As Integer)
Me.ProductID = ID
Me.ProductName = Name
Me.PackageType = PackType
Me.Material = BaseMaterial
Me.AnnualSales = Sales
Me.Quantity = Qty
End Sub
Private _ProductID As String
Public Property ProductID() As String
Get
Return _ProductID
End Get
Set(ByVal value As String)
_ProductID = value
End Set
End Property
Private _ProductName As String
Public Property ProductName() As String
Get
Return _ProductName
End Get
Set(ByVal value As String)
_ProductName = value
End Set
End Property
Private _PackageType As String
Public Property PackageType() As String
Get
Return _PackageType
End Get
Set(ByVal value As String)
_PackageType = value
End Set
End Property
Private _Material As MaterialType
Public Property Material() As MaterialType
Get
Return _Material
End Get
Set(ByVal value As MaterialType)
_Material = value
End Set
End Property
Private _quantity As Integer
Public Property Quantity() As Integer
Get
Return _quantity
End Get
Set(ByVal value As Integer)
_quantity = value
End Set
End Property
Private _annualsales As Integer
Public Property AnnualSales() As Integer
Get
Return _annualsales
End Get
Set(ByVal value As Integer)
_annualsales = value
End Set
End Property
Public Shared Function StockCheck() As List(Of DrinkProduct)
Dim CurrentProducts As New List(Of DrinkProduct)
With CurrentProducts
.Add(New DrinkProduct("CF1kg", "Coffee Powder", "1 Kg", MaterialType.Powder, 15684, 1276))
.Add(New DrinkProduct("CFB500", "Ground Coffee", "500 g", MaterialType.Powder, 22785, 12856))
.Add(New DrinkProduct("CFG500", "Coffee Granules", "500 g", MaterialType.Granules, 19233, 5907))
.Add(New DrinkProduct("Te500", "Tea", "500 g", MaterialType.Leaf, 8544, 235))
.Add(New DrinkProduct("TeInst500", "Instant Tea", "500 g", MaterialType.Powder, 1009, 22))
.Add(New DrinkProduct("SMlk1lt", "Skimmed Milk", "1 Litre", MaterialType.Liquid, 28012, 2650))
.Add(New DrinkProduct("HiJ300", "HiJuice Drink Mix", "300 g", MaterialType.Other, 578, 179))
.Add(New DrinkProduct("Sm400", "Smoothie", "400ml", MaterialType.Paste, 9346, 3284))
.Add(New DrinkProduct("Beef300", "Beef Drink", "300 g", MaterialType.Granules, 8316, 1965))
.Add(New DrinkProduct("Beef750", "Beef Drink", "750 g", MaterialType.Granules, 7612, 359))
End With
Return CurrentProducts
End Function
End Class
The Enumeration named 'MaterialType' identifies whether the product is powder, liquid, granule, etc and this is what I will use as the key for selecting the appropriate image.
The ValueConverter class is similar to those used in the previous blogs - IValueConverter requires the two methods named Convert and ConvertBack. ConvertBack serves no purpose is this scenario, so only throws a not implemented exception.
Public Class MaterialToImagePathConverter
Implements IValueConverter
Public Function Convert(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
Select Case value.ToString
Case "Powder"
Return "Images/powderbrown.jpg"
Case "Liquid"
Return "Images/liquiddrop4.jpg"
Case "Leaf"
Return "Images/leaf.jpg"
Case "Granules"
Return "Images/granules.jpg"
Case "Paste"
Return "Images/Paste2.jpg"
Case Else
Return "Images/questionmark.jpg"
End Select
End Function
Public Function ConvertBack(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
Throw New NotImplementedException()
End Function
End Class
As before, the parameter named 'value' is an Object type which is the key element in the conversion process. It contains the value that is to be converted. The conversion is simple in this case because all we need is to find the string value of the enumeration that is being passed in. So the basic ToString method will work fine.
Once we have a String value, this is compared against the various possibilities. A string which represents the file path to an appropriate image is returned by the converter. I've chosen to include the image files in the project, but of course they could be stored externally and still be accessed in the same way.
The next steps are the same as for the previous examples in the earlier blogs. First, map the current assembly to an XML namespace in Application.xaml:
xmlns:local="clr-namespace:WPFListView2"
Then create an instance of the converter class in Application.xaml and give it a key:
<local:MaterialToImagePathConverter x:Key="IconConverter" />
Next, create a DataTemplate for the new column of the ListView:
<DataTemplate x:Key="IconCellTemplate">
<Image Margin="0,0,1,3" Height="18" Width="25"
Stretch="Fill"
Source="{Binding Path=Material, Converter={StaticResource IconConverter}}" />
</DataTemplate>
In the above markup, the Binding Path is the Material field, the converter is the MaterialToImagePathConverter instance created above.
Finally, the ListView in the Window needs to have a new column added in which the images can be displayed:
<GridViewColumn
CellTemplate="{StaticResource IconCellTemplate}">
</GridViewColumn>
And now you are all set. The finished Window displays as shown below:

For completeness, the full markup for the Window which contains the ListView is shown here:
<Window x:Class="Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Image Path Converter Demo" Height="360" Width="450">
<Grid>
<ListView Name="ProductsListView"
ItemsSource="{Binding}"
Margin="5" >
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<!-- Icon column -->
<GridViewColumn
CellTemplate="{StaticResource IconCellTemplate}">
</GridViewColumn>
<!-- Product ID -->
<GridViewColumn
HeaderTemplate="{StaticResource IDColHeader}"
CellTemplate="{StaticResource IDCellTemplate}">
</GridViewColumn>
<!-- Product Name -->
<GridViewColumn
HeaderTemplate="{StaticResource NameColHeader}"
CellTemplate="{StaticResource NameCellTemplate}">
</GridViewColumn>
<!-- Pack Size -->
<GridViewColumn
HeaderTemplate="{StaticResource PackageColHeader}"
CellTemplate="{StaticResource PackCellTemplate}">
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
Of course you are not forced to use the ValueConverter approach for this task. You could rewrite the class and give it an ImagePath property. Then in the code-behind run a function with a similar Select Case block to the one used above, passing back the appropriate path to the ImagePath property. The collection would be amended on the fly to include this data and finally the amended collection would be used as the DataContext. The Binding Path for the Image Source property would be that field. Personally, I think the ValueConverter is neater. And if the data comes from an external source, perhaps in the form of an XML file, then using the ValueConverter is almost certainly a better way.