Sharing violation during image save in c# [duplicate] - c#

I am working on a program that downloads images from a URL to a bitmapimageand displays it. Next I try to save the bitmapimage to the harddrive using jpegbitmapencoder. The file is successfully created but the actual jpeg image is empty or 1 black pixel.
public Guid SavePhoto(string istrImagePath)
{
ImagePath = istrImagePath;
BitmapImage objImage = new BitmapImage(
new Uri(istrImagePath, UriKind.RelativeOrAbsolute));
PictureDisplayed.Source = objImage;
savedCreationObject = objImage;
Guid photoID = System.Guid.NewGuid();
string photolocation = photoID.ToString() + ".jpg"; //file name
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(objImage));
using (FileStream filestream = new FileStream(photolocation, FileMode.Create))
{
encoder.Save(filestream);
}
return photoID;
}
This is the function that saves and displays the photo. The photo is displayed correctly but again when it is saved I get an empty jpeg or 1 black pixel.

When you create your BitmapImage from a Uri, time is required to download the image.
If you check the following property, the value will likely be TRUE
objImage.IsDownloading
As such, you much attach a listener to the DownloadCompleted event handler and move all processing to that EventHandler.
objImage.DownloadCompleted += objImage_DownloadCompleted;
Where that handler will look something like:
private void objImage_DownloadCompleted(object sender, EventArgs e)
{
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
Guid photoID = System.Guid.NewGuid();
String photolocation = photoID.ToString() + ".jpg"; //file name
encoder.Frames.Add(BitmapFrame.Create((BitmapImage)sender));
using (var filestream = new FileStream(photolocation, FileMode.Create))
encoder.Save(filestream);
}
You will likely also want to add another EventHandler for DownloadFailed in order to gracefully handle any error cases.
Edit
Added full sample class based on Ben's comment:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
SavePhoto("http://www.google.ca/intl/en_com/images/srpr/logo1w.png");
}
public void SavePhoto(string istrImagePath)
{
BitmapImage objImage = new BitmapImage(new Uri(istrImagePath, UriKind.RelativeOrAbsolute));
objImage.DownloadCompleted += objImage_DownloadCompleted;
}
private void objImage_DownloadCompleted(object sender, EventArgs e)
{
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
Guid photoID = System.Guid.NewGuid();
String photolocation = photoID.ToString() + ".jpg"; //file name
encoder.Frames.Add(BitmapFrame.Create((BitmapImage)sender));
using (var filestream = new FileStream(photolocation, FileMode.Create))
encoder.Save(filestream);
}
}

Expanding on Chris Baxter's solution, this Converter uses the local version if it exists, otherwise downloads it and saves the file.
using System;
using System.Globalization;
using System.IO;
using System.Windows.Data;
using System.Windows.Media.Imaging;
namespace MyNamespace
{
public sealed class UriToCachedImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var url = value as string;
if (url == null)
return null;
var webUri = new Uri(url, UriKind.Absolute);
var filename = Path.GetFileName(webUri.AbsolutePath);
var localFilePath = Path.Combine("C:\\MyImagesFolder\\", filename);
if (File.Exists(localFilePath))
{
return BitmapFrame.Create(new Uri(localFilePath, UriKind.Absolute));
}
var image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = webUri;
image.EndInit();
SaveImage(image, localFilePath);
return image;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public void SaveImage(BitmapImage image, string localFilePath)
{
image.DownloadCompleted += (sender, args) =>
{
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create((BitmapImage) sender));
using (var filestream = new FileStream(localFilePath, FileMode.Create))
{
encoder.Save(filestream);
}
};
}
}
}
And make sure you can access the converter within your xaml
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:u="clr-namespace:MyNamespace"
d:DesignHeight="500"
d:DesignWidth="420">
<UserControl.Resources>
<ResourceDictionary>
<u:UriToCachedImageConverter x:Key="UrlToCachedImageConverter" />
</ResourceDictionary>
</UserControl.Resources>
</UserControl>
And use the converter on an image
<Image Source="{Binding URL, Mode=OneWay, Converter={StaticResource UrlToCachedImageConverter}, IsAsync=true}"/>

