Cannot delete file used by some other process - wpf

I am displaying some image in my wpf app using following code:
<Image Source="{Binding Path=TemplateImagePath, Mode=TwoWay}" Grid.Row="3" Grid.Column="2" Width="400" Height="200"/>
and setting it's binding property inside code behind's constructor by navigating through some directory, below is the code:
DirectoryInfo Dir = new DirectoryInfo(#"D:/Template");
if (Dir.Exists)
{
if (Dir.GetFiles().Count() > 0)
{
foreach (FileInfo item in Dir.GetFiles())
{
TemplateImagePath = item.FullName;
}
}
}
but if user upload some other image then I need to delete this old image which is I am doing in the following way and setting image binding to null:
DirectoryInfo Dir = new DirectoryInfo(#"D:/Template");
if (Dir.Exists)
{
if (Dir.GetFiles().Count() > 0)
{
foreach (FileInfo item in Dir.GetFiles())
{
TemplateImagePath= null;
File.Delete(item.FullName);
}
}
}
But Iam getting exception that Cannot delete file used by some other process.
How can I delete it?

In order to be able to delete the image while it is displayed in an ImageControl, you have to create a new BitmapImage or BitmapFrame object that has BitmapCacheOption.OnLoad set. The bitmap will then be loaded from file immediately and the file is not locked afterwards.
Change your property from string TemplateImagePath to ImageSource TemplateImage and bind like this:
<Image Source="{Binding TemplateImage}"/>
The set the TemplateImage property like this:
BitmapImage image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = new Uri(item.FullName);
image.EndInit();
TemplateImage = image;
or this:
TemplateImage = BitmapFrame.Create(
new Uri(item.FullName),
BitmapCreateOptions.None,
BitmapCacheOption.OnLoad);
If you want to keep binding to your TemplateImagePath property you may instead use a binding converter that converts the string to an ImageSource as shown above.

According to Clemens suggestion, here is the binding converter to have a good code-reuse:
namespace Controls
{
[ValueConversion(typeof(String), typeof(ImageSource))]
public class StringToImageSourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is string valueString))
{
return null;
}
try
{
ImageSource image = BitmapFrame.Create(new Uri(valueString), BitmapCreateOptions.IgnoreImageCache, BitmapCacheOption.OnLoad);
return image;
}
catch { return null; }
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
And there is a string for binding, for example
public string MyImageString { get; set; } = #"C:\test.jpg"
And in the UI the converter is used, in my case from the Library named "Controls"
<Window x:Class="MainFrame"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Controls;assembly=Controls">
<Window.Resources>
<controls:StringToImageSourceConverter x:Key="StringToImageSourceConverter" />
</Window.Resources>
<Grid>
<Image Source="{Binding MyImageString, Converter={StaticResource StringToImageSourceConverter}}" />
</Grid>
</Window>

Related

WPF Dependency Property for Image source with IValueConverter

I intend to create Usercontrol with boolean dependency property called IsPowerOn, When I change it True the PowerOn image load to Image.source and when I set IsPowerOn to Fals, the PowerOff image load to Image.source.
Here is my UserControl:
<UserControl x:Class="...UcPower"
...
<UserControl.Resources>
<local:PowerBoolean2Image x:Key="PowerBoolean2Image"/>
</UserControl.Resources>
<Grid>
<Image x:Name="imgPower" Source="{Binding Source, Converter={StaticResource PowerBoolean2Image}, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:UcPower}}}" />
</Grid>
And Code behind:
public static readonly DependencyProperty IsPowerOnProperty = DependencyProperty.Register("IsPowerOn", typeof(bool), typeof(UcPower),
new FrameworkPropertyMetadata(false) { BindsTwoWayByDefault = true });
public bool IsPowerOn
{
get
{
return (bool)GetValue(IsPowerOnProperty);
}
set
{
SetValue(IsPowerOnProperty, value);
}
}
And IValueConverter:
public class PowerBoolean2Image : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is bool))
{
return null;
}
if (value.Equals(true))
{
// Power On
return new BitmapImage(new Uri("pack://application:,,,/Resources/Power-On.png"));
}
else
{
// Power Off
return new BitmapImage(new Uri("pack://application:,,,/Resources/Power-Off.png"));
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
But it doesn't work I expect, whats the wrong with me?
You should bind to the IsPowerOn property:
<Image Source="{Binding IsPowerOn, ...}" />
instead of
<Image Source="{Binding Source, ...}" />
Besides that, the expression if (value.Equals(true)) looks rather strange. You could replace that by
if ((bool)value)
{
return new BitmapImage(new Uri("pack://application:,,,/Resources/Power-On.png"));
}
return new BitmapImage(new Uri("pack://application:,,,/Resources/Power-Off.png"));
or shorter:
return (bool)value
? new BitmapImage(new Uri("pack://application:,,,/Resources/Power-On.png"))
: new BitmapImage(new Uri("pack://application:,,,/Resources/Power-Off.png"));
When I use this code in IValueConverter I get Error: IOException: Cannot locate resource 'resources/power-on.png'. and cant see my form in design mode:
Uri("pack://application:,,,/Resources/Power-On.png")
But I can use Assembly name to solve the problem like this code:
Uri("pack://application:,,,/ReferencedAssembly;component/Resources/Power-On.png")

How do I set background image to a Canvas

Wpf Canvas Background image does not display selected image from local path
XAML Code
<Canvas x:Name="LayoutRoot" Margin="485,24,0,0" HorizontalAlignment="Left" Width="341" Height="506" VerticalAlignment="Top">
<Canvas.Background>
<ImageBrush ImageSource="{Binding BGImage}"/>
</Canvas.Background>
</Canvas>
MVVM code
private String _BGImage = #"‪C:/Users/sam/Desktop/photo-5.jpg";
public String BGImage
{
get
{
return this._BGImage;
}
set
{
this._BGImage = value;
NotifyPropertyChanged("BGImage");
}
}
Why this image not display on canvas background
or you can try using a converter
<UserControl.Resources>
<local:StringToImageConverter x:Key="StringToImageConverter" />
</UserControl.Resources>
...
<Canvas x:Name="LayoutRoot" Margin="485,24,0,0" HorizontalAlignment="Left" Width="341" Height="506" VerticalAlignment="Top">
<Canvas.Background>
<ImageBrush ImageSource="{Binding Path=BGImage, Converter={StaticResource StringToImageConverter}}"/>
</Canvas.Background>
</Canvas>
and this is the converter
public class StringToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value.GetType() != typeof(string))
{
throw new InvalidOperationException("The value must be a string");
}
return new BitmapImage(new Uri((string)value));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
of course you would still need to check if the string is a valid URI
Your viewmodel code for BGImage should look something like this:
private ImageSource _BGImage = new BitmapImage(new Uri(#"C:\Users\sam\Desktop\photo-5.jpg", UriKind.Absolute))
public ImageSource BGImage
{
get { return _BGImage; }
set
{
_BGImage= value;
NotifyPropertyChanged("BGImage");
}
}
Well you need to have BGImage as BitmapImage rather than string
public BitmapImage BGImage
{
get
{
return new BitmapImage((new Uri(this._BGImage, UriKind.Absolute)));
}
}
If you are changing image dynamically then you have to raise property changed to notify UI

Calling the Convert method from Converter just once for each item in ItemsControl

I have an ItemsControl control. In its items I show a lot of things: two images in each of the items, some textblocks, etc.
One of the images that are shown in each of the items is the same for all of the items. It's in the Resources.resx file in my project and here's how I load it:
<Image Width="60" Height="60" VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Row="0" Grid.Column="2" Grid.RowSpan="2">
<Image.Source>
<Binding Source="{x:Static properties:Resources.myImageName}" Converter="{StaticResource BitmapToImageConverter}" />
</Image.Source>
</Image>
The Convert method of my BitmapToImageConverter converter class looks like this:
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
using (MemoryStream stream = new MemoryStream())
{
((System.Drawing.Bitmap)value).Save(stream, ImageFormat.Png);
stream.Position = 0;
BitmapImage resultImage = new BitmapImage();
resultImage.BeginInit();
resultImage.CacheOption = BitmapCacheOption.OnLoad;
resultImage.StreamSource = stream;
resultImage.EndInit();
resultImage.Freeze();
return resultImage;
}
}
Now, the binding takes a lot of time and I want to shorten it somehow. The Convert method gets called once for every item in my itemscontrol, but with the same image (the same parameters). How can I call it just once for all of the items?
The Converter extends the IValueConverter class.
If I havent misunderstood your question Try this
public class ImageConverter : ValueConverter
{
static BitmapImage resultImage = null;
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (resultImage != null)
return resultImage;
using (MemoryStream stream = new MemoryStream())
{
((System.Drawing.Bitmap)value).Save(stream, ImageFormat.Png);
stream.Position = 0;
resultImage = new BitmapImage();
resultImage.BeginInit();
resultImage.CacheOption = BitmapCacheOption.OnLoad;
resultImage.StreamSource = stream;
resultImage.EndInit();
resultImage.Freeze();
return resultImage;
}
}
Create a static variable and assign it the value first time and return it for next times

Display image from resources by binding -WPF

I have an icon on resources that it key is: xxx
I want to bind it to an image in xaml..
1:
<Image Source="{x:Static p:Resources.xxx}"></Image>
2:
<Image>
<Image.Source>
<BitmapImage UriSource="{Binding x:Static p:Resources.xxx}"/>
</Image.Source>
</Image>
3:
<Image Source=" {Binding x:Static p:Resources.xxx,Converter={StaticResource IconToBitmap_Converter}}"></Image>
4:
<Image>
<Image.Source>
<BitmapImage UriSource="{Binding x:Static p:Resources.xxx,Converter={StaticResource IconToBitmap_Converter}}"/>
</Image.Source>
</Image>
The above ways does not work, how am I supposed to do that?
First you must add your Image into a Resource File in the Solution Explorer. Next you must set the Build Action of your Image to Resource and then you can use it in the XAML Code like this:
<UserControl>
<UserControl.Resources>
<ResourceDictionary>
<BitmapImage x:Key="name" UriSource="Resources/yourimage.bmp" />
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Image Source="{StaticResource name}"/>
</Grid>
</UserControl>
First:
Add resources rsx
then:
add images as images to the resource file and set the image build action to Resource.
Now you can access the images like this:
<Image Source="pack://application:,,,/Resources/image.png"/>
You may implement your own Markup extension and use it as follows:
<Image Source="{ex:ImageSource {x:Static p:Resources.xxx}}"></Image>
The MarkupExtension ImageSource may look like this:
public class ImageSource : MarkupExtension
{
private readonly object source;
public ImageSource(object source) => this.source = source;
public override object ProvideValue(IServiceProvider serviceProvider)
{
Binding sourceBinding = source is Binding binding ? binding : new Binding() { Source = source };
MultiBinding converterBinding = new MultiBinding() { Converter = new SourceConverter() };
converterBinding.Bindings.Add(sourceBinding);
return converterBinding.ProvideValue(serviceProvider);
}
private class SourceConverter : IMultiValueConverter
{
public object Convert(object[] value, Type targetType, object parameter, CultureInfo culture) => BitmapToSource(value[0] as Bitmap);
public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture) => throw new NotSupportedException();
private BitmapImage BitmapToSource(Bitmap bitmap)
{
using (MemoryStream memory = new MemoryStream())
{
bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Bmp);
memory.Position = 0;
BitmapImage bitmapimage = new BitmapImage();
bitmapimage.BeginInit();
bitmapimage.StreamSource = memory;
bitmapimage.CacheOption = BitmapCacheOption.OnLoad;
bitmapimage.EndInit();
return bitmapimage;
}
}
}
}

