How to save shapes which I draw on a Panel as binary

You don’t need to serialize the panel, a panel is not serializable. You can consider either of these options:

  1. You can draw on a bitmap and then save the bitmap, or draw your control to an image and save the image. This way you will flatten all shapes to a single image and after loading the image they are not editable shapes anymore. It would be like paint.
  2. You can make your shapes serializable and then serialize a list of your serializable shapes in a file. Then you can deserialize them again. This way you can load your shapes and let the user edit them, for example like Visio.

I shared two examples here:

  • Save Image Example: It simply saves the drawings which you painted on panel to a bitmap image file.

  • Serialization Example: In this example I’ve created some serializable shape classes containing some properties like coordinates and color, then I created a Save and Load method which allows you to serialize your shapes in a file and also deserialize them from the file and show them again. You can simply extend this example to add some functionality like hit-testing and moving shapes. Also you can use a an xml serializer instead of a binary serializer.

Save Image Example

To keep things simple in this example I just save panel paintings in a file.

If you put painting logic in Paint event of your panel, then you can use DrawToBitmap to save an image of your control to a file:

private void button1_Click(object sender, EventArgs e)
{
    using (var bm = new Bitmap(panel1.Width, panel1.Height))
    {
        panel1.DrawToBitmap(bm, new Rectangle(0, 0, bm.Width, bm.Height));
        bm.Save(@"d:\panel.bmp", System.Drawing.Imaging.ImageFormat.Bmp); 
    }
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.FillRectangle(Brushes.Red, 0, 0, 100, 100);
    e.Graphics.FillRectangle(Brushes.Blue, 50, 50, 100, 100);
}

Serialization Example

You can create some serializable shapes like LineShape and RectangleShape deriving from serializable Shape class. We store properties of shapes in this class, also these classes contain drawing logic:

[Serializable]
public abstract class Shape
{
    public abstract void Draw(Graphics g);
    public override string ToString() { return GetType().Name; }
}

[Serializable]
public class LineShape : Shape
{
    public LineShape(){ Color = Color.Blue; Width = 2; }
    public Point Point1 { get; set; }
    public Point Point2 { get; set; }
    public int Width { get; set; }
    public Color Color { get; set; }
    public override void Draw(Graphics g)
    {
        using (var pen = new Pen(Color, Width))
            g.DrawLine(pen, Point1, Point2);
    }
}

[Serializable]
public class RectangleShape : Shape
{
    public RectangleShape() { Color = Color.Red; }
    public Rectangle Rectangle { get; set; }
    public Color Color { get; set; }
    public override void Draw(Graphics g)
    {
        using (var brush = new SolidBrush(Color))
            g.FillRectangle(brush, Rectangle);
    }
}

You can create a ShapesList class to hold shapes and contain logic for saving and loading shapes. Also the logic for painting all shapes on a surface:

[Serializable]
public class ShapesList : List<Shape>
{
    public void Save(string file)
    {
        using (Stream stream = File.Open(file, FileMode.Create))
        {
            BinaryFormatter bin = new BinaryFormatter();
            bin.Serialize(stream, this);
        }
    }
    public void Load(string file)
    {
        using (Stream stream = File.Open(file, FileMode.Open))
        {
            BinaryFormatter bin = new BinaryFormatter();
            var shapes = (ShapesList)bin.Deserialize(stream);
            this.Clear();
            this.AddRange(shapes);
        }
    }
    public void Draw(Graphics g)
    {
        this.ForEach(x => x.Draw(g));
    }
}

Then you can use these shapes and shapes list this way:

ShapesList Shapes;
private void Form3_Load(object sender, EventArgs e)
{
    Shapes = new ShapesList();
    Shapes.Add(new RectangleShape() { Rectangle = new Rectangle(0, 0, 100, 100), 
        Color = Color.Green });
    Shapes.Add(new RectangleShape() { Rectangle = new Rectangle(50, 50, 100, 100), 
        Color = Color.Blue });
    Shapes.Add(new LineShape() { Point1 = new Point(0, 0), Point2 = new Point(150, 150), 
        Color = Color.Red });
    this.panel1.Invalidate();
}

private void button1_Click(object sender, EventArgs e)
{
    Shapes.Save(@"d:\shapes.bin");
    Shapes.Clear();
    this.panel1.Invalidate();
    MessageBox.Show("Shapes saved successfully.");
    Shapes.Load(@"d:\shapes.bin");
    this.panel1.Invalidate();
    MessageBox.Show("Shapes loaded successfully.");
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
    Shapes.Draw(e.Graphics);
}

Leave a Comment