Related

Wpf Image Control blocks the file

I have a simple Window with button and second Window is opened when I click on the Button. Second Window has a Image control, which displays a .png-file. So if I use FileObject property for Binding all is OK, I can delete file from File Explorer. But if I use FileName property for Binding I cannot delete file from File Explorer, I get OS exception. I cannot do this even if I close second window, even if I invoke GC explicitly.
What is the problem with FileName property? Any ideas?
Win 7, Net 4.0
Window1
<Grid>
<Button Content="Ok"
Width="100"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Click="Click"
Padding="0,2,0,2"
IsDefault="True"
Name="_btnOk"/>
</Grid>
public partial class Window : Window
{
public Window()
{
InitializeComponent();
DataContext = this;
}
private void Click(Object sender, RoutedEventArgs e)
{
var window = new Window3();
window.ShowDialog();
}
}
Window2
<Grid>
<Image Source="{Binding FileObject}"></Image>
</Grid>
public partial class Window2 : Window
{
public Window2()
{
InitializeComponent();
DataContext = this;
FileName = "D:/pdf/myfile.png";
Closing += Window2_Closing;
}
public String FileName { get; set; }
public Object FileObject
{
get
{
if (String.IsNullOrEmpty(FileName))
return null;
if (!File.Exists(FileName))
return null;
var ms = new MemoryStream();
var bi = new BitmapImage();
using (var fs = new FileStream(FileName, FileMode.Open, FileAccess.Read))
{
fs.CopyTo(ms);
bi.BeginInit();
bi.StreamSource = ms;
bi.EndInit();
}
return bi;
}
}
void Window2_Closing(Object sender, System.ComponentModel.CancelEventArgs e)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
When you bind the Image.Source property to an Uri (or a string, from which an Uri is created internally), WPF uses a built-in type converter that creates a BitmapFrame from the Uri.
If the Uri contains a path to a local file, the BitmapFrame keeps the file open, as long as it is existing. This may be longer than it is actually used in your application, because it may by cached by WPF.
When you need to be able to delete the file that an image was loaded from, you should always use your FileObject approach, but it should look like this:
public ImageSource Image
{
get
{
...
var bi = new BitmapImage();
using (var fs = new FileStream(FileName, FileMode.Open, FileAccess.Read))
{
bi.BeginInit();
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.StreamSource = fs;
bi.EndInit();
}
return bi;
}
}
Or like this:
public ImageSource Image
{
get
{
using (var fs = new FileStream(FileName, FileMode.Open, FileAccess.Read))
{
return BitmapFrame.Create(
fs, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
}
}
Or you bind to the FileName property with a binding converter that creates a BitmapImage or BitmapFrame as shown above.
use this code, I will explain later what is the problem.
var image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
image.UriSource = new Uri(FilePath);
image.EndInit();
return image;
You can solve this problem directly from xaml.
<Image>
<Image.Source>
<BitmapImage
CacheOption="OnLoad"
UriSource="{Binding FileName}" />
</Image.Source>
</Image>

How to Store and Capture Camera Images [duplicate]

I am working on a program that downloads images from a URL to a bitmapimageand displays it. Next I try to save the bitmapimage to the harddrive using jpegbitmapencoder. The file is successfully created but the actual jpeg image is empty or 1 black pixel.
public Guid SavePhoto(string istrImagePath)
{
ImagePath = istrImagePath;
BitmapImage objImage = new BitmapImage(
new Uri(istrImagePath, UriKind.RelativeOrAbsolute));
PictureDisplayed.Source = objImage;
savedCreationObject = objImage;
Guid photoID = System.Guid.NewGuid();
string photolocation = photoID.ToString() + ".jpg"; //file name
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(objImage));
using (FileStream filestream = new FileStream(photolocation, FileMode.Create))
{
encoder.Save(filestream);
}
return photoID;
}
This is the function that saves and displays the photo. The photo is displayed correctly but again when it is saved I get an empty jpeg or 1 black pixel.
When you create your BitmapImage from a Uri, time is required to download the image.
If you check the following property, the value will likely be TRUE
objImage.IsDownloading
As such, you much attach a listener to the DownloadCompleted event handler and move all processing to that EventHandler.
objImage.DownloadCompleted += objImage_DownloadCompleted;
Where that handler will look something like:
private void objImage_DownloadCompleted(object sender, EventArgs e)
{
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
Guid photoID = System.Guid.NewGuid();
String photolocation = photoID.ToString() + ".jpg"; //file name
encoder.Frames.Add(BitmapFrame.Create((BitmapImage)sender));
using (var filestream = new FileStream(photolocation, FileMode.Create))
encoder.Save(filestream);
}
You will likely also want to add another EventHandler for DownloadFailed in order to gracefully handle any error cases.
Edit
Added full sample class based on Ben's comment:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
SavePhoto("http://www.google.ca/intl/en_com/images/srpr/logo1w.png");
}
public void SavePhoto(string istrImagePath)
{
BitmapImage objImage = new BitmapImage(new Uri(istrImagePath, UriKind.RelativeOrAbsolute));
objImage.DownloadCompleted += objImage_DownloadCompleted;
}
private void objImage_DownloadCompleted(object sender, EventArgs e)
{
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
Guid photoID = System.Guid.NewGuid();
String photolocation = photoID.ToString() + ".jpg"; //file name
encoder.Frames.Add(BitmapFrame.Create((BitmapImage)sender));
using (var filestream = new FileStream(photolocation, FileMode.Create))
encoder.Save(filestream);
}
}
Expanding on Chris Baxter's solution, this Converter uses the local version if it exists, otherwise downloads it and saves the file.
using System;
using System.Globalization;
using System.IO;
using System.Windows.Data;
using System.Windows.Media.Imaging;
namespace MyNamespace
{
public sealed class UriToCachedImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var url = value as string;
if (url == null)
return null;
var webUri = new Uri(url, UriKind.Absolute);
var filename = Path.GetFileName(webUri.AbsolutePath);
var localFilePath = Path.Combine("C:\\MyImagesFolder\\", filename);
if (File.Exists(localFilePath))
{
return BitmapFrame.Create(new Uri(localFilePath, UriKind.Absolute));
}
var image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = webUri;
image.EndInit();
SaveImage(image, localFilePath);
return image;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public void SaveImage(BitmapImage image, string localFilePath)
{
image.DownloadCompleted += (sender, args) =>
{
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create((BitmapImage) sender));
using (var filestream = new FileStream(localFilePath, FileMode.Create))
{
encoder.Save(filestream);
}
};
}
}
}
And make sure you can access the converter within your xaml
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:u="clr-namespace:MyNamespace"
d:DesignHeight="500"
d:DesignWidth="420">
<UserControl.Resources>
<ResourceDictionary>
<u:UriToCachedImageConverter x:Key="UrlToCachedImageConverter" />
</ResourceDictionary>
</UserControl.Resources>
</UserControl>
And use the converter on an image
<Image Source="{Binding URL, Mode=OneWay, Converter={StaticResource UrlToCachedImageConverter}, IsAsync=true}"/>

