C# WPF Bitmap to BitmapImage Converter

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