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.
Related
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}"/>
I'm not quite sure what's happening, so I apologize if the title isn't specific. I've provided the code and xaml below which demonstrates my problem. I have the static methods that I call to convert bitmap to byte [] and vice versa. These methods work fine when used to bind source to image control. However, when I use them to assign a source to images which are children of BlockUIContainer as the code demostrates... I get the same image as the previous on my second call to ByteArrayToBitmapSource.
I'm clueless. What is obvious to me, however, is that the second image has the properties of the image that I expect it display, but is apparently the wrong image.
C# MainWindow.xaml.cs
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.Navigation;
using System.Windows.Shapes;
using System.IO;
using System.Drawing.Imaging;
using System.Drawing;
using System.Windows.Xps.Packaging;
using System.Windows.Xps;
namespace FlowDocumentTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, IDisposable
{
private XpsDocument xpsDocument;
private String randomFileName;
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
FlowDocument doc = new FlowDocument();
doc.Blocks.Add(new Paragraph(new Run("Test")));
Section section1 = new Section();
BlockUIContainer blockUIContainer1 = new BlockUIContainer();
blockUIContainer1.Child = new System.Windows.Controls.Image { Source = Source1 };
Section section2 = new Section();
BlockUIContainer blockUIContainer2 = new BlockUIContainer();
blockUIContainer2.Child = new System.Windows.Controls.Image { Source = Source2 };
doc.Blocks.Add(blockUIContainer1);
doc.Blocks.Add(blockUIContainer2);
randomFileName = System.IO.Path.GetRandomFileName();
this.xpsDocument = new XpsDocument(randomFileName, System.IO.FileAccess.ReadWrite);
XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(xpsDocument);
writer.Write(((IDocumentPaginatorSource)doc).DocumentPaginator);
this.Viewer.Document = xpsDocument.GetFixedDocumentSequence();
}
public BitmapSource Source1
{
get
{
byte[] tmp = BitmapSourceToByteArray(GetBitmapImage(new Bitmap(#"source1.jpg")));
return ByteArrayToBitmapSource(tmp);
}
}
public BitmapSource Source2
{
get
{
byte[] tmp = BitmapSourceToByteArray(GetBitmapImage(new Bitmap(#"source2.bmp")));
return ByteArrayToBitmapSource(tmp);
}
}
public static BitmapImage GetBitmapImage(Bitmap bitmap)
{
BitmapImage bitmapImage = new BitmapImage();
using (MemoryStream stream = new MemoryStream())
{
bitmap.Save(stream, ImageFormat.Png);
stream.Position = 0;
bitmapImage.BeginInit();
bitmapImage.StreamSource = stream;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
}
return bitmapImage;
}
public static byte[] BitmapSourceToByteArray(BitmapSource bitmapSource)
{
byte[] data;
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
using (MemoryStream ms = new MemoryStream())
{
encoder.Save(ms);
data = ms.ToArray();
}
return data;
}
public static BitmapSource ByteArrayToBitmapSource(byte[] data)
{
BitmapSource result;
using (MemoryStream ms = new MemoryStream(data))
{
PngBitmapDecoder decoder = new PngBitmapDecoder(ms, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
result = decoder.Frames[0];
}
return result;
}
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
base.OnClosing(e);
this.Dispose();
}
public void Dispose()
{
this.xpsDocument.Close();
if (System.IO.File.Exists(randomFileName))
System.IO.File.Delete(randomFileName);
}
}
}
XAML MainWindow.xaml
<Window x:Class="FlowDocumentTest.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">
<Grid>
<DocumentViewer Name="Viewer" />
</Grid>
</Window>
I guess it has to do with returning a BitmapSource as oppose to a discreet BitmapImage. Created this method and called this method instead, and it works as I expect.
Still don't know whether this is FlowDocument or XPSDocument related issue.
public static BitmapSource GetBitmapImage(Bitmap bitmap)
{
BitmapImage bitmapImage = new BitmapImage();
using (MemoryStream stream = new MemoryStream())
{
bitmap.Save(stream, ImageFormat.Png);
stream.Position = 0;
bitmapImage.BeginInit();
bitmapImage.StreamSource = stream;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
}
return bitmapImage;
}
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}"/>
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.
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}"/>