FileAttributes to ImageSource: IValueConverter not working - c#

So, I made a "tiny" file explorer page inside my music player (Universal App) and I need to place an image informing wheter is is Directory or file. But the code isn't working.
This is converter itself: namespace myApp before its own namespace.
namespace Converters
{
public sealed class AttributesToImageConverter : Windows.UI.Xaml.Data.IValueConverter
{
public object Convert ( object value, Type targetType, object parameter, string language )
{
FileAttributes f = (FileAttributes)value;
Windows.UI.Xaml.Media.Imaging.BitmapImage img = new Windows.UI.Xaml.Media.Imaging.BitmapImage ( );
img.DecodePixelWidth = 50;
if ( f == FileAttributes.Directory )
{
img.UriSource = new Uri ( "ms-appx:/Asstes/folder.png", UriKind.Absolute );
}
else
img.UriSource = new Uri ( "ms-appx:/Asstes/file.png", UriKind.Absolute );
return img;
}
public object ConvertBack ( object value, Type targetType, object parameter, string language )
{
throw new NotImplementedException ( );
}
}
}
This is XAML:
<Page
...
xmlns:converter="using:myApp.Converters" >
<Page.Resources>
<converter:AttributesToImageConverter x:Key="AttributesToImageConverter" />
</Page.Resources>
...
<Grid x:Name="LayoutRoot" DataContext="">
...
<ListView x:Name="ContentRoot" ItemsSource="{Binding List}" Height="500" Margin="10,-10,10,15" Background="Transparent" BorderBrush="Transparent" >
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="2,2,2,2">
<Image Width="50" Height="50" Margin="5,0,5,0" Source="{Binding Attributes, Converter={StaticResource AttributesToImageConverter}}" />
<TextBlock Text="{Binding Name}" Foreground="White" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
...
</Grid>
Other Bindings to this context work, binding to Name property in the same IStorageItem works perfectly, this one doesn't. Additionally, using ListView causes the app to shutdown few seconds AFTER displaying the loaded data without any debug information or exception but code -2147483645 (0x80000003). I'd appreciate any help.

Is "Attributes" an actual property for each item within ItemsSource "List" or is it a separate property within your view-model?
Create a storage file with the file path you listed and then leverage the following example:
var imageFile = args.Files[0] as StorageFile;
// Ensure the stream is disposed once the image is loaded
using (IRandomAccessStream fileStream = await imageFile.OpenAsync(Windows.Storage.FileAccessMode.Read))
{
// Set the image source to the selected bitmap
var bitmapImage = new BitmapImage();
await bitmapImage.SetSourceAsync(fileStream);
return bitmapImage;
}

Related

Direct3D Image Brush [duplicate]