Silverlight images not displaying

I am creating application in Silverlight. There is only a DataGrid right now. I am using WCF RIA Service for getting datas from database. Everything works fine expect Images. I am getting images as byte array and trying to convert them to BitmapImage using Converter. I want to show images in RowDetailsTemplate of DataGrid.
Here it is:
<sdk:DataGrid.RowDetailsTemplate>
<DataTemplate>
<StackPanel>
<StackPanel>
<Image x:Name="thumbNail" Width="60" Height="60" VerticalAlignment="Center" ImageFailed="thumbNail_ImageFailed"
Source="{Binding gameImage,Converter={StaticResource byteToImageConverter}}">
</Image>
</StackPanel>
</StackPanel>
</DataTemplate>
</sdk:DataGrid.RowDetailsTemplate>
And converter:
public class ByteToImageConverter : IValueConverter
{
public BitmapImage ConvertByteArrayToBitMapImage(byte[] imageByteArray)
{
BitmapImage img = new BitmapImage();
using (MemoryStream memStream = new MemoryStream(imageByteArray))
{
img.SetSource(memStream);
}
return img;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
BitmapImage img = new BitmapImage();
if (value != null)
{
img = this.ConvertByteArrayToBitMapImage(value as byte[]);
}
return img;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
But images are not displaying.
The method in my wcf service is like that:
public List<Games> GamesList()
{
string gmConn = ConfigurationManager.ConnectionStrings["GamesConnectionString"].ConnectionString;
var gamesList = new List<Games>();
using (SqlConnection conn = new SqlConnection(gmConn))
{
string sql = #"Select Name, Developer,Longevity,Distributor,Year,State,Type,graphics, gameplay,sound,general,photo From Games join scores on Games.G_ID=Scores.G_ID";
conn.Open();
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
SqlDataReader dr = cmd.ExecuteReader();
if (dr != null)
while (dr.Read())
{
Games game = new Games
{
Name = dr.GetString(0),
developer = dr.GetString(1),
Longevity = dr.GetInt32(2),
Distributor = dr.GetString(3),
Year = dr.GetString(4),
State = dr.GetString(5),
Type = dr.GetString(6),
Graphics = dr.GetDouble(7),
Gameplay = dr.GetDouble(8),
Sound = dr.GetDouble(9),
General = dr.GetDouble(10)
};
if (!dr.IsDBNull(11))
{
byte[] blob = new byte[(dr.GetBytes(11, 0, null, 0, int.MaxValue))];
dr.GetBytes(11, 0, blob, 0, blob.Length);
using (MemoryStream ms = new MemoryStream())
{
ms.Write(blob, 0, blob.Length - 0);
Bitmap bm = (Bitmap)Image.FromStream(ms);
using (MemoryStream msJpg = new MemoryStream())
{
bm.Save(msJpg, ImageFormat.Jpeg);
game.gameImage = msJpg.GetBuffer();
}
}
}
gamesList.Add(game);
}
return gamesList;
}
}
}
And in mainPage.xaml.cs I am setting ItemsSource of my datagrid.
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
SampleServiceClient client = new SampleServiceClient();
client.GamesListCompleted += new EventHandler<GamesListCompletedEventArgs>(client_GamesListCompleted);
client.GamesListAsync();
}
void client_GamesListCompleted(object sender, GamesListCompletedEventArgs e)
{
CustomerGrid.ItemsSource = e.Result;
}
}
Thanks in advance.
I have created a simple test project with your code and I could not recreate your problem. It might be the case that the byte array is corrupted? (Just a guess..)
Any way, you can find and download the test project here: https://skydrive.live.com/redir?resid=DEB5DDFF6A505390!278 I have made a simple project with an embedded image. I opened that image and converted it into a byte array. Then I used binding to display the image.
MainPage.xaml.cs
using System;
using System.ComponentModel;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Resources;
namespace SilverlightTest
{
public partial class MainPage : UserControl, INotifyPropertyChanged
{
public MainPage()
{
InitializeComponent();
using (var ms = new MemoryStream())
{
StreamResourceInfo sr = Application.GetResourceStream(new Uri("SilverlightTest;component/Koala.jpg", UriKind.Relative));
sr.Stream.CopyTo(ms);
_photo = ms.ToArray();
}
this.DataContext = this;
}
private byte[] _photo;
public byte[] Photo
{
get
{
return _photo;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string prop)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
}
}
Mainpage.xaml
<UserControl x:Class="SilverlightTest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SilverlightTest"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<local:ByteToImageConverter x:Name="byteToImageConverter" />
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<Image Width="320" Height="240" VerticalAlignment="Center" Source="{Binding Path=Photo, Converter={StaticResource byteToImageConverter}}" />
</Grid>
</UserControl>
And the image was included in the project as a resource.
UPDATE
I saw you used the following code in your project:
byte[] blob = new byte[(dr.GetBytes(11, 0, null, 0, int.MaxValue))];
dr.GetBytes(11, 0, blob, 0, blob.Length);
using (MemoryStream ms = new MemoryStream())
{
ms.Write(blob, 0, blob.Length - 0);
Bitmap bm = (Bitmap)Image.FromStream(ms);
using (MemoryStream msJpg = new MemoryStream())
{
bm.Save(msJpg, ImageFormat.Jpeg);
game.gameImage = msJpg.GetBuffer();
}
}
This code won't work. You use GetBuffer() this code returns (according to msdn):
The byte array from which this stream was created, or the underlying
array if a byte array was not provided to the MemoryStream constructor
during construction of the current instance.
To get to right array use the ToArray() function of the MemoryStream class:
byte[] blob = new byte[(dr.GetBytes(11, 0, null, 0, int.MaxValue))];
dr.GetBytes(11, 0, blob, 0, blob.Length);
using (MemoryStream ms = new MemoryStream())
{
ms.Write(blob, 0, blob.Length - 0);
Bitmap bm = (Bitmap)Image.FromStream(ms);
using (MemoryStream msJpg = new MemoryStream())
{
bm.Save(msJpg, ImageFormat.Jpeg);
game.gameImage = msJpg.ToArray();
}
}
And with a little refactoring:
byte[] blob = new byte[(dr.GetBytes(11, 0, null, 0, int.MaxValue))];
dr.GetBytes(11, 0, blob, 0, blob.Length);
using (MemoryStream ms = new MemoryStream(blob))
{
Bitmap bm = (Bitmap)Image.FromStream(ms);
using (MemoryStream msJpg = new MemoryStream())
{
bm.Save(msJpg, ImageFormat.Jpeg);
game.gameImage = msJpg.ToArray();
}
}
UPDATE 2
I wrote a little program to test the file you gave me (renamed the file btw):
SilverlightTestV2.zip
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
namespace ByteTransformer
{
class Program
{
static void Main(string[] args)
{
using (var fs = File.OpenRead("byteArray.dat"))
{
var bm = Image.FromStream(fs);
using (MemoryStream msJpg = new MemoryStream())
{
bm.Save(msJpg, ImageFormat.Jpeg);
using (var ts = File.Create("out.jpg"))
{
var img = msJpg.ToArray();
ts.Write(img, 0, img.Length);
}
}
}
}
}
}
This program throws an ArgumentException on the line: var bm = Image.FromStream(fs);
Hence I think that or the file you gave me doesn't contain the actual byte array from the sql (aka you saved it wrong) or the byte array in the sql is corrupt. But since you can actual see the image I think you saved it wrong.
Update:
It should be enough to assign the blob directly to your gameImage-Property in your method GamesList. The other code should be useless.
byte[] blob = new byte[(dr.GetBytes(11, 0, null, 0, int.MaxValue))];
dr.GetBytes(11, 0, blob, 0, blob.Length);
game.gameImage = blob;
Also when you call GetBuffer on the MemoryStream you will get the array that is passed in the ctor of the MemoryStream. In your case you will get an empty array or null. I´m not sure.
Initial:
Change the ConvertByteArrayToBitMapImage method to the following
public BitmapImage ConvertByteArrayToBitMapImage(byte[] imageByteArray) {
BitmapImage img = new BitmapImage();
MemoryStream memStream = new MemoryStream(imageByteArray);
img.SetSource(memStream);
return img;
}
When you dispose the MemoryStream using the using-Statement, the stream gets set to closed. So can´t the BitmapImage read from the stream that is set by using SetSource.

How to bind an Image control to Image that save in Database in VarBinary type

I want to bind an image that save in varbinary type in database in XAML.How I can do that?
for example Picture field in northwind DataBase.
thanks
EDIT 1:)
I write this codes for convert Image field (Picture field in Categories table in Northwind DataBase) but every time I get Exception:
class ImageConverter : IValueConverter
{
object IValueConverter.Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null) { return null; }
var image = (System.Drawing.Image)value;
var bitmap = new System.Windows.Media.Imaging.BitmapImage();
bitmap.BeginInit();
MemoryStream memoryStream = new MemoryStream();
image.Save(memoryStream, ImageFormat.Bmp);
memoryStream.Seek(0, System.IO.SeekOrigin.Begin);
bitmap.StreamSource = memoryStream;
bitmap.EndInit();
return bitmap;
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new Exception("The method or operation is not implemented.");
}
}
And :
class ImageConverter : IValueConverter
{
object IValueConverter.Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null && value is byte[])
{
byte[] bytes = value as byte[];
MemoryStream stream = new MemoryStream(bytes);
BitmapImage image = new BitmapImage();
image.BeginInit();
image.StreamSource = stream;
image.EndInit();
return image;
}
return null;
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new Exception("The method or operation is not implemented.");
}
}
and the exception:
Provide value on 'System.Windows.Markup.StaticResourceHolder' threw an exception.
If you are getting byte array from database then no need to convert that to image or bitmap image...
You can bind Source property of image to byte array.. wpf internally handle the byte array and it converts byte array to image...
Edit:
If you still want to convert byte array to Bitmap image here is the method which was tested
public BitmapImage ImageFromBytearray(byte[] imageData)
{
if (imageData == null)
return null;
MemoryStream strm = new MemoryStream();
strm.Write(imageData, 0, imageData.Length);
strm.Position = 0;
System.Drawing.Image img = System.Drawing.Image.FromStream(strm);
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
MemoryStream memoryStream = new MemoryStream();
img.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Bmp);
memoryStream.Seek(0, SeekOrigin.Begin);
bitmapImage.StreamSource = memoryStream;
bitmapImage.EndInit();
return bitmapImage;
}
I have crated a sample using above method...
Xaml code:
<Window x:Class="WpfApplication1.ImageFromByteArray"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ImageFromByteArray" Height="300" Width="300" Name="Root">
<Grid>
<Image Source="{Binding ImageSource,ElementName=Root}" Height="300" Width="300" RenderOptions.BitmapScalingMode="HighQuality"/>
</Grid>
</Window>
Code Behind
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.IO;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for ImageFromByteArray.xaml
/// </summary>
public partial class ImageFromByteArray : Window
{
public byte[] ByteArray
{
get
{
return (byte[])GetValue(ByteArrayProperty);
}
set
{
SetValue(ByteArrayProperty, value);
}
}
// Using a DependencyProperty as the backing store for ByteArray. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ByteArrayProperty =
DependencyProperty.Register("ByteArray", typeof(byte[]), typeof(ImageFromByteArray));
public BitmapImage ImageSource
{
get { return (BitmapImage)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
// Using a DependencyProperty as the backing store for ImageSource. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource", typeof(BitmapImage), typeof(ImageFromByteArray), new UIPropertyMetadata(null));
public ImageFromByteArray()
{
InitializeComponent();
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
if (dlg.ShowDialog().GetValueOrDefault())
{
FileStream fs = new FileStream(dlg.FileName, FileMode.Open, FileAccess.Read);
ByteArray = new byte[fs.Length];
fs.Read(ByteArray, 0, System.Convert.ToInt32(fs.Length));
fs.Close();
ImageSource = ImageFromBytearray(ByteArray);
}
}
public BitmapImage ImageFromBytearray(byte[] imageData)
{
if (imageData == null)
return null;
MemoryStream strm = new MemoryStream();
strm.Write(imageData, 0, imageData.Length);
strm.Position = 0;
System.Drawing.Image img = System.Drawing.Image.FromStream(strm);
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
MemoryStream memoryStream = new MemoryStream();
img.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Bmp);
memoryStream.Seek(0, SeekOrigin.Begin);
bitmapImage.StreamSource = memoryStream;
bitmapImage.EndInit();
return bitmapImage;
}
}
}
Hope this will help you...
This can be achieved only using a custom converter. Look at this for details on how to implement the "image" part and here for details about creating a converter.

