I need some help to figure out why two different ways of binding an Image in XAML don't behave the same way.
Here is my code:
public class Picture
{
public int ID
{
get;
set;
}
public string ThumbURL
{
get
{
return String.Format("http://"+ App.ServerAdress +"/Pictures/thumbs/{0}.jpg", ID);
}
}
public int ThumbLocal
{
get { return ID; }
}
}
public class ByteImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
using (var store =
IsolatedStorageFile.GetUserStoreForApplication())
{
if (!store.FileExists(value)) return null;
var stream =
store.OpenFile(path, FileMode.Open);
try
{
var image = new BitmapImage();
image.SetSource(stream);
return image;
}
finally
{
stream.Dispose();
}
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
In case I bind the image this way, it is working:
<Image Width="110" CacheMode="BitmapCache" Source="{Binding ThumbURL}"
Margin="12,0,9,0"/>
But this way does not:
<Image Width="110" CacheMode="BitmapCache" Source="{Binding ThumbLocal, Mode=TwoWay, Converter={StaticResource imgConverter}}"
Margin="12,0,9,0"/>
This situation happens when I try to set picture from one panorama item to the other. I my case I want to add picture from one list to another and when I use first type of binding everything is working fine. Also it is working when I navigate to other page and navigate back, the picture shows up. I'm a little confused by this behavior.
Any ideas how I can solve this problem?
Finally I realized why this happened. Before Binding I have to download picture from the web and save it to the Isolated Storage, all this process is async. And in time when I Bind the picture there in no picture yet. I have try to Bind existing picture from Isolated Storage and it is correctly displayed. So, now I need something like INotifyPropertyChanged. Is any way I can do it if I use IValueConverter?
You can make it so that if the image is not in isolated storage - you start downloading the image, but return a new BitmapImage immediately and only when downloading is complete - save the image to isostore and set it as the BitmapImage's source.
Something like:
if (!store.FileExists(value))
{
var image = new BitmapImage();
ImageDownloadAndCachingHelper.DownloadImage(path, (s, e) => image.SetSource(e.ImageStream));
return image;
}
It is up to you to implement the code that converts the local path into a web URI, download it and invoke the EventHandler, where ImageDownloadedEventArgs has the ImageStream.
Related
I have a image in window
<Image Source="{Binding Path=MYImage, Converter={StaticResource ResourceKey=imageConverter}}" />
I have also tried using a value converter:
public class ImageConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
try
{
return new BitmapImage(new Uri((string)value));
}
catch
{
return new BitmapImage();
}
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
and created a dependency property for it.
public string MYImage
{
get { return (string)GetValue(MYImageProperty); }
set { SetValue(MYImageProperty, value); }
}
public static readonly DependencyProperty MYImageProperty=DependencyProperty.Register("PickerImage",typeof(string),typeof(MYClass),new PropertyMetadata("/MYProject;component/pic.png"));
but when i use it, don't show image !!!
you don't have to convert the source.
you can Bind a string like that:
"/My.Namespace;component/Resources/thatsMyImage.png"
xaml:
<Image Source="{Binding Path=MYImage}" />
This is how I resolved the issue in my application.
My Application has many solutions and each solution has many projects.
I am running .NET 4.5 on VS 2012 using WPF.
My application structure is:
MyApplication
CoreSolution
-ProjectA1
-Resources
-Images
warning.ico (Build action set to Resource)
information.ico (Build action set to Resource)
error.ico (Build action set to Resource)
+ProjectA2
PersonDatabaseSolution
+ProjectB1
+ProjectB2
I have added images (actual images and not links) in CoreSolution's ProjectA1 project. I did not change the build action of any of the images. Compiled the project to get ProjectA1.dll.
In ProjectB2's PersonDatabaseSolution, I refer to error.ico in code behind using the following:
private ImageSource _myImage
public ImageSource MyImage
{
get
{
if(_myImage==null)
{
uriLoc=new Uri("pack://application:,,,/CoreSolution.ProjectA1;component/Resources/Images/error.ico", UriKInd.Absolute);
BitmapImage bmImage = new BitmapImage();
bmImage.BeginInit();
bmImage.UriSource = uriLoc;
bmImage.EndInit();
_myImage=bmImage;
}
return _myImage;
}
}
The MyImage property is bound in the xaml:
<Image Source="{Binding Path=MyImage}"/>
So far this has worked for me. Hope it helps others as well.
Thanks,
RDV
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>
I've got a WPF application that needs to send pre-generated EMF files to a specified printer/tray.
I don't want to show the PrintDialog; the actual printer/tray is configured before hand. I also don't need to actually view the EMF file either; but rather just send it to the printer.
So far, all my R&D into this has led to is a bunch of posts that are 5 years old dealing with EMF and WPF and how it isn't supported.
Has anybody had any luck with this before? Can someone point me in the right direction?
Turns out this was easier than I thought. You can do this via a Image control, and use of a converter. This example takes the file location of the emf file, and puts it into a WPF usercontrol that I then send to a printer.
In XAML:
<Grid Margin="12">
<Image Source="{Binding Path=FileName, Converter={StaticResource emfImageConverter}, Mode=OneWay}"></Image>
</Grid>
and your converter class:
[ValueConversion(typeof(string), typeof(BitmapImage))]
public class EmfImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var fileName = (string)value;
if (fileName == null || !File.Exists(fileName))
return new BitmapImage();
using (var stream = File.Open(fileName, FileMode.Open))
{
return GetImage(stream);
}
}
internal BitmapImage GetImage(Stream fileStream)
{
var img = Image.FromStream(fileStream);
var imgBrush = new BitmapImage { CacheOption = BitmapCacheOption.OnLoad, CreateOptions = BitmapCreateOptions.PreservePixelFormat };
imgBrush.BeginInit();
imgBrush.StreamSource = ConvertImageToMemoryStream(img);
imgBrush.EndInit();
return imgBrush;
}
public MemoryStream ConvertImageToMemoryStream(Image img)
{
var ms = new MemoryStream();
img.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
return ms;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I need to find the way to save images to the IsolatedStorage and show them I the Silverlight (XAML)
Important: Silverlight have to take image “himself”, I cannot set the image from the code behind
I have tried many solutions before.
The very last solution is, to bind byte array and convert them to the Image
XAML
StackPanel Orientation="Horizontal" Margin="0,0,0,20">
<Image Width="110" CacheMode="BitmapCache" Source="{Binding ThumbLocal,Converter={StaticResource imgConverter}}"
Margin="12,0,9,0"/>
<StackPanel Width="311">
Code behind
public byte[] ThumbLocal
{
get;
set;
}
public class ByteImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
MemoryStream memStream = new MemoryStream((byte[])value);
memStream.Seek(0, SeekOrigin.Begin);
BitmapImage thumbLocal = new BitmapImage();
thumbLocal.SetSource(memStream);
return thumbLocal;
}
}
Everything work till I save byte[] to the database and tried to retrieve.
By now I can see the only option save image as file to the IsolatedStorage and then retrieve and covert to byte[].
Is this “smart ” solution?
First, create this converter:
public class BinaryToImageSourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null && value is byte[])
{
var bytes = value as byte[];
var stream = new MemoryStream(bytes);
var image = new BitmapImage();
image.SetSource(stream);
stream.Close();
return image;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Second, bind to Your byte[] using this converter, i.e. if you are using MVVM:
View:
<Image Source="{Binding IsolatedStorageImage, Converter={StaticResource BinaryToImageSourceConverter}}" x:Name="ScanImage"/>
You can make property in contrlol (prop snippet) type byte[] and read image to byte array from isostorage, then set value of property to it.
If You got more questions, feel free to ask me.
I am trying to display filenames in a listbox, retrieved from a particular directory. They are stored in an ObservableCollection of FileInfo objects:
public ObservableCollection<FileInfo> ProjectFiles
{
get
{
if (SelectedFolder == null) return null;
DirectoryInfo d= new DirectoryInfo(SelectedFolder);
if (!d.Exists) return null;
return new ObservableCollection<FileInfo>(d.EnumerateFiles("*.xsi"));
}
}
I have implemented a filter on the listbox, called when text is entered or changed in a textbox "FilesFilterBy":
private void FilterFiles_TextChanged(object sender, TextChangedEventArgs e)
{
ICollectionView view = CollectionViewSource.GetDefaultView(ProjectFiles);
view.Filter = new Predicate<object>(IsTextInFilename);
}
public bool IsTextInFilename(object item)
{
string Filename = Path.GetFileNameWithoutExtension((item as FileInfo).Name);
return (Filename.ToLower().Contains(FilesFilterBy.Text.ToLower()));
}
At the same time, I want to display only the names of the files, without path or extension. To this end I have implemented a converter:
public class RemoveExtensionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return Path.GetFileNameWithoutExtension(value as string);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return new NotImplementedException();
}
}
Here is how the listbox is implemented in XAML:
<Window.Resources>
<ctr:RemoveExtensionConverter x:Key="JustFileName" />
</Window.Resources>
<ListBox ItemsSource="{Binding ProjectFiles}" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding FullName, Converter={StaticResource JustFileName}}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Currently the converter works - only the file names are listed, but the filter no longer has any effect. When I enter text in the FileFilterBy textbox the TextChanged event is fired but the listbox stays the same. Also, the converter is not called at that point.
What am I doing wrong?
ProjectFiles returns a new collection every time. Your FilterFiles_TextChanged handler is calling ProjectFiles to create a new collection, setting a filter on that new collection, and then throwing it away. The collection bound to the ListBox is not affected. You need to change ProjectFiles to keep the same collection object. Maybe something like this:
private ObservableCollection<FileInfo> _projectFiles;
public ObservableCollection<FileInfo> ProjectFiles
{
get
{
if (_projectFiles == null)
{
if (SelectedFolder == null) return null;
DirectoryInfo d = new DirectoryInfo(SelectedFolder);
if (!d.Exists) return null;
_projectFiles = new ObservableCollection<FileInfo>(
d.EnumerateFiles("*.xsi"));
}
return _projectFiles;
}
}
The Converter shouldn't affect the filter at all.