Binding image to property in wpf with image from resources - c#

i have some pictures included to my project.
this line of code works fine:
<Image Stretch="UniformToFill" Source="/Images/OffsetSituations/offsetsituation1.jpg"
but i have to change the picture from the VM so i made a property to bind to:
private ImageSource _image;
public ImageSource Image
{
get { return _image; }
set
{
if (_image == value)
return;
_image = value;
RaisePropertyChanged("Image");
}
}
from another post here on stackoverflow i got this code and changed it a bit:
string picture = "offsetsituation1";
if (!string.IsNullOrEmpty(picture))
{
var image = new BitmapImage(new Uri(String.Format("/Images/OffsetSituations/{0}.jpg", picture), UriKind.Relative));
image.Freeze(); // -> to prevent error: "Must create DependencySource on same Thread as the DependencyObject"
Image = image;
}
else
{
Image = null;
}
now in the xaml:
<Image Stretch="UniformToFill" Source="{Binding Image}" Margin="5"/>
but there never is a picture.
i added MessageBox.Show(Image.ToString()); after Image = image; to debug. it shows /Images/OffsetSituations/offsetsituation1.jpg, so the path seems to be right.
what am i doing wrong here?

WPF provide implicit converters for most of the framework types including ImageSource
all you have to do is provide the correct image path as string and let the implicit converter do the rest
so change the type of Image property to string
eg
public string Image
{
get { return _image; }
set
{
if (_image == value)
return;
_image = value;
RaisePropertyChanged("Image");
}
}
and assign the path as a string
Image = String.Format("/Images/OffsetSituations/{0}.jpg", picture);
that's all you need to show the image, rest will be handled by the framework
implicit converter are so handy in many places including this one.

Related

WPF Image won't update programmatically

I have an application where I want it to load an image when a command is invoked. But the problem is that nothing loads and nothing breaks either. I just dont see my image. I also made sure that I was setting the data context to my view model.
XAML:
<Image Grid.Column="3" Source="{Binding Path=LoadingImage, Mode=TwoWay}" Width="35" Height="35"/>
ViewModel:
private Image _loadingImage = new Image();
public Image LoadingImage
{
get => _loadingImage;
set
{
_loadingImage = value;
RaisePropertyChanged(nameof(LoadingImage));
}
}
//Method called by the command... i debugged it and it gets here just fine
private void GetDirectories()
{
FolderBrowserDialog folderBrowseDialog = new FolderBrowserDialog();
DialogResult result = folderBrowseDialog.ShowDialog();
if (result == DialogResult.OK)
{
//This is how I am getting the image file
LoadingImage.Source = new BitmapImage(new Uri("pack://application:,,,/FOONamespace;component/Resources/spinner_small.png"));
//More code below
}
}
Some other settings, my .png file has the following properties:
Build Action: Resource
Copy to Output Directory: Copy if newer
This is head scratcher for me. What am I doing wrong? Many thanks.
You can't use an Image element as the value of the Source property of another Image element.
Change the property type to ImageSource:
private ImageSource _loadingImage;
public ImageSource LoadingImage
{
get => _loadingImage;
set
{
_loadingImage = value;
RaisePropertyChanged(nameof(LoadingImage));
}
}
and assign the property like this:
LoadingImage = new BitmapImage(
new Uri("pack://application:,,,/FOONamespace;component/Resources/spinner_small.png"));
Besides that, setting the Binding's Mode to TwoWay is pointless
<Image Source="{Binding LoadingImage}" />
and copying to the output directory is also unnecessary, because the Build Action Resource makes the image file an assembly resource that is compiled into the assembly.

ImageFailed with GridView

I have a problem with GridView and multiple items. Each of the item has image in it, source is online image, bound to property, like this:
<GridView x:Name="gridView" Width="710" ItemTemplate="{StaticResource FirstTemplate}" AllowDrop="True" CanDragItems="True" CanReorderItems="True">
<DataTemplate x:Key="FirstTemplate">
<Grid HorizontalAlignment="Left" Width="306" Height="210">
<Border Background="White" Opacity="0.1"/>
<Image Stretch="Uniform" Width="190" Height="100" Margin="0,50,0,0" ImageFailed="ImageFailed" Source="{Binding ImagePath}"/>
</Grid>
</DataTemplate>
Image paths are like this:
www.example.com/images/1.png
www.example.com/images/2.png
www.example.com/images/3.png
and so on...
If some image not exist, for example www.example.com/images/29.png, I use the event ImageFailed, which change the source of the image to image that is located in my project (default image). Code in this event:
private void ImageFailed(object sender, ExceptionRoutedEventArgs e)
{
var image = sender as Image;
image.Source = new BitmapImage(new Uri("ms-appx:///Images/default.png"));
}
And this is working just fine, the default image is shown in the items that don't have images. But, when I scroll down the gridview, and then return to the beginning, images are messed up. Some items that had their images, now have the default image. Again when I scroll the gridview, and then return, again random changes with images.
Is this some cache problem? What could be the problem here? Or is there any better way of setting the default image source?
The source of your problem could be virtualization, i.e. reuse of item containers. When you replace a failed image by a fallback image in your ImageFailed handler, you are effectively replacing the Binding by a fixed value, so that the item container will later always show only the fallback image.
You may instead implement the ImageFailed handler in the view model, so that replacing the image with a fallback image won't break the Binding.
Add another property, e.g. Image to your item class
public class ImageItem
{
public string ImagePath { get; set; }
private BitmapImage image;
public BitmapImage Image
{
get
{
if (image == null)
{
image = new BitmapImage();
image.ImageFailed += (s, e) =>
image.UriSource = new Uri("ms-appx:///Images/default.png");
image.UriSource = new Uri(ImagePath);
}
return image;
}
}
}
and change the Binding to this:
<Image ... Source="{Binding Image}"/> // no ImageFailed handler here