Save BitmapImage to File

I am working on a program that downloads images from a URL to a bitmapimageand displays it. Next I try to save the bitmapimage to the harddrive using jpegbitmapencoder. The file is successfully created but the actual jpeg image is empty or 1 black pixel.
public Guid SavePhoto(string istrImagePath)
{
ImagePath = istrImagePath;
BitmapImage objImage = new BitmapImage(
new Uri(istrImagePath, UriKind.RelativeOrAbsolute));
PictureDisplayed.Source = objImage;
savedCreationObject = objImage;
Guid photoID = System.Guid.NewGuid();
string photolocation = photoID.ToString() + ".jpg"; //file name
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(objImage));
using (FileStream filestream = new FileStream(photolocation, FileMode.Create))
{
encoder.Save(filestream);
}
return photoID;
}
This is the function that saves and displays the photo. The photo is displayed correctly but again when it is saved I get an empty jpeg or 1 black pixel.
When you create your BitmapImage from a Uri, time is required to download the image.
If you check the following property, the value will likely be TRUE
objImage.IsDownloading
As such, you much attach a listener to the DownloadCompleted event handler and move all processing to that EventHandler.
objImage.DownloadCompleted += objImage_DownloadCompleted;
Where that handler will look something like:
private void objImage_DownloadCompleted(object sender, EventArgs e)
{
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
Guid photoID = System.Guid.NewGuid();
String photolocation = photoID.ToString() + ".jpg"; //file name
encoder.Frames.Add(BitmapFrame.Create((BitmapImage)sender));
using (var filestream = new FileStream(photolocation, FileMode.Create))
encoder.Save(filestream);
}
You will likely also want to add another EventHandler for DownloadFailed in order to gracefully handle any error cases.
Edit
Added full sample class based on Ben's comment:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
SavePhoto("http://www.google.ca/intl/en_com/images/srpr/logo1w.png");
}
public void SavePhoto(string istrImagePath)
{
BitmapImage objImage = new BitmapImage(new Uri(istrImagePath, UriKind.RelativeOrAbsolute));
objImage.DownloadCompleted += objImage_DownloadCompleted;
}
private void objImage_DownloadCompleted(object sender, EventArgs e)
{
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
Guid photoID = System.Guid.NewGuid();
String photolocation = photoID.ToString() + ".jpg"; //file name
encoder.Frames.Add(BitmapFrame.Create((BitmapImage)sender));
using (var filestream = new FileStream(photolocation, FileMode.Create))
encoder.Save(filestream);
}
}
Expanding on Chris Baxter's solution, this Converter uses the local version if it exists, otherwise downloads it and saves the file.
using System;
using System.Globalization;
using System.IO;
using System.Windows.Data;
using System.Windows.Media.Imaging;
namespace MyNamespace
{
public sealed class UriToCachedImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var url = value as string;
if (url == null)
return null;
var webUri = new Uri(url, UriKind.Absolute);
var filename = Path.GetFileName(webUri.AbsolutePath);
var localFilePath = Path.Combine("C:\\MyImagesFolder\\", filename);
if (File.Exists(localFilePath))
{
return BitmapFrame.Create(new Uri(localFilePath, UriKind.Absolute));
}
var image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = webUri;
image.EndInit();
SaveImage(image, localFilePath);
return image;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public void SaveImage(BitmapImage image, string localFilePath)
{
image.DownloadCompleted += (sender, args) =>
{
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create((BitmapImage) sender));
using (var filestream = new FileStream(localFilePath, FileMode.Create))
{
encoder.Save(filestream);
}
};
}
}
}
And make sure you can access the converter within your xaml
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:u="clr-namespace:MyNamespace"
d:DesignHeight="500"
d:DesignWidth="420">
<UserControl.Resources>
<ResourceDictionary>
<u:UriToCachedImageConverter x:Key="UrlToCachedImageConverter" />
</ResourceDictionary>
</UserControl.Resources>
</UserControl>
And use the converter on an image
<Image Source="{Binding URL, Mode=OneWay, Converter={StaticResource UrlToCachedImageConverter}, IsAsync=true}"/>

Categories

Resources