Validating WPF Bitmap Image Loaded from URI at Runtime - c#

I am trying to load a BitmapImage at runtime from a URI. I use a default image in my XAML user control which I'd like to replace via databindings. This works.
The problem I'm having is in situations where an invalid file is used for the replacement image (maybe it's a bad URI, or maybe the URI specifies a non-image file). When this happens, I want to be able to check the BitmapImage object to see if it was correctly loaded. If not, I want to stick to the default image being used.
Here's the XAML:
<UserControl x:Class="MyUserControl">
<Grid>
<Image
x:Name="myIcon"
Source="Images/default.png" />
</Grid>
</UserControl>
And the relevant codebehind:
public static readonly DependencyProperty IconPathProperty =
DependencyProperty.Register(
"IconPath",
typeof(string),
typeof(MyUserControl),
new PropertyMetadata(null, new PropertyChangedCallback(OnIconPathChanged)));
public string IconPath
{
get { return (string)GetValue(IconPathProperty); }
set { SetValue(IconPathProperty, value); }
}
private static void OnIconPathChanged(
object sender,
DependencyPropertyChangedEventArgs e)
{
if (sender != null)
{
// Pass call through to the user control.
MyUserControl control = sender as MyUserControl;
if (control != null)
{
control.UpdateIcon();
}
}
}
public void UpdateIcon()
{
BitmapImage replacementImage = new BitmapImage();
replacementImage.BeginInit();
replacementImage.CacheOption = BitmapCacheOption.OnLoad;
// Setting the URI does not throw an exception if the URI is
// invalid or if the file at the target URI is not an image.
// The BitmapImage class does not seem to provide a mechanism
// for determining if it contains valid data.
replacementImage.UriSource = new Uri(IconPath, UriKind.RelativeOrAbsolute);
replacementImage.EndInit();
// I tried this null check, but it doesn't really work. The replacementImage
// object can have a non-null UriSource and still contain no actual image.
if (replacementImage.UriSource != null)
{
myIcon.Source = replacementImage;
}
}
And here's how I might create an instance of this user control in another XAML file:
<!--
My problem: What if example.png exists but is not a valid image file (or fails to load)?
-->
<MyUserControl IconPath="C:\\example.png" />
Or maybe someone can suggest a different/better way to go about loading an image at runtime. Thanks.

Well, BitmapImage class has two events, which will be raised when either download or decoding has failed.
yourBitmapImage.DownloadFailed += delegate { /* fall to your def image */ }
yourBitmapImage.DecodeFailed += delegate { /* fall to your def img */ }
On a side note, if you're trying to implement fallback placeholder: http://www.markermetro.com/2011/06/technical/mvvm-placeholder-images-for-failed-image-load-on-windows-phone-7-with-caliburn-micro/ seems nice.

It's crude, but I found that a non-valid BitmapImage will have width and height of 0.
BitmapImage image;
if(image.Width > 0 && image.Height > 0)
{
//valid image
}

You can try this check
if (bitmapImage.UriSource==null || bitmapImage.UriSource.ToString()).Equals(""))
{
Console.WriteLine("path null");
}
else
{
bitmapImage.EndInit();
}

This has already been answered. Please have a look at this and this. I think that both of them somewhat answer your question but I would prefer the former approach as it even checks for the contentType of the remote resource.
You can also have a look at this post.
Update:
In case of local files, this can be checked by simply creating an Image object using the Uri, or the path. If it is successful, it means the image is a valid image:
try
{
Image image = Image.FromFile(uri);
image.Dispose();
}
catch (Exception ex)
{
//Incorrect uri or filetype.
}

Related

Update an image source when pressing a button

