question

MarcJeeves-9615 avatar image
0 Votes"
MarcJeeves-9615 asked MarcJeeves-9615 answered

WPF Image Binding Fails Form View Model

I am really struggling with this concept of setting an image source for a model property in the ViewModel and then binding to it. I have tried every way been on every forum but i cant get it to work correctly.

The odd thing is if i pause the code at line .65 and expand the Bitmap object and then allow the code to carry on running the bitmap shows in the UI. If i don't pause the code the image never shows this has to be a bug especially when my other bound variables show correctly.

Can somebody use my code to replicate the problem? And then show me what I'm doing wrong? or is it truly a bug with .net Core?

Thanks in advance please put me out of my missery....... I'm a self taught coder with only a little experience in WPF MVVM and binding.

Madaxe



     <Window x:Class="UI.Project.MainWindow"
             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:UI.Project"
             xmlns:convertors="clr-namespace:Infrastructure.Project.Convertors;assembly=Infrastructure.Project"
             mc:Ignorable="d"
             Title="MainWindow" Height="450" Width="800">
    
     <Window.Resources>
         <convertors:StringtoBitmapImageConvertor x:Key="StringtoBitmapImageConvertor" />
     </Window.Resources>
    
     <Grid>
         <Button
             x:Name="Btn_ShowSimpleDialogue"
             Content="Button"
             Command="{Binding Btn_ShowSimpleDialogue_Click}"
             CommandParameter="{Binding ElementName=Btn_ShowSimpleDialogue}"
             HorizontalAlignment="Left" 
             Margin="23,52,0,0" 
             VerticalAlignment="Top"/>
         <Image Source="{Binding BitmapImage}" Width="200" VerticalAlignment="Bottom"/>
     </Grid>
 </Window>


 using System.Windows;
 using ViewModels.Project;
    
 namespace UI.Project
 {
     public partial class MainWindow : Window
     {
         public MainWindow()
         {
             InitializeComponent();
             DataContext = new MainViewModel();
         }
     }
 }



 using Infrastructure.Project.WPF;
 using Models.Project.Models;
 using System;
 using System.Windows;
 using System.Windows.Media.Imaging;
 using UICatalog_Project.UI.Notification;
    
    
 namespace ViewModels.Project
 {
     public class MainViewModel :MainModel
     {
         public RelayCommand Btn_ShowSimpleDialogue_Click { get; private set; }
    
         public MainViewModel()
         {
             Btn_ShowSimpleDialogue_Click = new RelayCommand(ShowSimpleDialogue, CanShowSimpleDialogue);
             BitmapImage = new BitmapImage(new Uri(@"./Assets/NikolaDownload.png", UriKind.Relative));
             //If i review the var BitmapImage by pausing the code post its initilization the image shows in the main UI
             ImageSource = new Uri(@"../Assets/NikolaDownload.png", UriKind.Relative);
         }
    
         void ShowSimpleDialogue(object message)
         {
    
             SimpleNotificationViewModel simpleNotificationViewModel = new SimpleNotificationViewModel();          
             simpleNotificationViewModel.Title = "Error Message";
             simpleNotificationViewModel.Description = "Hello im An error";
             simpleNotificationViewModel.ErrorImage = new Uri(@"..\Assets\NikolaDownload.png",UriKind.Relative);
             Window SimpleDialogue = new SimpleNotification();
             SimpleDialogue.DataContext = simpleNotificationViewModel;
             SimpleDialogue.Show();
         }
    
         bool CanShowSimpleDialogue(object message)
         {
             return true;
         }
     }
 }



 using Models.Project.AbstractModels;
 using System;
 using System.Windows.Media.Imaging;
    
 namespace Models.Project.Models
 {
     public class MainModel : BaseModel
     {
         Uri _ImageSource;
         public Uri ImageSource
         {
             get => _ImageSource;
             set { _ImageSource = value; NotifyPropertyChanged(); }
         }
    
         BitmapImage _BitmapImage;
         public BitmapImage BitmapImage
         {
             get => _BitmapImage;
             set { _BitmapImage = value; NotifyPropertyChanged(); }
         }
     }
 }
    


 using System.ComponentModel;
 using System.Runtime.CompilerServices;
    
 namespace Models.Project.AbstractModels
 {
     public abstract class BaseModel : INotifyPropertyChanged
     {
         #region INotifyPropertyChanged Members
         public event PropertyChangedEventHandler PropertyChanged;
         protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
         {
             if (PropertyChanged != null)
             {
                 PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
             }
         }
         #endregion
     }
 }


 using System;
 using System.Windows.Input;
    
 namespace Infrastructure.Project.WPF
 {
     public class RelayCommand : ICommand
     {
         readonly Action<object> _execute;
         readonly Predicate<object> _canExecute;
    
         public RelayCommand(Action<object> execute, Predicate<object> canExecute)
         {
             if (execute == null)
             {
                 throw new NullReferenceException("execute");
             }
             else
             {
                 this._execute = execute;
                 this._canExecute = canExecute;
             }
         }
         public RelayCommand(Action<object> execute) : this(execute, null)
         {
    
         }
    
         public event EventHandler CanExecuteChanged
         {
             add { CommandManager.RequerySuggested += value; }
             remove { CommandManager.RequerySuggested -= value; }
         }
         public bool CanExecute(object parameter)
         {
             return this._canExecute == null ? true : this._canExecute(parameter);
         }
         public void Execute(object parameter)
         {
             this._execute.Invoke(parameter);
         }
     }
 }