using images from resources - image control is empty

I try use images from resources.
I add one png image to resources, name heart.png. It’s public property.
I expose resources with class as advice here:
http://houseofbilz.com/archives/2009/03/15/binding-to-resources-in-silverlightwpf/
Here is class:
namespace Spirit.Util
{
using Properties;
public class PublicResources
{
private readonly static Resources Resources = new Resources();
public Resources SpiritResources { get { return Resources; } }
}
}
I add to app.xaml:
<Util:PublicResources x:Key="SpiritResources"/>
And try use on image control.
<Image Style="{StaticResource InfoIcon}">
<Image.Source>
<!--<MultiBinding Converter="{StaticResource imageToGrayConverter}">-->
<Binding Path="SpiritResources.heart" Source="{StaticResource SpiritResources}"/>
<!--<Binding Path="Oponent.Info.IsFriend" Mode="OneWay" UpdateSourceTrigger="PropertyChanged"/>
</MultiBinding>-->
</Image.Source>
</Image>
Frist problem is that image control is empty, why?
My complete goal is bind image from resources on image control with multibinding and multiconverter.
If property Isfriend (Oponent.Info.IsFriend) is false I want convert image to grayscale.
Another problem is here. I use this converter class on conversion image to grayscale.
public class ImageToGrayConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
//string imageUri = values[0] as BimapImage;
//value is type of System.Drawing.Image
var image = values[0] as BitmapImage; //new BitmapImage(new Uri(imageUri, UriKind.Relative));
string s = values[1].ToString();
bool isLogged = System.Convert.ToBoolean(s);
if (!isLogged)
{
try
{
if (image != null)
{
var grayBitmapSource = new FormatConvertedBitmap();
grayBitmapSource.BeginInit();
grayBitmapSource.Source = image;
grayBitmapSource.DestinationFormat = PixelFormats.Gray32Float;
grayBitmapSource.EndInit();
return grayBitmapSource;
}
return null;
}
catch (Exception ex)
{
throw ex;
}
}
return image;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Value is type of System.Drawing.BitmapImage, I think that I can convert Bitmap to BitmapImage class with this simple class:
http://dog-net.org/content/development/wpf/system-drawing-bitmap-to-bitmapimage/
But I must solve first problem on the beggining. Thank for advice.
As your post is not taggeg as Silverlight, I've solved your problem in WPF application.
I just added existing PNG file to standart Resources. Then put Resources in XAML as static resource and binded PNG file's content to Image element:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:props="clr-namespace:WpfApplication1.Properties">
<Window.Resources>
<self:ImageConverter x:Key="Conv"/>
<props:Resources x:Key="Res"/>
</Window.Resources>
<StackPanel>
<Image Source="{Binding Source={StaticResource Res}, Path=dossier_ardoise_images, Converter={StaticResource Conv}}"/>
</StackPanel>
My converter method looks like this (it's standart IValueConverter, but it doesn't matter):
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is Bitmap)
{
var stream = new MemoryStream();
((Bitmap)value).Save(stream, ImageFormat.Png);
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = stream;
bitmap.EndInit();
return bitmap; // use when you need normal image
var conv = new FormatConvertedBitmap();
conv.BeginInit();
conv.Source = bitmap;
conv.DestinationFormat = PixelFormats.Gray32Float;
conv.EndInit();
return conv; // use when you need grayed image
}
return value;
}
EDITED
In order to get grayscaled bitmap with keeping transparency I would recommend you use next method (from this article):
public static Bitmap MakeGrayscale(Bitmap original)
{
//create a blank bitmap the same size as original
Bitmap newBitmap = new Bitmap(original.Width, original.Height);
//get a graphics object from the new image
Graphics g = Graphics.FromImage(newBitmap);
//create the grayscale ColorMatrix
ColorMatrix colorMatrix = new ColorMatrix(
new float[][]
{
new float[] {.3f, .3f, .3f, 0, 0},
new float[] {.59f, .59f, .59f, 0, 0},
new float[] {.11f, .11f, .11f, 0, 0},
new float[] {0, 0, 0, 1, 0},
new float[] {0, 0, 0, 0, 1}
});
//create some image attributes
ImageAttributes attributes = new ImageAttributes();
//set the color matrix attribute
attributes.SetColorMatrix(colorMatrix);
//draw the original image on the new image
//using the grayscale color matrix
g.DrawImage(original, new Rectangle(0, 0, original.Width, original.Height),
0, 0, original.Width, original.Height, GraphicsUnit.Pixel, attributes);
//dispose the Graphics object
g.Dispose();
return newBitmap;
}
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is Bitmap)
{
var stream = new MemoryStream();
MakeGrayscale((Bitmap)value).Save(stream, ImageFormat.Png);
//((Bitmap)value).Save(stream, ImageFormat.Png);
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = stream;
bitmap.EndInit();
return bitmap;
}
return value;
}

Resources