I have an application that takes photos, converts them into byte arrays and saves them locally in the Isolated Storage. It then reads them out and converts them back into an BitmapImage.
However, I can't seem to show the images in a ListBox. I am using the same code I have in another page that works perfectly.
The BitmapImage has a image in it, but that is as much as I know. Whether that image is legitimate or not, I don't know or know how to check.
Any ideas would be greatly appreciated.
SEE CODE BELOW
Convert Image
public byte[] ImageToBytes(BitmapImage img)
{
using (MemoryStream ms = new MemoryStream())
{
WriteableBitmap btmMap = new WriteableBitmap(img.PixelWidth, img.PixelHeight);
// write an image into the stream
Extensions.SaveJpeg(btmMap, ms, img.PixelWidth, img.PixelHeight, 0, 100);
return ms.ToArray();
}
}
public BitmapImage BytesToImage(byte[] bytes)
{
BitmapImage bitmapImage = new BitmapImage();
MemoryStream ms = new MemoryStream(bytes);
bitmapImage.SetSource(ms);
return bitmapImage;
}
Class with image
public class NewItem
{
ObservableCollection<BitmapImage> images = new ObservableCollection<BitmapImage>();
[DataMember]
public ObservableCollection<BitmapImage> Images
{
get { return images; }
set { images = value; }
}
[DataMember]
public string Notes { get; set; }
[DataMember]
public string ItemID { get; set; }
}
Saving to storage
public static void AddOrUpdateUnsavedItems(NewItem _item)
{
var store = IsolatedStorageFile.GetUserStoreForApplication();
List<NewItem> allunsaveditems = new List<NewItem>();
if (store.FileExists("unsaveditem"))
{
allunsaveditems.Add(_item);
allunsaveditems.AddRange(LoadUnsavedItemsFromIsolatedStorage());
store.DeleteFile("unsaveditem");
}
UnsavedRegisters.Clear();
foreach (NewItem ni in allunsaveditems)
{
StoredItem newUnsavedItem = new StoredItem();
newUnsavedItem.ItemID = ni.ItemID;
newUnsavedItem.Notes = ni.Notes;
foreach (BitmapImage bmp in ni.Images)
{
newUnsavedItem.ImageBytes.Add(newUnsavedItem.ImageToBytes(bmp));
}
UnsavedRegisters.Add(newUnsavedItem);
}
using (var stream = new IsolatedStorageFileStream("unsaveditem", FileMode.OpenOrCreate, FileAccess.Write, store))
{
DataContractSerializer dcs = new DataContractSerializer(typeof(List<StoredItem>));
dcs.WriteObject(stream, UnsavedRegisters);
}
}
Loading from storage
public static List<NewItem> LoadUnsavedItemsFromIsolatedStorage()
{
List<NewItem> unsavedItems = new List<NewItem>();
try
{
var store = IsolatedStorageFile.GetUserStoreForApplication();
if (store.FileExists("unsaveditem"))
{
using (var stream = new IsolatedStorageFileStream("unsaveditem", FileMode.OpenOrCreate, FileAccess.Read, store))
{
if (stream.Length > 0)
{
DataContractSerializer dcs = new DataContractSerializer(typeof(List<StoredItem>));
List<StoredItem> storedItems = dcs.ReadObject(stream) as List<StoredItem>;
foreach (StoredItem si in storedItems)
{
NewItem ni = new NewItem();
ni.ItemID = si.ItemID;
ni.Notes = si.Notes;
foreach (byte[] imageBytes in si.ImageBytes)
{
ni.Images.Add(si.BytesToImage(imageBytes));
}
unsavedItems.Add(ni);
}
}
}
}
}
catch (Exception)
{
//MessageBox.Show("and error happened getting the unsaved items");
// handle exception
return null;
}
return unsavedItems;
}
This should not be a problem, I had this working for a BitmapSource, Which I believe BitmapImage inherits from, try the code below in your listbox
<Border Height="200" Width="200">
<Border.Background>
<ImageBrush ImageSource="{Binding ItemImage}" />
</Border.Background>
</Border>
ItemImage is the Property holding your BitmapSource image.
I don't know what has changed, but it started working.
Related
For a WPF project I need to read a BitmapImage from a Zip file. The original fileformat is .png I know how to do this directly from a file system and that is working fine. From a Zip file, unfortunately the image is not shown, though an image seems to be read.
I created a simple test project:
public partial class MainWindow : Window
{
public BitmapImage RouteImage { get; set; }
public MainWindow()
{
InitializeComponent();
LoadBitmap();
DataContext = RouteImage;
}
public void LoadBitmap()
{
RouteImage = new BitmapImage();
var PackedFile = #"D:\Temp\MainContent.ap";
try
{
{
using (ZipArchive archive = ZipFile.OpenRead(PackedFile))
{
var file = archive.GetEntry("RouteInformation/image.png");
if (file != null)
{
using (var zipEntryStream = file.Open())
{
RouteImage.BeginInit();
RouteImage.CacheOption = BitmapCacheOption.OnLoad;
RouteImage.StreamSource = zipEntryStream;
RouteImage.EndInit();
return;
}
}
}
}
}
catch (Exception e)
{
var s = "Exception: " + e.Message;
}
}
}
}
The XAML code looks like this:
<Image Height="128" Width="256" Source="{Binding BitmapImage}"/>
In the debugger it looks like the stream is created and bound to the BitmapImage, but the width and height are set to 1. so I think something is wrong withe the reading of the data in the zip file.
Not sure about the exact reason, but it seems necessary to copy the zip stream to an intermediate MemoryStream and read the BitmapImage from there.
You should also write a view model class with property change notification:
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private BitmapImage routeImage;
public BitmapImage RouteImage
{
get { return routeImage; }
set
{
routeImage = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(RouteImage)));
}
}
public void LoadImage(string archiveName, string entryName)
{
using (var archive = ZipFile.OpenRead(archiveName))
{
var entry = archive.GetEntry(entryName);
if (entry != null)
{
using (var zipStream = entry.Open())
using (var memoryStream = new MemoryStream())
{
zipStream.CopyTo(memoryStream); // here
memoryStream.Position = 0;
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = memoryStream;
bitmap.EndInit();
RouteImage = bitmap;
}
}
}
}
}
Assign an instance of the view model to the DataContext of your Window:
public MainWindow()
{
InitializeComponent();
var viewModel = new ViewModel();
DataContext = viewModel;
viewModel.LoadImage(
#"D:\Games\steamapps\common\RailWorks\Content\Routes\00000036-0000-0000-0000-000000002012\MainContent.ap",
"RouteInformation/image.png");
}
and bind the RouteImage property like this:
<Image Source="{Binding RouteImage}"/>
If you are intending to load large image file from the zip archive, I'd recommend to call the view model code in an async method:
public async Task LoadImageAsync(string archiveName, string entryName)
{
RouteImage = await Task.Run(() => LoadImage(archiveName, entryName));
}
private BitmapImage LoadImage(string archiveName, string entryName)
{
BitmapImage bitmap = null;
using (var archive = ZipFile.OpenRead(archiveName))
{
var entry = archive.GetEntry(entryName);
if (entry != null)
{
using (var zipStream = entry.Open())
using (var memoryStream = new MemoryStream())
{
zipStream.CopyTo(memoryStream);
memoryStream.Position = 0;
bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = memoryStream;
bitmap.EndInit();
bitmap.Freeze(); // necessary when loaded in non-UI thread
}
}
}
return bitmap;
}
Call the method from an async Loaded event handler in your MainWindow:
Loaded += async (s, e) => await viewModel.LoadImageAsync
#"D:\Games\steamapps\common\RailWorks\Content\Routes\00000036-0000-0000-0000-000000002012\MainContent.ap",
"RouteInformation/image.png");
In my work to remove an image from a zip container, that using a middle thread as per Clemens answer only created an exception and was not tenable.
Ultimately I was able to use this code to extract a BitmapImage:
public static class StreamExtensions
{
public static BitmapImage ToImage(this Stream stream)
{
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = stream;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.EndInit();
return bitmap;
}
}
Usage
var image = zipEntry.Open().ToImage();
I'm using a BinaryFormatter, I serialize a treeview .
Now I want to do the same thing for an ImageList
I used this code to serialize :
public static void SerializeImageList(ImageList imglist)
{
FileStream fs = new FileStream("imagelist.iml", FileMode.Create);
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(fs, imglist.ImageStream);
fs.Close();
}
and this one to deserialize :
public static void DeSerializeImageList(ref ImageList imgList)
{
FileStream fs = new FileStream("imagelist.iml", FileMode.Open);
BinaryFormatter bf = new BinaryFormatter();
imgList.ImageStream = (ImageListStreamer)bf.Deserialize(fs);
fs.Close();
}
but I get an empty string in all keys !!
ImgList.Images.Keys
why ?
Most humans do not use their full resources to cross reference what they already know. ImageList is not serializable in its current form, but there are only two things you really want to save in it and that is the Key and the Image. So you build an intermediate class to hold them, that is serializable, as in the following example:
[Serializable()]
public class FlatImage
{
public Image _image { get; set; }
public string _key { get; set; }
}
void Serialize()
{
string path = Options.GetLocalPath("ImageList.bin");
BinaryFormatter formatter = new BinaryFormatter();
List<FlatImage> fis = new List<FlatImage>();
for (int index = 0; index < _smallImageList.Images.Count; index++)
{
FlatImage fi = new FlatImage();
fi._key = _smallImageList.Images.Keys[index];
fi._image = _smallImageList.Images[index];
fis.Add(fi);
}
using (FileStream stream = File.OpenWrite(path))
{
formatter.Serialize(stream, fis);
}
}
void Deserialize()
{
string path = Options.GetLocalPath("ImageList.bin");
BinaryFormatter formatter = new BinaryFormatter();
try
{
using (FileStream stream = File.OpenRead(path))
{
List<FlatImage> ilc = formatter.Deserialize(stream) as List<FlatImage>;
for( int index = 0; index < ilc.Count; index++ )
{
Image i = ilc[index]._image;
string key = ilc[index]._key;
_smallImageList.Images.Add(key as string, i);
}
}
}
catch { }
}
I am trying to convert a base64string to Bitmapimage.
The below code is used in my windows phone project and it works fine, however I am reusing this code in my windows store app project and getting this error. I have no clue in fixing this error.
Error Msg:
The best overloaded method match for 'Windows.UI.Xaml.Media.Imaging.BitmapSource.SetSource(Windows.Storage.Streams.IRandomAccessStream)' has some invalid arguments
cannot convert from 'System.IO.MemoryStream' to 'Windows.Storage.Streams.IRandomAccessStream'
DATADB.cs
class DATADB
{
public class NewsObject
{
BitmapImage thumb = null;
public BitmapImage Thumb {
get {
if (thumb==null)
{
Regex rgx = new Regex("^[^,]*,");
thumb = Utilities.base64image(rgx.Replace(this.default_photo, ""));
}
return thumb;
}
}
public string date { get; set; }
public string id { get; set; }
public string info { get; set; }
}
}
Utilities.cs
class Utilities
{
public static BitmapImage base64image(string base64string)
{
if (base64string == "" || base64string == null) return null;
byte[] fileBytes = Convert.FromBase64String(base64string);
using (MemoryStream ms = new MemoryStream(fileBytes, 0, fileBytes.Length))
{
ms.Write(fileBytes, 0, fileBytes.Length);
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.SetSource(ms); //Getting error message here.
return bitmapImage;
}
}
}
Try using InMemoryRandomAccessStream instead of MemoryStream.
trying to use
bitmapImage.SetSource(new MemoryRandomAccessStream(ms));
instead of
bitmapImage.SetSource(ms);
Got a little bit of a problem. I have a program that builds an observable collection of Users. The User has a Firstname, Lastname, and Image. I can add the user to the observable collection, but I also want to save the collection and load it everytime I reopen the program.
My problem is that while its fairly easy to save a firstname and lastname, the writer can't write the image to the xml file. Is there any way around this?
Here's what I have so far:
the observable collection:
ObservableCollection<VendorClass> ProfileList = new ObservableCollection<VendorClass>();
the problematic writer:
XmlSerializer xs = new XmlSerializer(typeof(ObservableCollection<VendorClass>));
using (StreamWriter wr = new StreamWriter("vendors.xml")) //Data/customers.xml
{
xs.Serialize(wr, ProfileList);
}
Any ideas? And if there does exist a solution to write in an image, is there a viable way to read it out again?
XmlSerializer can't serialize or deserialize the WPF image types like BitmapImage etc. It is however able to (de)serialize byte arrays. So you may add a byte[] ImageBuffer property to your Person class, which contains the binary image data. You would then also set the XmlIgnore attribute on the Image property to suppress its (de)serialization, and set XmlElement("Image") on the ImageBuffer properties to (de)serialize it as <Image>...</Image>.
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
[XmlIgnore]
public BitmapSource Image { get; set; }
[XmlElement("Image")]
public byte[] ImageBuffer
{
get
{
byte[] imageBuffer = null;
if (Image != null)
{
using (var stream = new MemoryStream())
{
var encoder = new PngBitmapEncoder(); // or some other encoder
encoder.Frames.Add(BitmapFrame.Create(Image));
encoder.Save(stream);
imageBuffer = stream.ToArray();
}
}
return imageBuffer;
}
set
{
if (value == null)
{
Image = null;
}
else
{
using (var stream = new MemoryStream(value))
{
var decoder = BitmapDecoder.Create(stream,
BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
Image = decoder.Frames[0];
}
}
}
}
}
This approach has also been suggested for properties of type Bitmap in this answer.
You would base64 encode the image to convert it to a string and then write that into a CDATA section. See How do you serialize a string as CDATA using XmlSerializer?
In my app I have to download all the images from the remote server and display it in a list view .When i tried to extract the images from isolated storage I have got an exception
"Operation not permitted on IsolatedStorageFileStream"
.here is my code
public static object ExtractFromLocalStorage(Uri imageFileUri)
{
byte[] data;
string isolatedStoragePath = GetFileNameInIsolatedStorage(imageFileUri); //Load from local storage
if (null == _storage)
{
_storage = IsolatedStorageFile.GetUserStoreForApplication();
}
BitmapImage bi = new BitmapImage();
//HERE THE EXCEPTION OCCURS
using (IsolatedStorageFileStream sourceFile = _storage.OpenFile(isolatedStoragePath, FileMode.Open, FileAccess.Read))
{
data = new byte[sourceFile.Length];
// Read the entire file and then close it
sourceFile.Read(data, 0, data.Length);
// Create memory stream and bitmap
MemoryStream ms = new MemoryStream(data);
// Set bitmap source to memory stream
bi.SetSource(ms);
sourceFile.Close();
}
return bi;
}
the above function is used to get the bitmap image from isolated storage,and my model class is
public class Details
{
public string id { get; set; }
public string name { get; set; }
public Uri imgurl { get; set; }
[IgnoreDataMember]
public BitmapImage ThumbImage{get;set;}
}
and i am using a singlton class to call the function to get image.
public class SingleTon
{
static SingleTon instance = null;
private SingleTon()
{
}
public static SingleTon Instance()
{
if (instance == null)
{
instance = new SingleTon();
}
return instance;
}
public BitmapImage getImage(Uri uri)
{
lock (this)
{
BitmapImage image = new BitmapImage();
image = (BitmapImage)CacheImageFileConverter.ExtractFromLocalStorage(uri);
return image;
}
}
public void writeImageToIsolatedStorage(Uri imageUri)
{
lock (this)
{
CacheImageFileConverter.DownloadFromWeb(imageUri);
}
}
}
this is the code for set the image to the object
SingleTon singleton = SingleTon.Instance();
List<(Details > datalist = new Details()
foreach (Details items in DataList)
{
items.ThumbImage = singleton.getImage(items.imgurl );
datalist.add(items);
}
please any one help me with a solution.
This exception usually occurs if you haven't closed a previously open Stream to the same file. Check CacheImageFileConverter.DownloadFromWeb and make sure your output Stream is wrapped in a using block.
That exception might also be thrown if the file doesn't exist, but I'm not 100% sure. Maybe through in a call to IsolatedStorageFile.FileExists() before you open it just to be sure.