WPF Binding Image Source

maybe stupid question, but I don't know anymore...
I have ViewModel class like this:
public class MainWindowsViewModel : INotifyPropertyChanged
{
private ImageSource _img;
public ImageSource StatusImage
{
get { return _img; }
set
{
_img = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName]String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Binding in XAML looks like this:
<Window.DataContext>
<VM:MainWindowsViewModel />
</Window.DataContext>
<Image x:Name="gui_image_status" HorizontalAlignment="Left" Height="26" Margin="144,10,0,0" VerticalAlignment="Top" Width="29" Source="{Binding Path=StatusImage}" />
And I set content of ImageSource like this:
MainWindowsViewModel _view = new MainWindowsViewModel();
var yourImage = new BitmapImage(new Uri(String.Format("Sources/{0}.png", "red"), UriKind.Relative));
_view.StatusImage = yourImage;
But it does not work. I think that problem is in that NotifyPropertyChanged, because I tried place brake point in the set and get. Get triggered few times at the start, after then set triggered as well with correct ImageSource, but after then get did not triggered anymore. Like no setting ever happened.
It's really simply binding that I have done many times similarly...I don't know why it doesn't work this time.
You are creating two instances of your MainWindowsViewModel class, one in XAML by
<Window.DataContext>
<VM:MainWindowsViewModel />
</Window.DataContext>
and one in code behind by
MainWindowsViewModel _view = new MainWindowsViewModel();
So your code behind sets the property on a different view model instance than the one the view is bound to.
Change your code behind to this:
var viewModel = (MainWindowsViewModel)DataContext;
viewModel.StatusImage = new BitmapImage(...);
I didn't find any problems in your code, but you can try to check few things.
Check that your Image added to the project and set build action of images to Content (copy if newer).
Before updating ImageSource call Freeze method to prevent error: "Must create DependencySource on same Thread as the DependencyObject"
var yourImage = new BitmapImage(new Uri(String.Format("Sources/{0}.png", "red"), UriKind.Relative));
yourImage.Freeze();
_view.StatusImage = yourImage;
Also, there is an easier way to bind image in WPF. You can use string as a source and set a resource path to the binded property:
public string StatusImage
{
get { return "/AssemblyName;component/Sources/red.png"; }
}

Image Source bound to missing file

How can I display a default image when the bound path file is missing?
<Image Source="{Binding DisplayedBook.ImagePath}" />
My solution: Used a converter, which check if the image exists and returns the appropriate path.
if you don't want to return default image from ImagePath getter, than another approach is to return null.
public string ImagePath
{
get
{
return File.Exists(m_Path) ? m_Path : null;
}
}
and in XAML use TargetNullValue property of Binding
<Image Source="{Binding DisplayedBook.ImagePath, TargetNullValue={StaticResource SomeImageResource}}" />
If you have code-behind associated with this XAML (i.e. not a Template) you can set a default image on the ImageFailed event:
<Image Source="{Binding ImagePath}" ImageFailed="Image_ImageFailed" />
and the handler:
private void Image_ImageFailed(object sender, ExceptionRoutedEventArgs e)
{
Image image = e.Source as Image;
if (image != null)
image.Source = new BitmapImage(new Uri("http://SomeDefaultImagePath.jpg"));
}
I don' use wpf, so i don't know if there exists such a special feature.
But i would implement such a thing in the getter method of DisplayedBook.ImagePath. It checks if the file exists and if not return a path to some default image.
You can probably do something like this:
Get the path of the image in path.
if (!File.Exists(path))
{
BitmapImage image = new BitmapImage();
image.BeginInit();
image.UriSource = new Uri(path);
image.DecodePixelWidth = Convert.ToInt32(img.Width);
image.EndInit();
//Set the image corresponding to that bound
this.img.Source = image;
}
You can also add the FallbackValue property which Gets or sets the value to use when the binding is unable to return a value.

How do I bind an Image dynamically in XAML?

The following displays an image correctly in a silverlight usercontrol:
Image image = pagingManager.BaseApp.DatasourceManager.GetImage("helloWorld1");
ContentArea.Content = image;
...
<ContentControl x:Name="ContentArea"/>
However, I want to dynamically bind the image to XAML, e.g. like this:
#region ViewModelProperty: MainImage
private Image _mainImage;
public Image MainImage
{
get
{
return _mainImage;
}
set
{
_mainImage = value;
OnPropertyChanged("MainImage");
}
}
#endregion
...
MainImage = pagingManager.BaseApp.DatasourceManager.GetImage("helloWorld1");
And my XAML is this but the result is that it shows nothing:
<Image Source="{Binding MainImage}"/>
What do I need to put in my XAML to make it display the image object I have in my ViewModelProperty?
The Image.Source property is of type ImageSource, so your ViewModel should expose an ImageSource. Image is a control, it has nothing to do in the ViewModel.
If you do expose an Image control in your ViewModel (which is definitely a bad idea), then you should display it in a ContentControl, not an Image control :
<ContentControl Content="{Binding MainImage}" />

Categories