| Microsoft | Articles | Forums | FAQs |
| C# .NET |  |  |  |  |
| VB.NET |  |  |  |  |
| Visual Studio .NET |  |  |  |  |
| ADO.NET |  |  |  |  |
| Xml / Xslt |  |  |  |  |
| VB 6.0 |  |  |  |  |
| .NET CF |  |  |  |  |
| GDI+ |  |  |  |  |
| LINQ |  |  |  |  |
| Deployment |  |  |  |  |
| Security |  |  |  |  |
| FoxPro |  |  |  |  |
| Silverlight / WPF |  |  |  |  |
| Entity Framework |  |  |  |  |
| RIA Services |  |  |  |  |
|
| Web Programming | Articles | Forums | FAQs |
| JavaScript |  |  |  |  |
| ASP |  |  |  |  |
| ASP.NET |  |  |  |  |
| Web Services |  |  |  |  |
|
| Non-Microsoft | Articles | Forums | FAQs |
| NHibernate |  |  |  |  |
| Perl |  |  |  |  |
| PHP |  |  |  |  |
| Ruby |  |  |  |  |
| Java |  |  |  |  |
| Linux / Unix |  |  |  |  |
| Apple |  |  |  |  |
| Open Source |  |  |  |  |
|
| Databases | Articles | Forums | FAQs |
| SQL Server |  |  |  |  |
| Access |  |  |  |  |
| Oracle |  |  |  |  |
| MySQL |  |  |  |  |
| Other Databases |  |  |  |  |
|
| Office | Articles | Forums | FAQs |
| Excel |  |  |  |  |
| Word |  |  |  |  |
| Powerpoint |  |  |  |  |
| Outlook |  |  |  |  |
| Publisher |  |  |  |  |
| Money |  |  |  |  |
|
| Operating Systems | Articles | Forums | FAQs |
| Windows 7 |  |  |  |  |
| Windows Server |  |  |  |  |
| Windows Vista |  |  |  |  |
| Windows XP |  |  |  |  |
| Windows Update |  |  |  |  |
| MAC |  |  |  |  |
| Linux / UNIX |  |  |  |  |
|
| Server Platforms | Articles | Forums | FAQs |
 |  |  |  |  |
