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; }
}
Related
This question already has an answer here:
Load a large BitmapImage asynchronously
(1 answer)
Closed 2 years ago.
My WPF MVVM application loads an image from the given URL asynchronously, through Webclient.DownloadFileAsync(url, fileLocation). That process goes fine and smooth, no freezes at all when downloading a picture. But the problem occurs when I present the image file to the user - an application becomes unresponsive.
After file is downloaded, I assign the image file to the BitmapImage:
public async void LoadFileToBitmapImage(string filePath)
{
_isDownloading = false;
await FileToBitmapImage(filePath);
}
public Task FileToBitmapImage(string filePath)
{
return Task.Run(() =>
{
var executableLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var imageLocation = Path.Combine(executableLocation, filePath);
var bi = new BitmapImage();
bi.BeginInit();
bi.UriSource = new Uri(imageLocation);
bi.EndInit();
bi.Freeze();
Image = bi;
});
}
Image.cs:
private BitmapImage _image;
public BitmapImage Image
{
get => _image;
set
{
_image = value;
NotifyOfPropertyChange("Image");
}
}
XAML Image Binding:
<Image Source="{Binding Image, IsAsync=True}" Margin="3"/>
The problem occurs when the image is downloaded and presenting it to the user. The bigger an image, the more time it takes to present an image to the user and the more time an application is unresponsive.
I tried clicking pause at that very time when the application freezes to check threads and get the following info and unfortunately it doesn't provide me with any information.
Any help will be much appreciated!
Edit
Worth noting that application becomes unresponsive after PropertyChanged event is raised, not before. Maybe it's something to do with rendering an image to the UI?
first, if you save the image, change the binding to a string/uri directly, no BitmapImage, no nned to create it, Wpf handle that for you
public BitmapImage Image ===> public Uri Image
and remove FileToBitmapImage.
I spent a few days to find a simple solution to this problem. I needed to display over a hundred images in high quality without UI freezing.
I tried various modifications of the binding and so on in the end only the creation of the Image control through the code and set of the Source property worked before Image appeared in the tree of interface elements.
In XAML only empty ContentControl:
<ContentControl x:Name="ImageContent"/>
In code:
static readonly ConcurrentExclusiveSchedulerPair _pair = new ConcurrentExclusiveSchedulerPair();
// Works for very big images
public void LoadImage(Uri imageUri)
{
var image = new System.Windows.Controls.Image(); // On UI thread
RenderOptions.SetBitmapScalingMode(image, BitmapScalingMode.HighQuality);
Task.Factory.StartNew(() =>
{
var source = new BitmapImage(imageUri); // load ImageSource
Dispatcher.RunOnMainThread(() =>
{
image.Source = source; // Set source while not in UI
// Add image in UI tree
ImageContent.Content = image; // ImageContent is empty ContentControl
ImageContent.InvalidateVisual();
});
}, default, TaskCreationOptions.LongRunning, _pair.ExclusiveScheduler);
}
Works better with loading image with CacheOption OnLoad.
public static ImageSource BitmapFromUri(Uri source)
{
if (source == null)
return new BitmapImage(source);
using (var fs = new FileStream(source.LocalPath, FileMode.Open))
{
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = fs;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.EndInit();
bitmap.Freeze();
return bitmap;
}
}
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").
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");
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.
}
I'm programming a little application that shows thumbnails of images. All the displayed images are in the same directory, each Image is inside it's own groupbox with a few labels and a checkbox. All the group boxes get added to a flowlayoutpanel. The problem is, that the amount of images may get pretty large and I'm concerned that memory usage / performance might get a little out of hand if I load all images even if they're not yet visible.
Is there a way to load only images that are currently visible to the user? My first thought is storing the location of my boxes and determine which images to load depending on the scroll position, or is there an easier way to determine if a picturebox/groupbox is currently visible?
Ideally what you should be doing is creating buffer logic rather than hiding 1 image and showing the other. It's a much better idea to have a couple buffers loading the images before you show them and have a fixed number of actual fields showing images rather than a new set per image.
But if your solution requires that, try to create a custom user control.
Try something like this:
public class customUserControl : UserControl
{
//Store image as a Uri rather than an Image
private Uri StoredImagePath;
public class PictureBoxAdv : PictureBox
{
public PictureBoxAdv()
{
this.VisibleChanged +=new EventHandler(VisibleChanged);
}
}
public Uri Image
{
get { return StoredImagePath; }
set
{
StoredImagePath = value;
if (this.Visible && StoredImagePath != null)
{
this.Image = Image.FromFile(StoredImagePath.AbsolutePath);
}
}
}
public void VisibleChanged(object sender, EventArgs e)
{
//When becomes visible, restore image, invisible, nullify.
if (this.Visible && StoredImagePath != null)
{
this.Image = Image.FromFile(StoredImagePath.AbsolutePath);
}
else
{
this.Image = null;
}
}
}