Search code examples
c#wpfserializationdeserializationwpf-controls

Serializing and Deserializing multiple custom UserControls to a Canvas with WPF?


As stated in the title, I am attempting to serialize and deserialize the contents of a canvas within WPF. I have been using this link as my guide. In this case, the contents of the canvas are custom user controls that are added by the user. In my program , these are blue circles and red squares (used for simplicity and ease of explanation).

Currently, I can save and load a file, however, when loading the saved file, there are a few issues that arise.

  1. Only red squares are created by the program.
  2. The red squares created are not in the same position as when the file was saved. They are all generated in the 0,0 position of the canvas.

The program does create the correct number of shapes though. However, quite unhelpfully, I am not sure whether serialisation, deserialisation or both is where the program is failing.

To aid understanding, here is the class that is storing the shapes, ready for serialisation:

[Serializable()]
public class shaypeh
{
    private double x, y;
    private bool circleOrSquare; 

    public shaypeh()
    {

    }

    public shaypeh(double x, double y, bool circleOrSquare)
    {
        this.x = x;
        this.y = y;
        this.circleOrSquare = circleOrSquare;
    }

    public bool getCircleOrSquare()
    {
        return circleOrSquare;
    }

    public double getX()
    {
        return x;
    }

    public double getY()
    {
        return y;
    }
}

The following code is the function that I am using to save the canvas. I suspect that this is the issue with the program, however I am unsure where I am going wrong.

Here is the output when 2 circles and 1 square are added to the canvas and then it is saved.

<?xml version="1.0"?>
<ArrayOfShaypeh xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <shaypeh />
  <shaypeh />
  <shaypeh />
</ArrayOfShaypeh>
double top;
double left;
List<shaypeh> shaypehs = new List<shaypeh>();
SaveFileDialog sfd = new SaveFileDialog();
sfd.Filter = "Text file (*.txt)|*.txt|XML (*.xml)|*.xml";
if (sfd.ShowDialog() != true)
{
    return; 
}

foreach(object obj in pictureBoard.Children)
{
    if (obj is Circle)
    {
        UIElement s = (Circle)obj;
        top = Canvas.GetTop(s); 
        left = Canvas.GetLeft(s);
        shaypehs.Add(new shaypeh(left, top, true));
    }
    if (obj is Rectangle)
    {
         UIElement s = (Rectangle)obj;
         top = Canvas.GetTop(s);
         left = Canvas.GetLeft(s);
         shaypehs.Add(new shaypeh(left, top, true));
     }
}
XmlSerializer serializer = new XmlSerializer(typeof(List<shaypeh>));
using (FileStream stream = File.Create(sfd.FileName))
{
    serializer.Serialize(stream, shaypehs);
}

The following code is what I am using to then deserialise the file. This I do believe is correct, however I am unsure about this.

OpenFileDialog ofd = new OpenFileDialog();
ofd.DefaultExt = ".xml"; 
ofd.Filter = "Text file (*.txt)|*.txt|XML (*.xml)|*.xml";
if (ofd.ShowDialog()!= true)
{
    return; 
}

pictureBoard.Children.Clear();
XmlSerializer serializer = new XmlSerializer (typeof(List<shaypeh>));
using (FileStream stream = File.Open(ofd.FileName, FileMode.Open))
{
    List<shaypeh> shaypehs = (List<shaypeh>)serializer.Deserialize(stream);
    foreach(shaypeh shape in shaypehs)
    {
        if (shape.getCircleOrSquare() == true)
        {
            Circle c = new Circle();
            Canvas.SetTop(c, shape.getY());
            Canvas.SetLeft(c, shape.getX());
            c.PreviewMouseDown += c_PreviewMouseDown; 
            pictureBoard.Children.Add(c);
        }
        if (shape.getCircleOrSquare() == false)
        {
            Rectangle r = new Rectangle();
            Canvas.SetTop(r, shape.getY());
            Canvas.SetLeft(r, shape.getX());
            r.PreviewMouseDown += c_PreviewMouseDown;
            pictureBoard.Children.Add(r);
         }
    }
}

Any help/advice would be greatly appreciated.


Solution

  • The XmlSerializer ignores private members.

    You should implement shaypeh as a C# type, i.e. replace the get* methods with properties and change the names to comply with the common coding conventions:

    [Serializable]
    public class Shaypeh
    {
        public Shaypeh()
        {
    
        }
    
        public Shaypeh(double x, double y, bool circleOrSquare)
        {
            this.X = x;
            this.Y = y;
            this.CircleOrSquare = circleOrSquare;
        }
    
        public bool CircleOrSquare { get; set; }
    
        public double X { get; set; }
    
        public double Y { get; set; }
    }
    

    Using the above implementation of Shaypeh, your seralization code should work.