MVVM Image Source Binding from in memory bitmap not working

sleepyVision 20 Reputation points
2023-03-04T15:50:40.5333333+00:00

I have a small app that does some font rendering to a bitmap and then attempts to update the UI with the rendererd bitmap using MVVM style data binding.

My view has the following image control...

<Image Name="BitmapImagePreview"
             Source="{Binding PreviewImage}"/>

My View Model exposes the following property

public ImageSource? PreviewImage
        {
            get => _previewImage;
            set
            {
                _previewImage = value;
                NotifyPropertyChanged(nameof(PreviewImage));
            }
        }

However, when I change the PreviewImage property it fails to refresh/re-draw the control.
If I bypass DataBinding, and set the source property directly, it does update.

_viewModel.FontPreview.PreviewImage = image;  <== Does not work
_view.BitmapFontPreview.BitmapImagePreview.Source = image; <== Does work

The image itself is generated in memory from a drawing operation like so...

            var bitmap = new RenderTargetBitmap(
                width,
                height,
                96,
                96,
                PixelFormats.Default);
            bitmap.Render(visual);
            return bitmap;

The rendering is certainly working. The data binding/propertychanged event is also firing correctly, so it seems to me like some kind of caching is going on behind the scenes.

Anyone have any ideas?

Developer technologies | Windows Presentation Foundation
Developer technologies | XAML
Developer technologies | C#
{count} votes

Accepted answer
  1. Peter Fleischer (former MVP) 19,341 Reputation points
    2023-03-04T20:09:27.5833333+00:00

    Hi,
    I cannot reproduce your problem with your code in my demo:

    <Window x:Class="WpfApp1.Window044xaml"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApp044"
            mc:Ignorable="d"
            Title="sleepyVision_230304" Height="200" Width="400">
      <Window.DataContext>
        <local:ViewModel/>
      </Window.DataContext>
      <Grid>
        <Grid.RowDefinitions>
          <RowDefinition/>
          <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Image Source="{Binding PreviewImage}" Margin="10"/>
        <Button Grid.Row="1" Content="Load" Command="{Binding}" Margin="5"/>
      </Grid>
    </Window>
    

    ViewModel:

    using System;
    using System.ComponentModel;
    using System.Globalization;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Media.Media3D;
    
    namespace WpfApp044
    {
    	public class ViewModel : INotifyPropertyChanged, ICommand
    	{
    		private ImageSource? _previewImage;
    		public ImageSource? PreviewImage
    		{
    			get => _previewImage;
    			set
    			{
    				_previewImage = value;
    				NotifyPropertyChanged(nameof(PreviewImage));
    			}
    		}
    
    		public bool CanExecute(object? parameter) => true;
    
    		public void Execute(object? parameter)
    		{
    			using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
    			{
    				FormattedText text = new FormattedText(DateTime.Now.ToString("hh:mm:ss.fff"),
    								new CultureInfo("en-us"),
    								FlowDirection.LeftToRight,
    								new Typeface(new FontFamily("Arial"), FontStyles.Normal, FontWeights.Normal, new FontStretch()),
    								30,
    								Brushes.Red);
    
    				DrawingVisual visual = new DrawingVisual();
    				DrawingContext drawingContext = visual.RenderOpen();
    				drawingContext.DrawText(text, new Point(2, 2));
    				drawingContext.Close();
    
    				int width = 300;
    				int height = 300;
    
    				var bitmap = new RenderTargetBitmap(
    					width,
    					height,
    					96,
    					96,
    					PixelFormats.Default);
    				bitmap.Render(visual);
    				PreviewImage = bitmap;
    			}
    		}
    
    		// INotifyPropertyChanged
    #pragma warning disable CS8612 // Nullability of reference types in type doesn't match implicitly implemented member.
    		public event PropertyChangedEventHandler PropertyChanged;
    		public event EventHandler? CanExecuteChanged;
    		public void NotifyPropertyChanged([CallerMemberName] string info = "") =>
    				PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info));
    	}
    }
    
    

    Result:

    x

    1 person found this answer helpful.

1 additional answer

Sort by: Most helpful
  1. Peter Fleischer (former MVP) 19,341 Reputation points
    2023-03-04T19:56:45.57+00:00

    Hi,
    I cannot reproduce your problem with your code in my demo:

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.