| BizTalk |  |  |  |  |
| Site Server |  |  |  |  |
| Exhange Server |  |  |  |  |
| IIS |  |  |  |  |
|
| Graphic Design | Articles | Forums | FAQs |
| Macromedia Flash |  |  |  |  |
| Adobe PhotoShop |  |  |  |  |
| Expression Blend |  |  |  |  |
| Expression Design |  |  |  |  |
| Expression Web |  |  |  |  |
|
| Other | Articles | Forums | FAQs |
| Subversion / CVS |  |  |  |  |
| Ask Dr. Dotnetsky |  |  |  |  |
| Active Directory |  |  |  |  |
| Networking |  |  |  |  |
| Uninstall Virus |  |  |  |  |
| Job Openings |  |  |  |  |
| Product Reviews |  |  |  |  |
| Search Engines |  |  |  |  |
| Resumes |  |  |  |  |
|
| |
|
|
|
Silverlight 2 Beta 2: Doing Data Part III By Peter Bromberg Printer Friendly Version View My Articles 28 Views |  |
For this exercise we put aside our Quotations database work: We'll use the DataContractSerializer to serialize a Generic List of type Note. The serialized List will then be compressed with SharpZipLib, and finally we'll save the compressed byte array in an IsolatedStorage File so that we can load, decompress, deserialize, and get our Notes List back on demand. |
Silverlight: Compressing and Serializing Objects to Isolated Storage
Rob Houweling did a pretty decent job of porting the ICSharpCode.SharpZipLib library to Silverlight. I wonder how many people realize what a tremendous favor he did for us.
Now you can extract and decompress resources from your SilverLight xap file on demand; you can compress objects to compact byte arrays for sending over the wire, and a host of other useful things
For this exercise I've put aside my Quotations database work in favor of a different take on working with data: We'll use the DataContractSerializer to serialize a Generic List of type Note (a small class representing a "note" with item, description, dueDate, status, etc.). The serialized List will then be compressed with SharpZipLib, and finally we'll save the compressed byte array in an IsolatedStorage File so that we can load, decompress, deserialize, and get our Notes List back on demand, such as when you first load the app.
We'll display the Notes in a Silverlight DataGrid and add the options to create a new Note and save our work in Isolated Storage.
The easiest way to get started with this one is just to download the solution zip file from the bottom of this article, and load it into Visual Studio.
First, you need to create a class that represents the Data-bindable object you want to work with. In this case, its "Note":
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Runtime.Serialization;
namespace NotesSerializer
{
public enum Priority
{
Low,
Medium,
High
}
public enum Category
{
Personal,
Home,
Business
}
[DataContract]
public class Note
{
[DataMember]
public string Item { get; set; }
[DataMember]
public string Description { get; set; }
[DataMember]
public Priority Priority { get; set; }
[DataMember]
public Category Category { get; set; }
[DataMember]
public DateTime DueDate { get; set; }
[DataMember]
public bool Status {get;set;}
public Note()
{
}
public Note(string item, string description, Priority priority, Category category, DateTime dueDate, bool status )
{
this.Item = item;
this.Description = description;
this.Priority = priority;
this.Category = category;
this.DueDate = dueDate;
this.Status = status;
}
}
}
You can see that I have added the required [DataContract] and [DataMember] attributes that the DataContractSerializer needs in order to know what to do with the instance you feed it. There are other attributes that you can look up in the documentation, but we don't need any of them here.
I was going to use the XmlSerializer, but after reading the Silverlight documentation on it I realized it probably was not going to buy me anything special. I even thought about porting Angelo Scotto's CompactFormatter to get binary serialization but I quickly realized it was just too much work. I see that Rocky Lhotka has already started some similar work in this vein for his CSLA; Rocky, I wish you luck! The bottom line here is that you've got some 4.6MB of stuff to install for the user to get Silverlight, and there's only so much "cool stuff" you can pack into it. Painful decisions have to be made, so get over it. It's natural that the average developer won't agree with what decisions were made -- all you need to do is look at the ridiculous posts on the Silverlight Forums about a petition to bring back synchronous WebRequests. DOH! (The moderators deleted the entire thread as it had sunk into an ad-hominem hatefest).
Now that I have my Note class, I need a wrapper over the SharpZipLib library in order to make compressing / decompressing and Serialization easier to work with. This was easy to add since I already had one from some previous work. It only needed minor additions for the Serialization:
using System;
using System.Text;
using System.IO;
using System.Collections;
using System.Diagnostics;
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace ICSharpCode.SharpZipLib
{
public class Wrapper
{
public Wrapper()
{
}
public byte[] Serialize (Object inst)
{
Type t = inst.GetType();
DataContractSerializer dcs = new DataContractSerializer(t);
MemoryStream ms = new MemoryStream();
dcs.WriteObject(ms, inst);
return ms.ToArray();
}
public Object Deserialize (Type t, byte[] objectData)
{
DataContractSerializer dcs = new DataContractSerializer(t);
MemoryStream ms = new MemoryStream(objectData);
return dcs.ReadObject(ms);
}
public byte[] SerializeAndCompress(Object inst)
{
byte[] b = Serialize(inst);
byte[] b2 = Compress(b);
return b2;
}
public Object DecompressAndDeserialize(Type t, byte[] bytData)
{
byte[] b = Decompress(bytData);
Object o = Deserialize(t, b);
return o;
}
public byte[] Compress(string strInput)
{
try
{
byte[] bytData = System.Text.Encoding.UTF8.GetBytes(strInput);
MemoryStream ms = new MemoryStream();
ICSharpCode.SharpZipLib.Zip.Compression.Deflater defl = new ICSharpCode.SharpZipLib.Zip.Compression.Deflater(9,false);
Stream s = new ICSharpCode.SharpZipLib.Zip.Compression.Streams.DeflaterOutputStream(ms,defl);
s.Write(bytData, 0, bytData.Length);
s.Close();
byte[] compressedData = (byte[])ms.ToArray();
return compressedData;
}
catch
{
throw;
}
}
public byte[] Compress(byte[] bytData)
{
try
{
MemoryStream ms = new MemoryStream();
ICSharpCode.SharpZipLib.Zip.Compression.Deflater defl = new ICSharpCode.SharpZipLib.Zip.Compression.Deflater(9, false);
Stream s = new ICSharpCode.SharpZipLib.Zip.Compression.Streams.DeflaterOutputStream(ms, defl);
s.Write(bytData, 0, bytData.Length);
s.Close();
byte[] compressedData = (byte[])ms.ToArray();
return compressedData;
}
catch
{
throw;
}
}
public byte[] Compress(byte[] bytData, params int[] ratio)
{
int compRatio=9;
try
{
if ( ratio[0] >0 )
{
compRatio=ratio[0];
}
}
catch
{
}
try
{
MemoryStream ms = new MemoryStream();
ICSharpCode.SharpZipLib.Zip.Compression.Deflater defl = new ICSharpCode.SharpZipLib.Zip.Compression.Deflater(compRatio,false);
Stream s = new ICSharpCode.SharpZipLib.Zip.Compression.Streams.DeflaterOutputStream(ms,defl);
s.Write(bytData, 0, bytData.Length);
s.Close();
byte[] compressedData = (byte[])ms.ToArray();
return compressedData;
}
catch
{
throw;
}
}
public byte[] Decompress(byte[] bytInput)
{
MemoryStream ms = new MemoryStream(bytInput,0,bytInput.Length);
byte[] bytResult =null;
string strResult=String.Empty;
byte[] writeData = new byte[4096];
Stream s2 = new ICSharpCode.SharpZipLib.Zip.Compression.Streams.InflaterInputStream(ms);
try
{
bytResult=ReadFullStream(s2);
s2.Close();
return bytResult;
}
catch
{
throw;
}
}
public byte[] ReadFullStream (Stream stream)
{
byte[] buffer = new byte[32768];
using (MemoryStream ms = new MemoryStream())
{
while (true)
{
int read = stream.Read (buffer, 0, buffer.Length);
if (read <= 0)
return ms.ToArray();
ms.Write (buffer, 0, read);
}
}
}
}
}It is important to understand that you do not need to create a Zip file and use the ZipEntry class to use SharpZipLib. You can simply use the DeflaterInputStream to compress to a byte array, and then do whatever you want with that (save to a file, send over the wire, etc.).
Then, I needed to put the UI elements (Grid, buttons, and DataGrid) into the Page Xaml which look like this:
<UserControl xmlns:my="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" x:Class="NotesSerializer.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="600" Height="400">
<Grid x:Name="LayoutRoot" Background="White" Width="600" Height="400" ShowGridLines="False">
<Grid.RowDefinitions>
<RowDefinition Height="120"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button x:Name="btnSave" Click="btnSave_Click" Content="Create" Width="100" Height="25" HorizontalAlignment="Left" VerticalAlignment="Bottom" Grid.Row="1" Grid.Column="0"/>
<Button x:Name="btnLoad" Click="btnLoad_Click" Content="Load" Width="100" Height="25" HorizontalAlignment="Right" VerticalAlignment="Bottom" Grid.Row="1" Grid.Column="1"/>
<Button x:Name="btnSaveCompressed" Click="btnSaveCompressed_Click" Content="Save Comp." Width="100" Height="25" HorizontalAlignment="Center" VerticalAlignment="Bottom" Grid.Row="1" Grid.Column="2"/>
<Button x:Name="btnNew" Click="btnNew_Click" Content="New Note" Width="100" Height="25" HorizontalAlignment="Left" VerticalAlignment="Bottom" Grid.Row="1" Grid.Column="3"/>
<my:DataGrid x:Name="Grid1" HorizontalAlignment="Center" VerticalAlignment="Top" AutoGenerateColumns="True" AlternatingRowBackground="AliceBlue" BorderThickness="2" Width="550" Height="250" Grid.Row="1" Grid.ColumnSpan="4">
</my:DataGrid>
</Grid>
</UserControl>Finally, here is the codebehind for the Page with all the code that handles the UI and saving / loading the serialized, compressed List that we are working with:
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.IsolatedStorage;
using System.Windows;
using System.Windows.Controls;
using ICSharpCode.SharpZipLib;
namespace NotesSerializer
{
public partial class Page : UserControl
{
private List LNotes;
private Note note;
private Note note2;
public Page()
{
InitializeComponent();
}
private void btnSave_Click(object sender, RoutedEventArgs e)
{
// (this is actually for the create button)
note = new Note("Test", "This is a test", Priority.High, Category.Personal, DateTime.Now, false);
note2 = new Note("Test2", "This is a test2", Priority.Low, Category.Business, DateTime.Now, true);
LNotes = new List();
LNotes.Add(note);
LNotes.Add(note2);
Grid1.ItemsSource = LNotes;
}
private void SaveNotes()
{
var w = new Wrapper();
byte[] b = w.SerializeAndCompress(LNotes);
using (IsolatedStorageFile isoStore =
IsolatedStorageFile.GetUserStoreForApplication())
{
using (var isoStream =
new IsolatedStorageFileStream("notes.dat",
FileMode.Create, isoStore))
{
isoStream.Write(b, 0, b.Length);
}
}
}
private void LoadSavednotes()
{
var notesBytes = new byte[1024];
byte[] fullnotesBytes = null;
var ms = new MemoryStream();
using (IsolatedStorageFile isoStore =
IsolatedStorageFile.GetUserStoreForApplication())
{
using (var isoStream =
new IsolatedStorageFileStream("notes.dat",
FileMode.Open,FileAccess.Read, isoStore))
{
using (var reader = new BinaryReader(isoStream))
{
while (true)
{
int read = reader.Read(notesBytes, 0, notesBytes.Length);
if (read <= 0)
{
fullnotesBytes = ms.ToArray();
break;
}
ms.Write(notesBytes, 0, read);
}
}
}
byte[] b = ms.ToArray();
var w = new Wrapper();
LNotes = (List) w.DecompressAndDeserialize(typeof (List), b);
Grid1.ItemsSource = LNotes;
}
}
private void btnLoad_Click(object sender, RoutedEventArgs e)
{
LoadSavednotes();
}
private void btnSaveCompressed_Click(object sender, RoutedEventArgs e)
{
SaveNotes();
}
private void btnNew_Click(object sender, RoutedEventArgs e)
{
Note note = new Note("", "", Priority.Medium, Category.Personal, DateTime.Now, false);
LNotes.Add(note);
Grid1.ItemsSource = null;
Grid1.ItemsSource = LNotes;
}
}
}When you load the app, here's what you should see, right after you hit the "Create" button:
"Create" just adds two Note instances to "LNotes", which is a class-level List of type Note, and binds the DataGrid to the collection. This is just so that we can start out with something to work with. I didn't bother to figure out how to make dropdowns out of the Category and Priority enums since this is just a proof of concept. I didn't add a Delete function either. But the general concept is clear: you can build apps that use IsolatedStorage as a client-side data store, and if the data is compressed, you can store one heck of a lot of "stuff".
"New Note" adds a new Note to the List, and rebinds the grid, basically giving you a new Note to fill in.
"Save Comp" serializes and compresses the LNotes List and saves it to an IsolatedStorage file "notes.dat".
And "Load" Loads the notes.dat file bytes, decompresses it, and then deserializes into a new LNotes list and binds the grid. If you restart the app at a later time, and then hit the LOAD button, you'll get back all your work - all saved in highly compressed form on the client, from IsolatedStorage. You can download the Visual Studio 2008 Silverlight Application here.
View Part I of this series.
View Part II of the series. |
 |
