Save and restore lines drawn by a WPF line editor in C#

[WPF line editor]

The example Make a WPF line editor C# shows how to make a program that lets the user draw lines on a WPF canvas. This example is similar but it also allows you to save and restore drawings.

The WPF objects that represent lines are from the Line class. Unfortunately that class is marked as serializable. If you want to save and restore the lines in a serialization, you need to make your own class that is serializable. This example uses the following Segment class.

// Represent a serializable line segment.
[Serializable()]
public class Segment
{
    public double X1, Y1, X2, Y2;
    public Segment()
    {
    }
    public Segment(double x1, double y1, double x2, double y2)
    {
        X1 = x1;
        Y1 = y1;
        X2 = x2;
        Y2 = y2;
    }
}

This class stores the coordinates needed by a WPF line. You can add other properties such as line thickness and color if you like.

This class has three key features. First, it is marked with the Serializable attribute. Second, it must be public. Third, it must have a constructor that takes no parameters. It includes another constructor to make working with the class easier.

The following code shows how the WPF line editor saves the current lines into an XML file.

// Save a serialized file.
private void Save_Click(object sender, RoutedEventArgs e)
{
    // Get the name of the file where we should save the segments.
    SaveFileDialog dlg = new SaveFileDialog();
    dlg.DefaultExt = ".xml";
    dlg.Filter = "XML (*.xml)|*.xml|All Files (*.*)|*.*";
    Nullable<bool> result = dlg.ShowDialog();
    if (result.Value != true) return;

    // Make a list of segments.
    List<Segment> segments = new List<Segment>();
    foreach (object obj in canDrawing.Children)
    {
        if (obj is Line)
        {
            Line line = (Line)obj;
            segments.Add(new Segment(line.X1, line.Y1, line.X2, line.Y2));
        }
    }

    // Make the XmlSerializer.
    XmlSerializer serializer =
        new XmlSerializer(typeof(List<Segment>));
    using (FileStream stream = File.Create(dlg.FileName))
    {
        serializer.Serialize(stream, segments);
    }
}

This method creates a SaveFileDialog and uses it to let the user pick teh file where the drawing should be saved.

Next, it creates a list of Segment objects named segments. It then loops through the Canvas control’s children. If a child is a Line object, the code converts the generic child object into a Line and adds it to the segments list.

The method then creates an XML serializer that can serialize objects of the type List<Segment>. It creates a file stream to create the file selected by the OpenFileDialog and uses the serializer to serialize the segments list into the file.

In order to load a serialized file, the program uses the following simple helper routine.

private void ClearCanvas()
{
    // Remove existing segments.
    for (int i = canDrawing.Children.Count - 1; i >= 0; i--)
    {
        if (canDrawing.Children[i] is Line)
            canDrawing.Children.RemoveAt(i);
    }
}

This method simply loops through the Canvas control’s children and removes those that are Line objects.

The following code shows how the program restores a previously saved drawing.

// Open a serialized file.
private void Open_Click(object sender, RoutedEventArgs e)
{
    // Get the name of the file where we should save the segments.
    OpenFileDialog dlg = new OpenFileDialog();
    dlg.DefaultExt = ".xml";
    dlg.Filter = "XML (*.xml)|*.xml|All Files (*.*)|*.*";
    Nullable<bool> result = dlg.ShowDialog();
    if (result.Value != true) return;

    // Remove existing segments.
    ClearCanvas();

    // Make the XmlSerializer.
    XmlSerializer serializer =
        new XmlSerializer(typeof(List<Segment>));
    using (FileStream stream = 
        File.Open(dlg.FileName, FileMode.Open))
    {
        List<Segment> segments =
            (List<Segment>)serializer.Deserialize(stream);

        // Add the loaded segments.
        foreach (Segment segment in segments)
        {
            Line new_line = new Line();
            new_line.X1 = segment.X1;
            new_line.Y1 = segment.Y1;
            new_line.X2 = segment.X2;
            new_line.Y2 = segment.Y2;
            new_line.Stroke = Brushes.Black;
            canDrawing.Children.Add(new_line);
        }
    }
}

This method uses an OpenFileDialog to let the user select the XML file to open. If the user selects a file, the code calls the ClearCanvas method to remove any existing lines. It then creates a serializer that can serialize the List<Segment> type.

Next, the method creates a file stream to read the selected file and uses the serializer to deserialize the file and create a List<Segment> from its contents.

Finally, the method loops through the returned Segment objects and uses them to add new Line objects to the program’s Canvas control.

That’s all of the code that deals with saving and restoring drawings in the WPF line editor. To see additional details, download the example program and see the previous post Make a WPF line editor C#.


Download Example   Follow me on Twitter   RSS feed   Donate




About RodStephens

Rod Stephens is a software consultant and author who has written more than 30 books and 250 magazine articles covering C#, Visual Basic, Visual Basic for Applications, Delphi, and Java.
This entry was posted in drawing, graphics, wpf and tagged , , , , , , , , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.