How to release Image from Image Source in WPF - c#

Am loading image like below
XAML
<Image Stretch="None" Grid.Row="16" Height="70" HorizontalAlignment="Left" Name="imgThumbnail" VerticalAlignment="Top" Width="70" Grid.RowSpan="3" Margin="133,1,0,0" Grid.Column="2" Grid.ColumnSpan="2" />
CodeBehind
if (Path.GetFileNameWithoutExtension(filePath).ToLower().Contains(slugName.ToLower() + "_70x70"))
{
imgThumbnail.BeginInit();
imgThumbnail.Stretch = Stretch.UniformToFill;
imgThumbnail.Source = new BitmapImage(new Uri(filePath));
imgThumbnail.EndInit();
count = count + 1;
}
Above code work fine , now I have a delete button next to my thumbnail, if delete button called I suppose to delete all the images from the source location.
Here is the code to delete the image files
internal int Remove(string slugName, DirectoryInfo outputFolder)
{
Helper.MetadataView.imgThumbnail.Source = null;
foreach (string filePath_ToBeDeleted in filePathList_ToBeDeleted)
{
if (File.Exists(filePath_ToBeDeleted))
{
Helper.MetadataView.imgThumbnail.IsEnabled = false;
File.Delete(filePath_ToBeDeleted);
count += 1;
}
}
return count;
}
return 0; // slugName == null
}
I tried to source to be null and delete, but it throws exception like below
The process cannot access the file '\serv1\Dev\Images\730_Test4_0406_70x70.jpg' because it is being used by another process.
Am not sure how to dispose, please someone guide me.

You should not use that Image directly in your application if you want to delete or move it.
imgThumbnail.Source = new BitmapImage(new Uri(filePath));
Instead, do this:
BitmapImage image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = new Uri(filePath);
image.EndInit();
imgThumbnail.Source = image;
For more read this

To have a good code-reuse a binding converter could be used:
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

Binding array of objects using ItemsControl

I need to bind array of object Images to WrapPanel.
I have declared objects in main class constructor:
public MainWindow()
{
InitializeComponent();
private Masina[] _masina = new Masina[12];
DataContext = new
{
data1 = _masina
};
}
My Class Masina haves few variables inside it, but I want to bind just Image:
public class Masina
{
public Image masina_pav = new Image();
public bool r_mas;
public string s_mas;
public Masina()
{
byte[] buffer = File.ReadAllBytes("teksturos/masinos/red/top.png");
MemoryStream memoryStream = new MemoryStream(buffer);
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.DecodePixelWidth = 100;
bitmap.DecodePixelHeight = 200;
bitmap.StreamSource = memoryStream;
bitmap.EndInit();
bitmap.Freeze();
masina_pav.Source = bitmap;
Canvas.SetLeft(masina_pav, 100);
Canvas.SetTop(masina_pav, 200);
}
}
I have tried this XAML code:
<WrapPanel Name="zem" Height="1000" Width="1108" >
<ItemsControl ItemsSource="{Binding data1}" DisplayMemberPath="masina_pav">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Name="masinu_sarasas" HorizontalAlignment="Center" IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</WrapPanel >
For now program starts but don't show me any Image (should be 12 of them). Can someone help me to figure it out ?
Image is a view class that should not be used in view models. Instead, your class should provide a public property of type ImageSource. Note that it's a property, not a field as you had declared it. This is necessary because WPF data binding only works with public properties.
public class Masina
{
public ImageSource MasinaPav { get; private set; }
...
public Masina()
{
using (var fileStream = new FileStream(
"teksturos/masinos/red/top.png",
FileMode.Open, FileAccess.Read, FileShare.Read))
{
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.DecodePixelWidth = 100;
bitmap.DecodePixelHeight = 200;
bitmap.StreamSource = fileStream;
bitmap.EndInit();
bitmap.Freeze();
MasinaPav = bitmap;
}
}
}
Now your ItemsControl would have an ItemTemplate with an Image control that is bound to the view model property:
<ItemsControl ItemsSource="{Binding data1}">
...
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding MasinaPav}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Besides that, you should be careful with setting a BitmapImage's DecodePixelWidth and DecodePixelHeight at the same time, because it may spoil the bitmap's aspect ratio.