| Biography - Peter Bromberg |
Peter Bromberg is a C# MVP, MCP, and .NET expert who has worked in banking, financial and telephony for over 20 years. Pete focuses exclusively on the .NET Platform, and currently develops SOA and other .NET applications for a Fortune 500 clientele. Peter enjoys producing digital photo collage with Maya,playing jazz flute, the beach, and fine wines. You can view Peter's UnBlog and IttyUrl sites. Please post questions at forums, not via email! |  |
|
|
 |
| Article Discussion: Silverlight 2 Beta 2: Doing Data Part III |
| Peter Bromberg posted at Friday, July 04, 2008 2:58 PM |
 | Original Article |
 |
| |
|
| IsolatedStorage, Silverlight, and Web Browers |
| Robbe Morris replied to Peter Bromberg at Friday, July 04, 2008 4:47 PM |
 | Pete,
For those who do not do much with IsolatedStorage or perhaps don't even know what it means, can you provide a little information on gotchas, size maximums, potential security roadblocks that corporations might throw up via pc security configurations, and any browser specific behavior that might be relevant to implementing your strategy in SilverLight. |
 |
| |
|
| IsolatedStorage |
| Peter Bromberg replied to Robbe Morris at Saturday, August 16, 2008 6:30 PM |
 | In the .NET Framework with isolated storage, data is always isolated by user and by assembly. Credentials such as the origin or the strong name of the assembly determine assembly identity. Data can also be isolated by application domain, using similar credentials.