I have a problem with Uri and my image file.
I tested a lot of things on how to make it work but without success.
(I have image A on start. I click on image and image A change to B.)
Please can anyone expain me this problem?
I know here is lot questions about this but i still dont understand it.
Thx in advance
XAML:
<Image x:Name="obr_0_1" Grid.ColumnSpan="1"
Grid.Column="0" Grid.Row="0"
Tapped="obr_0_1_Tapped" Loaded="obr_0_1_Loaded"/>`
C#:
private void obr_0_1_Tapped(object sender, TappedRoutedEventArgs e)
{
zmenObrazek();
}
private void zmenObrazek()
{
obr_0_1.Source = new BitmapImage(new Uri("/Content/Obrazky/half-life.png", UriKind.Relative));
}
When I set the source this way, I get:
An exception of type 'System.ArgumentException' occurred in mscorlib.dll but was not > handled in user code
How do I set the image source from code ?
What about implementing the following static class:
using System;
using System.IO;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
// I don't know your namespace but put it in the same namespace as your code above
// (or reference this namespace in your code above)
namespace MyNamespace
{
public static class Helper
{
public static Image CreateImage(string fileName, int desiredPixelWidth)
{
Image myImage = new Image();
//set image source
myImage.Source = CreateSource(fileName);
myImage.Width = desiredPixelWidth;
return myImage;
}
public static BitmapImage CreateSource(string fileName)
{
var file = new FileInfo(fileName);
System.Drawing.Image im = System.Drawing.Image.FromFile(file.FullName);
int actualPixelWidth = im.Width;
Uri fileUri = new Uri(file.FullName);
// Create source
BitmapImage myBitmapImage = new BitmapImage();
// BitmapImage.UriSource must be in a BeginInit/EndInit block
myBitmapImage.BeginInit();
myBitmapImage.UriSource = fileUri;
// To save significant application memory, set the DecodePixelWidth or
// DecodePixelHeight of the BitmapImage value of the image source to the desired
// height or width of the rendered image. If you don't do this, the application will
// cache the image as though it were rendered as its normal size rather then just
// the size that is displayed.
// Note: In order to preserve aspect ratio, set DecodePixelWidth
// or DecodePixelHeight but not both.
myBitmapImage.DecodePixelWidth = actualPixelWidth;
myBitmapImage.EndInit();
return myBitmapImage;
}
}
}
Then in your source code you do the following
private void zmenObrazek()
{
// do you need the first forward slash?
// (I assume "Content" is a folder in the bin directory)
obr_0_1.Source = Helper.CreateSource("Content/Obrazky/half-life.png");
}
Is the image above (half-life.png) found in the bin folder under Content/Obrazky?
If not you may also want to change the image property, Copy to Output Directory, to "Copy always" (when you add an image to project folder the default for this property is "Do not copy").

Performing If statement on a Textblock in C#

Question:
Trying to essentially say IF my TextBlock contains "X" THEN Perform the IF but I don't know how to write that in the correct context.
Example:
If the word "Chocolate" is in the TextBlock I'd like to show an Image of "Chocolate" (I already know how to correctly display the image, my problem is with the IF Statement itself)
I'm new to this kinda stuff and I'd like like to know for future reference.
Problems:
Right now, I don't know HOW to perform an IF statement on a TextBlock/String in a way that actually works.
Attempted:
if (string content.contains("Chocolate"));
{
Uri Pure = new Uri("Images/ChocolatePortrait.png", UriKind.Relative);
BitmapImage imageSource = new BitmapImage(Pure);
image2.Source = imageSource;
}
if (textBlock.text = ("Chocolate"));
{
Uri Pure = new Uri("Images/ChocolatePortrait.png", UriKind.Relative);
BitmapImage imageSource = new BitmapImage(Pure);
image2.Source = imageSource;
}
The problem is that you are terminating the if-statement prematurely by a semicolon ;!
if (textBlock.Text.Contains("Chocolate")) // <= removed the ";"
{
Uri Pure = new Uri("Images/ElfPortrait.png", UriKind.Relative);
BitmapImage imageSource = new BitmapImage(Pure);
image2.Source = imageSource;
}
Also the property is Text with an upper case "T". In C# Text and text are two different identfiers!
Many programmers prefer to write the opening brace on the same line. This makes it clearer that the end of the line is not the end of a statement:
if (condition) {
statement-sequence
}
Note that "The Chocolate is fine!".Contains("Chocolate") returns true. If the whole string must be equal to the word then compare with textBlock.Text == "Chocolate"
use == to test for equality
if (textBlock.Text == "Chocolate")
{
Uri Pure = new Uri("Images/ChocolatePortrait.png", UriKind.Relative);
BitmapImage imageSource = new BitmapImage(Pure);
image2.Source = imageSource;
}
you could also do something like this:
if (textBlock.Text.Contains("Chocolate", StringComparison.OrdinalIgnoreCase))
{
// ....
}
if you're looking for the word "chocolate" in the text block's text and you don't want the check to be case sensitive.
if(YOUR_STRING_HERE.Contains(STRING_YOU_WANT_TO_FIND))
{
//do your stuff here
}
You can either check for equality with:
if (textBlock.text.Equals("Chocolate");
{
//do whatever must be done here
}
Or you can also use == (then you dont need to check for null before).
However, with the 'Equals' you can also use the overload functions e.g. to ignore the case.

Winforms: Using Activator.CreateInstance to Create System.Drawing.Image from Resource

How can I use, if possible,
Activator.CreateInstance("MyExeName", "Resources.Image1")
to create a background image that will be associated with a button? Image1 is the image defined in the resources named "Image1".
var handle = Activator.CreateInstance("TestApp", "Resources.Image1");
var backgroundImage = handle.Unwrap() as System.Drawing.Image;
I did not see anything like this in stackoverflow. What am I missing?
Thanks for looking and any help is appreciated.
If you can derive the name of the resource you want, then use this:
using System.Resources;
public static Image GetImage(String ImageName)
{
Image retImage = null;
Object o = Properties.Resources.ResourceManager.GetObject(ImageName);
if (o != null && (o is Image))
{
Image img = (Image)((Image)o).Clone(); //necessary to prevent premature disposal
retImage = img;
}
return retImage;
}
Here's the MSDN on ResourceManager, and there are many SO questions relating to its use.

Cannot delete file when used in DataContext

My application shows images on screen (images based upon files on the local computer) and users can delete them if needed.
Every time I try to delete a file it results in the following error message:
"The process cannot access the file 'C:\\Users\\Dave\\Desktop\\Duplicate\\Swim.JPG' because it is being used by another process."
I understand the error message.
I have a UserControl which accepts a file path (via a parameter in the constructor) and then binds it to it's (UserControl) DataContext.
As part of debugging this issue I have found the issue is due to setting the DataContext within the UserControl. If I remove this.DataContext = this; from within my UserControl then I can delete the file.
So, my TestUnit looks like
Ui.UserControls.ImageControl ic = new ImageControl(
#"C:\Users\Dave\Desktop\Duplicate\Swim.JPG");
try
{
File.Delete(#"C:\Users\Dave\Desktop\Duplicate\Swim.JPG");
}
catch (Exception ex)
{
Assert.Fail(ex.Message);
}
The UserControl CodeBehind
public ImageControl(string path)
{
this.FilePath = path;
this.DataContext = this; // removing this line allows me to delete the file!
InitializeComponent();
}
#region Properties
private string _filePath;
public string FilePath
{
get { return _filePath; }
set
{
_filePath = value;
OnPropertyChanged("FilePath");
}
}
If it matters, my UserControl XAML is using the 'Image' control, bound to 'FilePath'
I have tried making the UserControl null before deleting, this did not help.
I have tried adding the IDisposible Interface to my UserControl and within the Dispose() method setting this.DataContext = null; but this did not help.
What am I doing wrong? How can I delete this file (or more accurately, make it unused).
The problem is not the DataContext, but simply the way WPF loads images from files.
When you bind the Source property of an Image control to a string that contains a file path, WPF internally creates a new BitmapFrame object from the path basically like this:
string path = ...
var bitmapImage = BitmapFrame.Create(new Uri(path));
Unfortunately this keeps the Image file opened by WPF, so that you can't delete it.
To get around this you have to change the type of your image property to ImageSource (or a derived type) and load the image manually like shown below.
public ImageSource ImageSource { get; set; } // omitted OnPropertyChanged for brevity
private ImageSource LoadImage(string path)
{
var bitmapImage = new BitmapImage();
using (var stream = new FileStream(path, FileMode.Open))
{
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = stream;
bitmapImage.EndInit();
bitmapImage.Freeze(); // optional
}
return bitmapImage;
}
...
ImageSource = LoadImage(#"C:\Users\Dave\Desktop\Duplicate\Swim.JPG");

Loading Icons from a given path to display in WPF window

I have a tree that displays the directory and another panel that displays the files. Right now the files displayed have no icons. All i know is the path to the file. What i woudl like to do is get that files icon to display in that panel. I need the output to be and Image.source. Currently this is what i have
private ImageSource GetIcon(string filename)
{
System.Drawing.Icon extractedIcon = System.Drawing.Icon.ExtractAssociatedIcon(filename);
ImageSource imgs;
using (System.Drawing.Icon i = System.Drawing.Icon.FromHandle(extractedIcon.ToBitmap().GetHicon()))
{
imgs = Imaging.CreateBitmapSourceFromHIcon(
i.Handle,
new Int32Rect(0, 0, 16, 16),
BitmapSizeOptions.FromEmptyOptions());
}
return imgs;
From there i call my itme and try to change its default icon with:
ImageSource i = GetIcon(f.fullname)
ic.image = i
ic is the given item to the list, f.fullname contains the path
here is the get and set of image
public BitmapImage Image
{
get { return (BitmapImage)img.Source; }
set { img.Source = value; }
}
It doesn't work and this is one of many ways I've tried it says it cant cast the different types. Does anyone have a way to do this?
I'm completely lost.
I'm assuming that img is a standard Image control.
Your Image property is of type BitmapImage, which is a specific kind of ImageSource. CreateBitmapSourceFromHIcon returns an instance of an internal class called InteropBitmap, which cannot be converted to BitmapImage, resulting in an error.
You need to change you property to ImageSource (or BitmapSource, which CreateBitmapSourceFromHIcon returns, and inherits ImageSource), like this:
public ImageSource Image
{
get { return img.Source; }
set { img.Source = value; }
}

Categories