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(); } }