When using isolated storage for Silverlight apps, applications save data to a unique data compartment that is associated with either the application, or the local machine. This is the ONLY direct client - side filesystem access that is permitted under the Silverlight browser security sandbox.
The .NET Framework (and the Silverlight subset framework) provides access to Isolated Storage via the IsolatedStorage classes. Think of it as a local client-side data store that is accessible only by the current user on their own machine via a Silverlight application that is running in their browser. The default space allowed is 1 MB, but the API allows you to request the user to increase this at their discretion. |
 |
| |
|
| maybe Compress could be improved |
| Wilson Chan replied to Peter Bromberg at Monday, July 07, 2008 6:48 AM |
 | Hi,
those 4 Compress functions are more or less the same, maybe digest them and make a more reuseful function could be better :) |
 |
| |
|
| They are convenience overloads. |
| Peter Bromberg replied to Wilson Chan at Wednesday, July 09, 2008 3:58 PM |
 | I put them there for convenience depending on the input and output types. Developers do this all the time. This is called "method overloading". |
 |
| |
|
| zip file is invalid format! |
| Junaid Khalid replied to Peter Bromberg at Tuesday, June 16, 2009 4:11 AM |
Hi Peter,
I am trying to zip the user selected file and place it insolated storage. Frist copying the same file without compression works fine and i can save it in isolated storage, now i am trying to compress it. For that I tried many things but did not succeed. http://silverlight.net/forums/t/101789.aspx
Then I found your article and I tried to compress this way, it works at leaset no exception, but the file I see in isolated storage is not in correct format, If I try to open it with winrar I see message that file is not in regonised format or damaged, can you please guide me what I am doing wrong. Here is my code snippet.
var memoryStream = new MemoryStream();
var deflater = new Deflater(9, false);
Stream stream = new DeflaterOutputStream(memoryStream, deflater);
stream.Write(filebuffer, 0, filebuffer.Length);
stream.Close();
var compressedfilebuffer = memoryStream.ToArray();
var isolatedStorageFileStream = new IsolatedStorageFileStream("\\MyApp\\" + openFileDialog1.File.Name, FileMode.Create, isolatedStorageFile);
isolatedStorageFileStream.Write(compressedfilebuffer, 0, compressedfilebuffer.Length);
isolatedStorageFileStream.Close();
isolatedStorageFileStream.Dispose();
isolatedStorageFile.Dispose();
Regards --Junaid |
 |