Displaying image from a class attribute in a button

i need to display an image which is from a class attribute inside a button, the binding doesn't work.
This is how i proceed:
i have a class Products that contains ProductImage of type system.drawable.image and some other strings.
XAML code :
<Button Content="{Binding ProductImage}" Name="ImageButton"></Button>
the button is showing with Content : System.Drawing.Bitmap in it.
any help Please.
EDIT : i found important to add the Window2.XAML.cs file
MySqlConnection connection = new MySqlConnection(ConnectionManager.ConnectionString);
try
{
connection.Open();
MySqlCommand command = new MySqlCommand("SELECT * FROM products", connection);
MySqlDataReader reader = command.ExecuteReader();
var list = new List<ProductsBLL>();
while (reader.Read())
{
ProductsBLL product = new ProductsBLL();
product.ProductId = (int) reader[0];
product.ProductName = (string) reader[1];
product.ProductReference = (string) reader[2];
product.ProductColor = (string) reader[3];
var imagems = (byte[]) reader[4];
MemoryStream ms = new MemoryStream(imagems);
product.ProductImage = System.Drawing.Image.FromStream(ms);
product.ProductDescription = (string) reader[5];
product.ProductUnitPrice = (decimal) reader[6];
product.ProductUnitInStock = (int) reader[7];
product.ProductUnitInCommand = (int) reader[8];
list.Add(product);
product = null;
}
ProductsListView.ItemsSource = list;
}
catch (Exception e)
{
MessageBox.Show(this, e.Message);
}
finally
{
if(connection.State == ConnectionState.Open)
connection.Close();
}
The type of your ProductImage property is currently System.Drawing.Image, which is WinForms, not WPF. A System.Drawing.Image can not be drawn directly by a WPF Image control.
You should change the property type to System.Windows.Media.ImageSource:
public ImageSource ProductImage { get; set; }
Now you would create an instance of either System.Windows.Media.Imaging.BitmapImage or
System.Windows.Media.Imaging.BitmapFrame as value of the property. Note that the BitmapCacheOption.OnLoad must be set, because you want to dispose of the stream right after loading the image.
byte[] imageBuffer = ...
using (var memoryStream = new MemoryStream(imageBuffer))
{
product.ProductImage = BitmapFrame.Create(
memoryStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
or
using (var memoryStream = new MemoryStream(imageBuffer))
{
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = memoryStream;
bitmapImage.EndInit();
product.ProductImage = bitmapImage;
}
Finally (as already said in the other answer) you would have to use an Image control as the Button's Content:
<Button Name="ImageButton">
<Image Source="{Binding ProductImage}" />
</Button>
Try to use Image control as place holder for Bitmap :
<Button Name="ImageButton">
<Image Source="{Binding ProductImage}" />
</Button>

Check if ImageSource is empty

I'm using Binding to Image.SourceProperty from byte[] variable. At IValueConverter I check if value.Length > 0, if yes then I set source from it's value. Then I need to know, if it was set, so I can show or hide clear button. Image.Source always is not null. How can know, if it was set from byte[] array?
My code:
var bnd = new Binding
{
Mode = BindingMode.TwoWay,
Path = new PropertyPath("DataPath.Value"),
Converter = new ByteToImageConverter()
};
myImage.SetBinding(Image.SourceProperty, bnd);
public class ByteToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var val = value as byte[];
var bmp = new BitmapImage();
if (val.Length > 0) {
bmp.SetSource(new MemoryStream(val));
}
return bmp;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null) {
return new byte[0];
}
var ms = new MemoryStream();
var bmp = new WriteableBitmap(value as BitmapSource);
bmp.SaveJpeg(ms, 150, 200, 0, 100);
return ms.ToArray();
}
}
Now I need code to check, if image source property is set:
// myImage.Source always != null even if there was no bmp.SetSource() call
var str = myImage.Source != null ? "Image is set" : "Image is empty";
something like that
if (((BitmapImage)myImage.Source).UriSource == null)
{
//Image is empty
}
or
var str = ((BitmapImage)myImage.Source).UriSource != null ? "Image is set" : "Image is empty";

