skip to Main Content

That’s my basic code:

public class MyObject
{
    public decimal MyDeciaml { get; set; }
    public string? MyImageBase64
    {
        get
        {
            if (MyImageBase64 != null)
            {
                byte[] imageBytes = Convert.FromBase64String(MyImageBase64);
                using (MemoryStream stream = new MemoryStream(imageBytes))
                {
                    BitmapImage bitmapImage = new BitmapImage();
                    bitmapImage.BeginInit();
                    bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
                    bitmapImage.StreamSource = stream;
                    bitmapImage.EndInit();
                    bitmapImage.Freeze();
                    MyImageBitmap = bitmapImage;
                }
            }

            return MyImageBase64;
        }
        set { }
    }
    [JsonIgnore]
    public BitmapImage? MyImageBitmap { get; set; }
}

which I bind using a ViewModelData class and ObservableCollection<MyObject> myObjects = new ObservableCollection<MyObject>(); against a ListBox:

<ListBox ItemsSource="{Binding MyObjects}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <Grid>
                    <Image x:Name="myImage"
                           Source="{Binding MyImageBitmap}"
                           VerticalAlignment="Center"
                           HorizontalAlignment="Center"
                           Width="Auto"
                           Height="Auto" />
                </Grid>
                <TextBlock Text="{Binding MyDeciaml }" />
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

It seems to works nice on binding. Now, when I serialize it on json using Newtonsoft.Json‘s JsonConvert.SerializeObject(data), I see this on the json:

"MyImageBitmap": "System.Windows.Media.Imaging.BitmapImage"

How can I remove it? It seems [JsonIgnore] has no effect?

I want to remove, and just keep MyImageBase64 (on deserialization, convert it to BitmapImage, and get back the image).

Thanks for any tips on this. Maybe a better way is to bind directly the base64 string and do the conversion to BitmapImage on ListBox, if possible?

2

Answers


  1. Your code won’t work at all. An assignment to the MyImageBase64 property has no effect because the property setter is empty. And the getter would recursively call itself and hence end up in a StackOverflow exception.

    You may write it like shown below, where the BitmapImage is only created when the getter of the (read-only) MyImageBitmap property is actually accessed.

    public class MyObject
    {
        private string myImageBase64;
        private BitmapImage myBitmapImage;
    
        public string MyImageBase64
        {
            get { return myImageBase64; }
            set
            {
                myImageBase64 = value;
                myBitmapImage = null; // reset
            }
        }
    
        public BitmapImage MyImageBitmap
        {
            get
            {
                if (myBitmapImage == null && !string.IsNullOrEmpty(myImageBase64))
                {
                    byte[] imageBytes = Convert.FromBase64String(myImageBase64);
    
                    using (MemoryStream stream = new MemoryStream(imageBytes))
                    {
                        myBitmapImage = new BitmapImage();
                        myBitmapImage.BeginInit();
                        myBitmapImage.CacheOption = BitmapCacheOption.OnLoad;
                        myBitmapImage.StreamSource = stream;
                        myBitmapImage.EndInit();
                        myBitmapImage.Freeze();
                    }
                }
    
                return myBitmapImage;
            }
        }
    }
    

    Besides that, it is unclear why the JsonIgnore attribute won’t work in your case. You may however omit the MyImageBitmap completely and create the BitmapImage in a Binding Converter.

    The converter implementation could like like this:

    public class Base64ToBitmapImageConverter : IValueConverter
    {
        public object Convert(
            object value, Type targetType, object parameter, CultureInfo culture)
        {
            var imageBytes = System.Convert.FromBase64String(value.ToString());
    
            using (var stream = new MemoryStream(imageBytes))
            {
                var bitmapImage = new BitmapImage();
                bitmapImage.BeginInit();
                bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
                bitmapImage.StreamSource = stream;
                bitmapImage.EndInit();
                bitmapImage.Freeze();
                return bitmapImage;
            }
        }
    
        public object ConvertBack(
            object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }
    

    In XAML, it would typically be declared as resource:

    <Window.Resources>
        ...
        <local:Base64ToBitmapImageConverter x:Key="Base64ToBitmapImageConverter"/>
    </Window.Resources>
    

    And used in a Binding like this:

    <Image Source="{Binding MyImageBase64,
                    Converter={StaticResource Base64ToBitmapImageConverter}}" .../>
    
    Login or Signup to reply.
  2. Use a byte array for the image in the data class.

    • Newtonsoft.Json uses a base64 converter for byte arrays by default
    • Image.Source can be bound to a byte array

    So change your class to

    public class MyObject
    {
        public decimal MyDeciaml { get; set; }
        public byte[] MyImage { get; set; }
    }
    

    and the Xaml to

    <ListBox ItemsSource="{Binding MyObjects}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <Grid>
                        <Image x:Name="myImage"
                               Source="{Binding MyImage}"
                               VerticalAlignment="Center"
                               HorizontalAlignment="Center"
                               Width="Auto"
                               Height="Auto" />
                    </Grid>
                    <TextBlock Text="{Binding MyDeciaml}" />
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search