Reputation: 13
I'm trying to save strokes and children from a WPF InkCanvas. I managed to get the file to save, but now I can't load it. It throws this exception:
Newtonsoft.Json.JsonSerializationException HResult=0x80131500 Message=Unable to find a constructor to use for type System.Windows.Ink.Stroke. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Path 'strokes[0].DrawingAttributes', line 4, position 26. Source=Newtonsoft.Json
Code for save/load classes:
class SaveLoad
{
public static void Save(Board board) {
Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog();
dlg.FileName = "Document";
dlg.DefaultExt = ".txt";
dlg.Filter = "Text documents (.txt)|*.txt";
Nullable<bool> result = dlg.ShowDialog();
if (result == true)
{
string outpath = dlg.FileName;
string jsonData = JsonConvert.SerializeObject(board, Newtonsoft.Json.Formatting.Indented);
var myFile = File.Create(outpath);
myFile.Close();
File.WriteAllText(@"" + outpath, jsonData);
}
}
public static Board Load()
{
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
dlg.FileName = "Document";
dlg.DefaultExt = ".txt";
dlg.Filter = "Text documents (.txt)|*.txt";
Nullable<bool> result = dlg.ShowDialog();
if (result == true)
{
string outpath = dlg.FileName;
string jsonData = File.ReadAllText(outpath);
return JsonConvert.DeserializeObject<Board>(jsonData);
}
else { return null; }
}
}
internal class Board
{
public StrokeCollection strokes;
public UIElementCollection elements;
public UIElementCollection elements_serialized = new UIElementCollection(new UIElement(),new FrameworkElement());
public Board(StrokeCollection strokes, UIElementCollection elements) { this.strokes = strokes; this.elements = elements; }
public void Serialize() {
foreach (UIElement element in this.elements) {
elements_serialized.Add(Clone(element));
}
}
public Board getSerialized() {
return new Board(strokes,elements_serialized);
}
public static T Clone<T>(T element)
{
string xaml = XamlWriter.Save(element);
using (StringReader stringReader = new StringReader(xaml))
using (XmlReader xmlReader = XmlReader.Create(stringReader))
return (T)XamlReader.Load(xmlReader);
}
}
Code for saving:
Board board = new Board(mainInk.Strokes, mainInk.Children);
board.Serialize();
SaveLoad.Save(board.getSerialized());
Code for loading:
Board board = SaveLoad.Load();
if (board == null) {
mainInk.Strokes = board.strokes;
mainInk.Children.Clear();
foreach (UIElement element in board.elements)
{
mainInk.Children.Add(Board.Clone(element));
}
}
Upvotes: 0
Views: 56
Reputation: 6793
As @Migue Ang Rdz said you create a custom converter for the
StrokeCollection
orStroke
classes to handle their serialization and deserialization.
Refer the code below for more reference:
Create a class like below:
public class StrokeConverter : JsonConverter<Stroke>
{
public override void WriteJson(JsonWriter writer, Stroke value, JsonSerializer serializer)
{
var data = new
{
Points = value.StylusPoints,
DrawingAttributes = value.DrawingAttributes
};
serializer.Serialize(writer, data);
}
public override Stroke ReadJson(JsonReader reader, Type objectType, Stroke existingValue, bool hasExistingValue, JsonSerializer serializer)
{
var data = serializer.Deserialize<dynamic>(reader);
var points = ((JArray)data["Points"]).ToObject<StylusPointCollection>();
var attributes = ((JObject)data["DrawingAttributes"]).ToObject<DrawingAttributes>();
return new Stroke(points) { DrawingAttributes = attributes };
}
}
Then register above and start using it like below:
var settings = new JsonSerializerSettings();
settings.Converters.Add(new StrokeConverter()); // Register the converter
var jsonData = JsonConvert.SerializeObject(board, Formatting.Indented, settings);
Upvotes: 0
Reputation: 96
Implement a custom converter that handles the serialization and deserialization of the Stroke object. since this class does not have a constructor parameter less.
public class StrokeConverter : JsonConverter
{
//overrides here
}
Upvotes: 0