Table of Contents
C# WPF Bitmap to BitmapImage Converter
When working with WPF and list views, there are occasions when you want to grab a bitmap and bind to it in the list view. This is where you will need a converter, that will do the dirty work for you of converting the Bitmap (Old-Money) to a BitmapImage (New-Money) that can be displayed by WPF (Windows Presentation Foundation).
Model
So I have a model that contains a Bitmap, and during the lifecycle of that model the Bitmap is established by streaming it from a URL into memory.
public class Attachment
{
[DefaultValue("")]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string self { get; set; } = String.Empty;
[DefaultValue("")]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string id { get; set; } = String.Empty;
[DefaultValue("")]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string filename { get; set; } = String.Empty;
public Author? author { get; set; } = null;
public DateTime? created { get; set; } = null;
public int size { get; set; }
[DefaultValue("")]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string mimeType { get; set; } = String.Empty;
[DefaultValue("")]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string content { get; set; } = String.Empty;
[DefaultValue("")]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string thumbnail { get; set; } = String.Empty;
public Bitmap? image { get; set; } = null;
}Streaming the Bitmap From URL
So to get the Bitmap from the URL i have a two methods the initial one doesn’t really do much unlike some of the other methods established in the interface, but fields the request to a private method which uses the private read-only HTTPClient to get the Bitmap and stream it into a Bitmap object.
public async Task<Bitmap?> GetBitmapAsync(string thumbnail)
{
_logger.Information("EnvironmentMangerHTTPClient - GetBitmapAsync");
try
{
Bitmap? returnBitmap = await GetBitmap(thumbnail);
return returnBitmap;
}
catch (Exception ex)
{
_logger.Fatal(String.Concat("EnvironmentMangerHTTPClient - GetJiraProjectsAsync - ", ex.Message));
return null;
}
}
private async Task<Bitmap?> GetBitmap(string url)
{
_logger.Information(String.Format("EnvironmentMangerHTTPClient - GetBitmap - Url : {0} ", url));
try
{
_httpClient.DefaultRequestHeaders.Clear();
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Basic {_jiraAccountHelpers.Credentials}");
var dataStream = await _httpClient.GetStreamAsync(url);
var memStream = new MemoryStream();
await dataStream.CopyToAsync(memStream);
memStream.Position = 0;
Bitmap newbitmap = new Bitmap(memStream);
return newbitmap;
}
catch(Exception ex)
{
_logger.Fatal(String.Concat("EnvironmentMangerHTTPClient - GetBitmap - ", ex.Message));
return null;
}
}Somewhere else in my code i use these methods to establish the bitmap in the model.
attachment.image = _jiraHttpClientService.GetBitmapAsync(attachment.thumbnail).Result;
Setting Up WPF
Within the namespace declaration within the WPF XAML we have to add a namespace to our Converter.
xmlns:Converters="clr-namespace:JiraRestAPI.WPF.Converters"
We can then create a new windows resource pointing to the specific class that defines the converter we want to use.
<Window.Resources>
<Converters:ByteArrayToBitmapImageConverter x:Name="binaryConverter" x:Key="byteToImageConverter"/>
</Window.Resources>and finally in the WPF code, in this case i have a list view that contains an Image which I’m binding to the Image in the model but I’m also defining the converter that will convert the Bitmap to a BitmapImage for WPF to render.
<ListView
ItemsSource="{Binding Path=WPFAttachmentHelpers.ListOfAttachments}"
Grid.Row="0"
Margin="0,20,20,0">
<ListView.View>
<GridView>
<GridViewColumn Header="ID" DisplayMemberBinding="{Binding Path=id}"/>
<GridViewColumn Header="File Name" DisplayMemberBinding="{Binding Path=filename}"/>
<GridViewColumn Header="Author" DisplayMemberBinding="{Binding Path=author.displayName}"/>
<GridViewColumn Header="Created" DisplayMemberBinding="{Binding Path=created}"/>
<GridViewColumn Header="Mime Type" DisplayMemberBinding="{Binding Path=mimeType}"/>
<GridViewColumn>
<GridViewColumnHeader Content="Thumbnail"/>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Height="80" Width="160" Stretch="Fill" Source="{Binding Path=image, Converter={StaticResource byteToImageConverter}}"/>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>Converter Class
Finally the nuts and the bolts, within a converter we have to implement the IValue interface, this defines two methods Convert and ConvertBack. Were not to interested in the ConvertBack so as a result its just left as Not Implemented. The Convert method creates a new ImageConverter and then translates our Value (the Bitmap) to and array of bytes. This is then passed into a method that converts the array of bites to a BitmapImage, this is done using a memory stream.
public class ByteArrayToBitmapImageConverter : IValueConverter
{
public static BitmapImage? ConvertByteArrayToBitMapImage(byte[]? imageByteArray)
{
if (imageByteArray != null)
{
BitmapImage img = new BitmapImage();
using (MemoryStream memStream = new MemoryStream(imageByteArray))
{
img.BeginInit();
img.CacheOption = BitmapCacheOption.OnLoad;
img.StreamSource = memStream;
img.EndInit();
img.Freeze();
}
return img;
}
return null;
}
public object? Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
ImageConverter converter = new ImageConverter();
byte[]? imageByteArray = (byte[]?)converter.ConvertTo(value, typeof(byte[]));
return ConvertByteArrayToBitMapImage(imageByteArray);
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}