| |
|
| serilize and compress |
| Junaid Khalid replied to Junaid Khalid at Tuesday, June 16, 2009 5:11 AM |
I tried serialize and compress as well, but i see the same error.
|
 |
| |
|
| The problem you have is that the compressed stream |
| Peter Bromberg replied to Junaid Khalid at Tuesday, June 16, 2009 7:46 AM |
 | is just the raw compressed data without any of the zip headers that are required by utilities like Winzip and winrar to "read" a zip file. These headers store folder information and so on. If you just have one file, there is no need to use winzip or winrar to read it - just read the compressed bytes into a byte array, and apply the decompression code on that. |
 |
| |
|
| serilize and compress |
| Junaid Khalid replied to Peter Bromberg at Tuesday, June 16, 2009 8:24 AM |
OK,
I did not know that its raw compressed data, actually I have to compress many files but one by one and have to keep them seprate rather I put them in one folder and compress the whole folder, is it possible to add those headers seprately to each file ? SharpLibZip has some method for that ? |
 |
| |
|
| compressing a file |
| Junaid Khalid replied to Junaid Khalid at Thursday, June 18, 2009 7:28 AM |
this thread might help of those who are facing situation like me.
http://silverlight.net/forums/p/101789/234808.aspx
|
 |
| |
|
|
|