Title: Save and restore pictures drawn by the user in C#
The example Let the user scribble with different line styles in C# explains how to let the user draw curves with different colors, line thicknesses, and dash patterns. This example adds the ability to save and load pictures drawn by the user.
It turns out, this is remarkably easy. The following code shows how the program saves the Polyline objects stored in the Polylines list.
// Save the drawing.
private void mnuFileSaveAs_Click(object sender, EventArgs e)
{
if (sfdFile.ShowDialog() == DialogResult.OK)
{
XmlSerializer xml_serializer =
new XmlSerializer(Polylines.GetType());
using (StreamWriter stream_writer =
new StreamWriter(sfdFile.FileName))
{
xml_serializer.Serialize(stream_writer, Polylines);
stream_writer.Close();
}
}
}
The code displays a SaveFileDialog. If the user selects a file and clicks Save, the program create an XmlSerializer object. It passes the constructor the type of the thing that it will serialize. In this case, that's the data type of the Polylines variable, which is List<Polyline>.
The program then creates a StreamWriter associated with the file. It calls the serializer's Serialize method, passing it the StreamWriter and the object to serialize.
The following code shows how the program deserializes a serialized file.
// Open a saved drawing.
private void mnuFileOpen_Click(object sender, EventArgs e)
{
if (ofdFile.ShowDialog() == DialogResult.OK)
{
try
{
XmlSerializer xml_serializer =
new XmlSerializer(Polylines.GetType());
using (FileStream file_stream =
new FileStream(ofdFile.FileName, FileMode.Open))
{
List<Polyline> new_polylines =
(List<Polyline>)
xml_serializer.Deserialize(file_stream);
Polylines = new_polylines;
picCanvas.Refresh();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
This code displays a OpenFileDialog. If the user selects a file and clicks Open, the program again creates an XmlSerializer for the Polylines type.
Next the program creates a FileStream associated with the file, opening it as it creates the FileStream.
The code then calls the serializer's Deserialize method. The result is a generic object, so the program casts it to the List<Polyline> type. It finally saves the result in the variable Polylines and refreshes the drawing PictureBox.
If those were the only changes you made, you would be able to save and restore Polylines, but they would be missing their Color properties. It turns out, that all of the other properties of the Polyline class (int, DashStyl, and even List<Point>) all serialize automatically, but the Color class does not. That means when you serialize a list of Polylines, their colors are not saved. When you reload a file, the program draws the Polylines, but you don't see them because their colors are not reloaded.
The workaround is to create a property that represents Color but that can be serialized. This example uses a property named Argb that gets and sets the color's alpha, red, green, and blue color components as a combined integer. An integer can serialize automatically so the program works.
To avoid a bit of wasted effort, the program also flags the Color property with the XmlIgnore attribute so the serializer ignores it completely.
The following shows the revised Polyline class.
public class Polyline
{
[XmlIgnore] public Color Color = Color.Black;
public int Thickness = 1;
public DashStyle DashStyle = DashStyle.Solid;
public List<Point> Points = new List<Point>();
// Get or set the color as an ARGB value.
public int ToArgb
{
get { return this.Color.ToArgb(); }
set { Color = Color.FromArgb(value); }
}
public void Draw(Graphics gr)
{
using (Pen the_pen = new Pen(Color, Thickness))
{
the_pen.DashStyle = DashStyle;
if (DashStyle == DashStyle.Custom)
{
the_pen.DashPattern = new float[] { 10, 2 };
}
gr.DrawLines(the_pen, Points.ToArray());
}
}
}
The Argb property's get method returns the value represented by the Color property, and its set method uses an integer to set the object's Color property.
Download the example to experiment with it and to see additional details.
|