It seems like it's quite complicated to load an image in runtime to a WPF window.
Image image;
image = new Uri("Bilder/sas.png", UriKind.Relative);
????.Source = new BitmapImage(image);
I'm trying this code, but I need some help to get it to work. I get some red lines below the code! I also wonder if I need to add some extra code inside the XAML code or is in enough with this:
<Image Height="200" HorizontalAlignment="Left" Margin="12,12,0,0" Name="image1"
Stretch="Fill" VerticalAlignment="Top" Width="350" />
Wonder because I have seen examples with sorces to the images inside the XAML tags.
EDIT:
I'm using this now:
var uri = new Uri("pack://application:,,,/sas.png");
var bitmap = new BitmapImage(uri);
image1.Source = bitmap;
The XAML:
<Grid Width="374">
<Image Height="200" HorizontalAlignment="Left" Margin="12,12,0,0" Name="image1" Stretch="Fill" VerticalAlignment="Top" Width="350" />
<Button Content="Start" Height="23" HorizontalAlignment="Left" Margin="12,226,0,0" Name="btnStart" VerticalAlignment="Top" Width="75" />
<Button Content="Land" Height="23" HorizontalAlignment="Left" Margin="287,226,0,0" Name="btnLand" VerticalAlignment="Top" Width="75" />
<ComboBox Height="23" HorizontalAlignment="Left" Margin="110,226,0,0" Name="cmbChangeRoute" VerticalAlignment="Top" Width="156" />
</Grid>
EDIT 2:
My issue is solved, this code works fine:
BitmapImage image = new BitmapImage(
new Uri("pack://application:,,,/Resources/" + company + ".png"));
image2.Source = image;
In WPF an image is typically loaded from a Stream or an Uri.
BitmapImage supports both and an Uri can even be passed as constructor argument:
var uri = new Uri("http://...");
var bitmap = new BitmapImage(uri);
If the image file is located in a local folder, you would have to use a file:// Uri. You could create such a Uri from a path like this:
var path = Path.Combine(Environment.CurrentDirectory, "Bilder", "sas.png");
var uri = new Uri(path);
If the image file is an assembly resource, the Uri must follow the the Pack Uri scheme:
var uri = new Uri("pack://application:,,,/Bilder/sas.png");
In this case the Visual Studio Build Action for sas.png would have to be Resource.
Once you have created a BitmapImage and also have an Image control like in this XAML
<Image Name="image1" />
you would simply assign the BitmapImage to the Source property of that Image control:
image1.Source = bitmap;
Make sure that your sas.png is marked as Build Action: Content and Copy To Output Directory: Copy Always in its Visual Studio Properties...
I think the C# source code goes like this...
Image image = new Image();
image.Source = (new ImageSourceConverter()).ConvertFromString("pack://application:,,,/Bilder/sas.png") as ImageSource;
and XAML should be
<Image Height="200" HorizontalAlignment="Left" Margin="12,12,0,0"
Name="image1" Stretch="Fill" VerticalAlignment="Top"
Source="../Bilder/sas.png"
Width="350" />
EDIT
Dynamically I think XAML would provide best way to load Images ...
<Image Source="{Binding Converter={StaticResource MyImageSourceConverter}}"
x:Name="MyImage"/>
where image.DataContext is string path.
MyImage.DataContext = "pack://application:,,,/Bilder/sas.png";
public class MyImageSourceConverter : IValueConverter
{
public object Convert(object value_, Type targetType_,
object parameter_, System.Globalization.CultureInfo culture_)
{
return (new ImageSourceConverter()).ConvertFromString (value.ToString());
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Now as you set a different data context, Image would be automatically loaded at runtime.

Bind listview image source to image (Sqlite/ UWP/ C#)

The following code can add images from a database to a listview, however I would like to bind the image property to the pictures in XAML using Image Source rather than use listview1.Items.Add. Can this code be easily modified to do this or do I have to go about this another way. Hopefully this isn't a stupid question to ask and I would appreciate any help.
public async void showImage()
{
var query = GetAll();
foreach (var stuff in query)
{
string FileName;
FileName = stuff.RecipeImage;
var file = await
Windows.Storage.KnownFolders.PicturesLibrary.GetFileAsync(FileName);
var stream = await file.OpenReadAsync();
var bitmapImage = new BitmapImage();
bitmapImage.SetSource(stream);
Image ctrlImage = new Image();
ctrlImage.Source = bitmapImage;
ctrlImage.Height = 50;
ctrlImage.Width = 50;
ctrlImage.Stretch = Stretch.UniformToFill;
listView1.Items.Add(ctrlImage);
}
}
I need to add the image to the item source that is already used for my database which contains:
public class AddRecipe
{
[PrimaryKey,AutoIncrement]
public int ID { get; set; }
public string RecipeName { get; set; }
public string RecipeImage { get; set; }
}
<ListView x:Name="listView" HorizontalAlignment="Left" Height="493" Margin="725,60,0,0" VerticalAlignment="Top" Width="528" IsItemClickEnabled="True" SelectionMode="None" ItemClick="listView_SelectionChanged" FontSize="26.667">
<ListView.ItemTemplate>
<DataTemplate >
<StackPanel Orientation="Vertical" Margin="4">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding RecipeName}" Foreground="Black"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding MealType}" Foreground="Black"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
We can use ListView.ItemTemplate to set the DataTemplate used to display each item and put all the images into a ObservableCollection<BitmapImage> as ListView's ItemsSource. Then in DataTemplate, we can use Bind to set the Image.Source. Following is a simple sample:
In XAML, set DataTemplate with {x:Bind} to show image.
<ListView ItemsSource="{x:Bind ImgList}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="BitmapImage">
<Image Width="50"
Height="50"
Source="{x:Bind }"
Stretch="UniformToFill" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
ImgList is defined in code-behind, it's defined as ObservableCollection<BitmapImage>, so the DataType of DataTemplate is BitmapImage and as I just bind the whole BitmapImage object to Image.Source so here just use Source="{x:Bind }".
The code-behind may like following:
public sealed partial class MainPage : Page
{
public ObservableCollection<BitmapImage> ImgList = new ObservableCollection<BitmapImage>();
public MainPage()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
showImage();
}
public async void showImage()
{
var query = GetAll();
foreach (var stuff in query)
{
string FileName = stuff.RecipeImage;
var file = await Windows.Storage.KnownFolders.PicturesLibrary.GetFileAsync(FileName);
var stream = await file.OpenReadAsync();
var bitmapImage = new BitmapImage();
bitmapImage.SetSource(stream);
ImgList.Add(bitmapImage);
}
}
}
Besides, I noticed that you are getting images form Pictures Library. If these images are stored by your app earlier, then storing them in app data would be better as this makes the binding easier and in Pictures Library, these images could be easily deleted by users.
To store images in app data, we can using ApplicationData.Current.LocalFolder to retrieve app's local data folder. For example, copy selected image into local data folder:
//This method copies selected image into local data folder and returns new file's name.
public async Task<string> CopySelectedImage()
{
FileOpenPicker openPicker = new FileOpenPicker();
openPicker.ViewMode = PickerViewMode.Thumbnail;
openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
openPicker.FileTypeFilter.Clear();
openPicker.FileTypeFilter.Add(".bmp");
openPicker.FileTypeFilter.Add(".jpg");
openPicker.FileTypeFilter.Add(".jpeg");
openPicker.FileTypeFilter.Add(".png");
var file = await openPicker.PickSingleFileAsync();
if (file != null)
{
var localFolder = ApplicationData.Current.LocalFolder;
var newCopy = await file.CopyAsync(localFolder, file.Name, NameCollisionOption.GenerateUniqueName);
return newCopy.Name;
}
else
{
return null;
}
}
Then we can using code like following to retrieve the image and create the BitmapImage:
var path = await CopySelectedImage();
var bitmapImage = new BitmapImage(new Uri($"ms-appdata:///local/{path}"));
Update:
I suppose you have had RecipeImage in your item source, then you can add a Image control into your DataTemplate and bind RecipeImage to it's Source with a ImageConverter like following:
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Margin="4" Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBlock Foreground="Black" Text="{Binding RecipeName}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Foreground="Black" Text="{Binding MealType}" />
</StackPanel>
<Image Width="50"
Height="50"
Source="{Binding RecipeImage,
Converter={StaticResource ImageConverter}}"
Stretch="UniformToFill" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
ImageConverter is used to convert string to BitmapImage as your RecipeImage is defined as string but Image.Source need a BitmapImage. Before using it in Binding, we need set it as a StaticResource firstly:
<Page.Resources>
<local:ImageConverter x:Key="ImageConverter" />
</Page.Resources>
The code of ImageConverter may like:
public class ImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
string FileName = value as string;
var file = Windows.Storage.KnownFolders.PicturesLibrary.GetFileAsync(FileName).AsTask().Result;
var stream = file.OpenReadAsync().AsTask().Result;
var bitmapImage = new BitmapImage();
bitmapImage.SetSource(stream);
return bitmapImage;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
Simple
xaml
<ListView Name="listView1">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Image Source="{Binding}" Height="50" Width="50" Stretch="UniformToFill" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
и в коде добавляем List<BitmapImage>
List<BitmapImage> data_list = new List<BitmapImage>();
foreach (var stuff in query)
{
string FileName;
FileName = stuff.RecipeImage;
var file = await
Windows.Storage.KnownFolders.PicturesLibrary.GetFileAsync(FileName);
BitmapImage bitmapImage;
using (var stream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read))
{
bitmapImage = new BitmapImage();
bitmapImage.SetSource(stream);
}
data_list.Add(bitmapImage);
}
listView1.ItemsSource = data_list;
then we just fill in the data into our ListView