Add images to ListBox (c#, windows phone 7)

I am developing the Windows Phone 7, I need to add images to ListBox.
I want do it with C# code, not XAML.
I read about it, but everyone uses BitmapImage, which I can't get to work on Windows Phone 7.
I have XAML code:
<StackPanel Margin="0,0,0,17">
<local:MatchDates Adress="http://soccernet.espn.go.com/results/_/league/esp.1/spanish-la-liga?cc=57393" />
</StackPanel>
and C# function in my class MatchDates:
void add_items(List<List<string>> code)
{
if (code.Count == 0)
this.Items.Add("no mathes");
else
{
foreach (List<string> temp1 in code)
{
foreach (string temp2 in temp1)
{
this.Items.Add(temp2);
}
this.Items.Add("---------------------------------");
}
}
}
How can I add images in function add_items?
This code:
Uri uri = new Uri("/eng.png", UriKind.Relative);
BitmapImage bitmap = new BitmapImage(uri);
Image img = new Image();
img.Height = 30;
img.Width = 30;
img.Source = bitmap;
this.Items.Add(img);
this.Items.Add(temp2);
Only empty space is presented, How do I add images to ListBox?
One of the ways I used:
XAML:
<ListBox Name="lstView" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ImagePath}"></Image>
<TextBlock Text="{Binding Name}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
C#:
public class Article
{
public string Name { get; set; }
public string ImagePath { get; set; }
}
Article article1 = new Article() { Name = "name1", ImagePath = "path of image 1" };
Article article2 = new Article() { Name = "name2", ImagePath = "path of image 2" };
var articles = new List<Article>();
articles.Add(article1);
articles.Add(article2);
lstView.DataContext = articles;
For retrieving articles I use WCF service.
BitmapImage is supported. You have to declare the BitmapImage as ImageSource.

LinkButton doesn't work Silverlight

I have a method that I call to create a link button. However the problem is that it passing the field name that the actual field to link button becasically giving me a dead link.
This is the method:
private static DataGridTemplateColumn CreateHyperlink(string fieldName)
{
DataGridTemplateColumn column = new DataGridTemplateColumn();
column.Header = "";
string link = #"http://www.amazon.com/gp/product/" + fieldName;
StringBuilder sb = new StringBuilder();
sb.Append("<DataTemplate ");
sb.Append("xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' ");
sb.Append("xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' ");
sb.Append("xmlns:src='clr-namespace:Silverlight1.Classes;assembly=Silverlight1.Classes'>");
sb.Append("<HyperlinkButton ");
sb.Append("TargetName= '_blank' ");
sb.Append("Content = 'Link' ");
sb.Append("NavigateUri =" +"'"+ link +"'");
sb.Append(" />");
sb.Append("</DataTemplate>");
column.CellTemplate = (DataTemplate)XamlReader.Load(sb.ToString());
column.IsReadOnly = false;
return column;
}
Which is called by this
dgOrder.Columns.Add(CreateHyperlink("asin"));
Which is getting pulled from a WCF Silverlight enable data service.
How do I pass the content instead of field name?
You don't pass the content, you pass the field name but create a Binding. You will also need an implementation of a IValueConverter.
Lets start with an IValueConverter, you need something which take the string value of the "field" (by which I'm assuming you mean a Property of the objects bound to rows in the grid) and prefix it with #"http://www.amazon.com/gp/product/" to form a complete URL.
public class UrlPrefixConverter : IValueConverter
{
public string Prefix {get; set;}
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null)
{
return new Uri(Prefix + value.ToString(), UriKind.Absolute);
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Place an instance of this in the App.xaml:-
<Application.Resources>
<local:UrlPrefixConverter Prefix="http://www.amazon.com/gp/product/" x:Key="AmazonPrefixConverter" />
</Application.Resources>
Now your create method can look like this:-
private static DataGridTemplateColumn CreateHyperlink(string fieldName)
{
DataGridTemplateColumn column = new DataGridTemplateColumn();
column.Header = "";
string template = #"<DataTemplate
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" >
<HyperlinkButton TargetName=""_blank"" Content=""Link""
NavigateUri=""{{Binding {0}, Converter={{StaticResource AmazonPrefixConverter}}}}"" />
</DataTemplate>";
column.CellTemplate = (DataTemplate)XamlReader.Load(String.Format(template, fieldName));
column.IsReadOnly = false;
return column;
}

Categories