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?

Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,667 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,207 questions
XAML
XAML
A language based on Extensible Markup Language (XML) that enables developers to specify a hierarchy of objects with a set of properties and logic.
762 questions
{count} votes

Accepted answer
  1. Peter Fleischer (former MVP) 19,231 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,231 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