How do I bind a WPF Image element to a PNG on the local hard drive using a relative filepath derived from a DB?

I've got a folder on the local hard drive with several images in it. The image names/paths are stored in a local SQLCE database. In a WPF application I'm trying to bind those images to an Image element (which eventually goes into a listbox). I've got the application to run and compile and the listbox shows up but there is no image where it is supposed to be.
This is the XAML that defines the data template that the listbox uses...
<Window.Resources>
<DataTemplate x:Key="assetLBTemplate">
<StackPanel Orientation="Horizontal">
<Image Height="32" Width="32" Source="{Binding imageFileName}" />
<TextBlock Text="{Binding imageFileName}" />
<TextBlock Text="{Binding assetName}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
The XAML for the listbox...
<ListBox x:Name="lbAssetsLiquid"
ItemsSource="{Binding Tables[0]}"
ItemTemplate="{StaticResource assetLBTemplate}"
BorderThickness="1, 1, 1, 1" Grid.Column="0" Grid.Row="1" />
The code that I run on Window_Loaded:
private void BindLiquidAssetsListBoxData()
{
SqlCeConnection connection;
SqlCeCommand command;
string sql = "SELECT tblLiquidAssets.assetName, tblLiquidAssets.assetQuantity, tblLiquidAssets.assetValueGP, tblLiquidAssets.assetDescription, tblImages.imageFileName FROM tblLiquidAssets INNER JOIN tblImages ON tblLiquidAssets.assetImageIndex=tblImages.imageID;";
string connectionString = "Data Source=sharecalc_db.sdf;Persist Security Info=False;";
DataSet dtSet = new DataSet();
try
{
using (connection = new SqlCeConnection(connectionString))
{
command = new SqlCeCommand(sql, connection);
SqlCeDataAdapter adapter = new SqlCeDataAdapter();
connection.Open();
adapter.SelectCommand = command;
adapter.Fill(dtSet, "tblLiquidAssets");
lbAssetsLiquid.DataContext = dtSet;
connection.Close();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
The result from the SQL Query is...
Again...the program loads with the listbox but no images get loaded.
I get this in the output window, which makes me I think I am missing something important here...
converter failed to convert value 'gold64.png' (type 'String')
When I add the images to the project itself in Solution Explorer it seems to work (the images appear where they are supposed to be)...but it does not work otherwise. Can someone shove me in the right direction?
You need to use custom value converter to convert strings to images if you want to load files from the file system. Image.Source, when a string is passed, expects a file name from resources. You can find implementation of a such converter here: Display an image in WPF without holding the file open.
Thank you Athari, you got me on the right path!
Revised chunk of XAML...
<Window x:Class="pf_sharecalc.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Share Calculator" WindowStyle="ThreeDBorderWindow" Loaded="Window_Loaded"
xmlns:local="clr-namespace:pf_sharecalc">
<Window.Resources>
<local:PathToImageConverter x:Key="PathToIMageConverter"/>
<DataTemplate x:Key="assetLBTemplate">
<StackPanel Orientation="Horizontal">
<Image Height="32" Width="32" Source="{Binding imageFileName, Converter={StaticResource PathToIMageConverter}}" />
<TextBlock Text="{Binding imageFileName}" />
<TextBlock Text="{Binding assetName}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
And I added this code...
public class PathToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string path = value as string;
if (path != null)
{
BitmapImage image = new BitmapImage();
using (FileStream stream = File.OpenRead(path))
{
image.BeginInit();
image.StreamSource = stream;
image.CacheOption = BitmapCacheOption.OnLoad;
image.EndInit(); // load the image from the stream
} // close the stream
return image;
}
else
return null;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I'll have to do some fine tuning to get exactly what I want, but I've passed the road-block.

How to load image to WPF in runtime?

It seems like it's quite complicated to load an image in runtime to a WPF window.
Image image;
image = new Uri("Bilder/sas.png", UriKind.Relative);
????.Source = new BitmapImage(image);
I'm trying this code, but I need some help to get it to work. I get some red lines below the code! I also wonder if I need to add some extra code inside the XAML code or is in enough with this:
<Image Height="200" HorizontalAlignment="Left" Margin="12,12,0,0" Name="image1"
Stretch="Fill" VerticalAlignment="Top" Width="350" />
Wonder because I have seen examples with sorces to the images inside the XAML tags.
EDIT:
I'm using this now:
var uri = new Uri("pack://application:,,,/sas.png");
var bitmap = new BitmapImage(uri);
image1.Source = bitmap;
The XAML:
<Grid Width="374">
<Image Height="200" HorizontalAlignment="Left" Margin="12,12,0,0" Name="image1" Stretch="Fill" VerticalAlignment="Top" Width="350" />
<Button Content="Start" Height="23" HorizontalAlignment="Left" Margin="12,226,0,0" Name="btnStart" VerticalAlignment="Top" Width="75" />
<Button Content="Land" Height="23" HorizontalAlignment="Left" Margin="287,226,0,0" Name="btnLand" VerticalAlignment="Top" Width="75" />
<ComboBox Height="23" HorizontalAlignment="Left" Margin="110,226,0,0" Name="cmbChangeRoute" VerticalAlignment="Top" Width="156" />
</Grid>
EDIT 2:
My issue is solved, this code works fine:
BitmapImage image = new BitmapImage(
new Uri("pack://application:,,,/Resources/" + company + ".png"));
image2.Source = image;
In WPF an image is typically loaded from a Stream or an Uri.
BitmapImage supports both and an Uri can even be passed as constructor argument:
var uri = new Uri("http://...");
var bitmap = new BitmapImage(uri);
If the image file is located in a local folder, you would have to use a file:// Uri. You could create such a Uri from a path like this:
var path = Path.Combine(Environment.CurrentDirectory, "Bilder", "sas.png");
var uri = new Uri(path);
If the image file is an assembly resource, the Uri must follow the the Pack Uri scheme:
var uri = new Uri("pack://application:,,,/Bilder/sas.png");
In this case the Visual Studio Build Action for sas.png would have to be Resource.
Once you have created a BitmapImage and also have an Image control like in this XAML
<Image Name="image1" />
you would simply assign the BitmapImage to the Source property of that Image control:
image1.Source = bitmap;
Make sure that your sas.png is marked as Build Action: Content and Copy To Output Directory: Copy Always in its Visual Studio Properties...
I think the C# source code goes like this...
Image image = new Image();
image.Source = (new ImageSourceConverter()).ConvertFromString("pack://application:,,,/Bilder/sas.png") as ImageSource;
and XAML should be
<Image Height="200" HorizontalAlignment="Left" Margin="12,12,0,0"
Name="image1" Stretch="Fill" VerticalAlignment="Top"
Source="../Bilder/sas.png"
Width="350" />
EDIT
Dynamically I think XAML would provide best way to load Images ...
<Image Source="{Binding Converter={StaticResource MyImageSourceConverter}}"
x:Name="MyImage"/>
where image.DataContext is string path.
MyImage.DataContext = "pack://application:,,,/Bilder/sas.png";
public class MyImageSourceConverter : IValueConverter
{
public object Convert(object value_, Type targetType_,
object parameter_, System.Globalization.CultureInfo culture_)
{
return (new ImageSourceConverter()).ConvertFromString (value.ToString());
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Now as you set a different data context, Image would be automatically loaded at runtime.

Delete an image bound to a control

I am writing an Image Manager WPF application. I have a ListBox with the following ItemsTemplate:
<Grid x:Name="grid" Width="150" Height="150" Background="{x:Null}">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="27.45"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
</Grid.ColumnDefinitions>
<Border Margin="5,5,5,5.745" Grid.RowSpan="2" Background="#FF828282" BorderBrush="{DynamicResource ListBorder}" CornerRadius="5,5,5,5" BorderThickness="1,1,2,2" x:Name="border">
<Grid>
<Viewbox Margin="0,0,0,21.705">
<Image Width="Auto" Height="Auto" x:Name="picture" Source="{Binding Path=FullName}" />
</Viewbox>
<TextBlock Height="Auto" Text="{Binding Path=Name}" TextWrapping="Wrap" x:Name="PictureText" HorizontalAlignment="Left" Margin="70,0,0,0" VerticalAlignment="Bottom" />
</Grid>
</Border>
</Grid>
Note that the "Image" control is bound to the "FullName" property, which is a string representing the absolute path to a JPG.
Several application features require that I alter the JPG file (move, rename, or delete). When I try to do so (currently trying to Move the file) I receive an IOException: "The process cannot access the file because it is being used by another process." The process locking the file is my WPF application.
I did some searching online and found several postings indicating that Images in particular have trouble letting go of their resources. I have tried the following:
Setting the ListBox.Source to null
Adding a 10 second wait time before
attempting the move.
Issuing GC.Collect().
Moving the operation to a different
thread.
What else can I try? I thought about finding a reference to the Image object in the ItemsTemplate and trying to dispose of the Image, but I can't figure out how to get the reference.
One possible solution I read about was to create copies of the Images rather than the actual images, but since the Binding is to the filename and not the actual Image I don't know if I could make this work.
Any help or suggestions would be most appreciated.
My Intuipic application allows users to delete images, too. I had to write this converter to achieve it. Relevant code:
//create new stream and create bitmap frame
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = new FileStream(path, FileMode.Open, FileAccess.Read);
bitmapImage.DecodePixelWidth = (int) _decodePixelWidth;
bitmapImage.DecodePixelHeight = (int) _decodePixelHeight;
//load the image now so we can immediately dispose of the stream
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
//clean up the stream to avoid file access exceptions when attempting to delete images
bitmapImage.StreamSource.Dispose();
I marked Kent's response as an answer, and I would have marked bendewey's as well, because I used both of them in the final solution.
The file was definitely locked because the file name was all that was being bound, so the Image control opened the actual file to produce the image.
To Solve this, I created a Value Converter like bendewey suggested, and then I used (most of) the code form Kent's suggestion to return a new BitmapImage:
[ValueConversion(typeof(string), typeof(BitmapImage))]
public class PathToBitmapImage : IValueConverter
{
public static BitmapImage ConvertToImage(string path)
{
if (!File.Exists(path))
return null;
BitmapImage bitmapImage = null;
try
{
bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = new FileStream(path, FileMode.Open, FileAccess.Read);
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
bitmapImage.StreamSource.Dispose();
}
catch (IOException ioex)
{
}
return bitmapImage;
}
#region IValueConverter Members
public virtual object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null || !(value is string))
return null;
var path = value as string;
return ConvertToImage(path);
}
public virtual object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
As the comments suggest above, however, this did not solve the problem. I have been away on other projects and recently returned to this one reinvigorated to find the solution.
I created another project that only tested this code, and of course it worked. This told me that there was more amiss in the original program.
Long story short, the Image was being generated in three places, which I thought had been addressed:
1) The ImageList, now bound using the Converter.
2) The main Image which was bound to the ImageList SelectedItem property.
3) The DeleteImage popup, which was bound using the Converter.
It turns out the problem was in #2. By binding to the SelectedItem, I mistakenly assumed I was binding to the newly rendered Image (based on the Converter). In reality, the SelectedItem object was in fact the file name. This meant that the main Image was again being built by directly accessing the file.
So the solution was to bind the main Image control to the SelectedItem property AND employ the Converter.
Check out this post here.
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/dee7cb68-aca3-402b-b159-2de933f933f1/
Sample
Basically you'll have to preload the image using a stream. I would create a PreLoadImageConverter, something like this, I haven't tested it.
<Grid>
<Grid.Resources>
<local:PreLoadImageConverter x:Key="imageLoadingConverter" />
</Grid.Resources>
<Image Width="Auto" Height="Auto" x:Name="picture" Source="{Binding Path=FullName, Converter={StaticResource imageLoadingConverter}}" />
</Grid>
PreLoadImageConverter.cs
public class PreLoadImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null) return null;
string imagePath = value.ToString();
ImageSource imageSource;
using (var stream = new MemoryStream())
{
Bitmap bitmap = new Bitmap(imagePath);
bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
PngBitmapDecoder bitmapDecoder = new PngBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
imageSource = bitmapDecoder.Frames[0];
imageSource.Freeze();
}
return imageSource;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new Exception("The method or operation is not implemented.");
}
}
This is very old, but the framework has changed and the resolve for this is much easier, in .NET Core at least.
From what I gather, BitmapImage.UriSource didn't used to be bindable. It is now. Simply specify your image source explicitly in xaml. Bind your UriSource and set cache mode to OnLoad. Done. No converters required.
<Image Grid.Row="1">
<Image.Source>
<!-- specify the source explicitly and set CacheOption to OnLoad to avoid file locking-->
<BitmapImage UriSource="{Binding ImagePath}" CacheOption="OnLoad" />
</Image.Source>
</Image>

Categories

Resources