windows-wpf
· 3
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Not sure whether it'll solve the problem BUT I faced somewhat similar issues when I did like this DataContext = new MainViewModel();. I'd to do it this way:

  public partial class MainWindow : Window
  {
      MainViewModel model;
      public MainWindow()
      {
          InitializeComponent();
          model = new MainViewModel();
          DataContext = model;
      }
  }
0 Votes 0 ·

So its the same behavior, if i add a code stop after the initialization of the viewmodel and inspect the bitmap variabale it shows in the UI if i dont it wont

but thanks for the suggestion

0 Votes 0 ·

@MarcJeeves-9615, sounds like somewhere in your code the image is deleted by the GC. I faced similar issue once in DataContext and the other in ICollectionView. To solve these issues I'd to keep a reference field for ViewModel, that is set as the DataContext, and CollectionViewSource which is used by ICollectionView.

0 Votes 0 ·
MarcJeeves-9615 avatar image
1 Vote"
MarcJeeves-9615 answered

I found a solution

I changed my property type to BitmapSource and then wrote this helper to convert the image path to a BitmapSource and now it works fine


 using System;
 using System.Drawing;
 using System.Windows;
 using System.Windows.Interop;
 using System.Windows.Media.Imaging;
    
 namespace Infrastructure.Project.Image
 {
     public static class BitmapHelper
     {
         public static BitmapSource BitmapToBitmapSource(Bitmap bitmap)
         {
             return Imaging.CreateBitmapSourceFromHBitmap(
                             bitmap.GetHbitmap(),
                             IntPtr.Zero,
                             Int32Rect.Empty,
                             BitmapSizeOptions.FromEmptyOptions());
         }
    
         public static BitmapSource PathToBitmapSource(string path)
         {
             return BitmapHelper.BitmapToBitmapSource((Bitmap)Bitmap.FromFile(path, true));
         }
     }
 }

here is my message box being called


             SimpleNotificationViewModel simpleNotificationViewModel = new SimpleNotificationViewModel();          
             simpleNotificationViewModel.Title = "Error Message";
             simpleNotificationViewModel.Description = "Hello im An error";
             simpleNotificationViewModel.BitmapSource = BitmapHelper.PathToBitmapSource(@"./Assets/NikolaDownload.png");
             Window SimpleDialogue = new SimpleNotification();
             SimpleDialogue.DataContext = simpleNotificationViewModel;
             SimpleDialogue.Show();


And my XAML binding

 Image 
                 x:Name="Img_SimpleNotifcation"
                 Source="{Binding DataContext.BitmapSource,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}"
                 Width="200" 
                 Height="200" 
                 VerticalAlignment="Top" 
                 HorizontalAlignment="Left" 
                 Margin="10,10,10,10">
             </Image>

5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

MarcJeeves-9615 avatar image
1 Vote"
MarcJeeves-9615 answered

In addition i wrote a convertor so i can bind directly to the path

 using System;
 using System.Drawing;
 using System.Globalization;
 using System.Windows;
 using System.Windows.Data;
 using System.Windows.Interop;
 using System.Windows.Media.Imaging;
    
    
 namespace Infrastructure.Project.Convertors
 {
     public class StringtoBitmapSourceConvertor : IValueConverter
     {
         public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
             if (value != null && value is string)
             {
                 string Path = (string)value;
                 return Imaging.CreateBitmapSourceFromHBitmap(
                                             ((Bitmap)Bitmap.FromFile(Path, true)).GetHbitmap(),
                                             IntPtr.Zero,
                                             Int32Rect.Empty,
                                             BitmapSizeOptions.FromEmptyOptions());
             }
             return null;
         }
    
         public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
         {
             throw new NotImplementedException();
         }
     }
 }



 <Image 
                 x:Name="Img_SimpleNotifcation"
                 Source="{Binding DataContext.BitmapPath,Converter={StaticResource StringtoBitmapSourceConvertor},RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}"
                 Width="200" 
                 Height="200" 
                 VerticalAlignment="Top" 
                 HorizontalAlignment="Left" 
                 Margin="10,10,10,10">
             </Image>


5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.