Reputation: 3135
So after reading various other questions and answers regarding Json.NET's inability to (de)serialize Brush
types, it was apparent that I needed my own JsonConverter
. However where I am stuck is I am using a go-between object to handle the data as it is (de)serialized and since there are various typed objects depending on the brush type, I assume I need to store the type information in Json aswell, however using a [JsonProperty( TypeNameHandling = TypeNameHandling.All )]
is not working.
My converter:
public class BrushJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
object SerializableBrush = null;
if (value is SolidColorBrush)
SerializableBrush = new SerializableColorBrush(value as SolidColorBrush);
var jo = JObject.FromObject(SerializableBrush);
jo.WriteTo(writer);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
// Load JObject from stream
var jObject = JObject.Load(reader);
dynamic SerializableBrush = JsonConvert.DeserializeObject(jObject.ToString());
if (SerializableBrush is SerializableColorBrush)
return (SerializableBrush as SerializableColorBrush).ToBrush();
else if (SerializableBrush is SerializableImageBrush)
return (SerializableBrush as SerializableImageBrush).ToBrush();
else return null;
}
public override bool CanConvert(Type objectType)
{
return typeof(Brush).IsAssignableFrom(objectType);
}
}
My Go-between objects:
class SerializableColorBrush
{
public Color Color{ get; set; }
public SerializableColorBrush(SolidColorBrush Brush)
{
this.Color = Brush.Color;
}
public SolidColorBrush ToBrush()
{
SolidColorBrush brush = new SolidColorBrush(this.Color);
return brush;
}
}
class SerializableImageBrush
{
public ImageSource ImageSource { get; set; }
public TileMode TileMode { get; set; }
public Stretch Stretch { get; set; }
public AlignmentX AlignmentX { get; set; }
public AlignmentY AlignmentY { get; set; }
public SerializableImageBrush(ImageBrush Brush)
{
this.ImageSource = Brush.ImageSource;
this.TileMode = Brush.TileMode;
this.Stretch = Brush.Stretch;
this.AlignmentX = Brush.AlignmentX;
this.AlignmentY = Brush.AlignmentY;
}
public ImageBrush ToBrush()
{
ImageBrush brush = new ImageBrush();
brush.ImageSource = ImageSource;
brush.TileMode = TileMode;
brush.Stretch = Stretch;
brush.AlignmentX = AlignmentX;
brush.AlignmentY = AlignmentY;
return brush;
}
}
An example property to be serialized:
private Brush _WindowBG = SystemColors.AppWorkspaceBrush;
[JsonConverter(typeof(BrushJsonConverter))]
[JsonProperty( TypeNameHandling = TypeNameHandling.All )]
public Brush WindowBG { get { return _WindowBG; } set { if (value != _WindowBG) { _WindowBG = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("WindowBG")); } } }
Where have I gone astray? And am I even going in the right direction with this method?
End result is I am trying to store theming data for my application into json and have the various brushes be either color, image, or gradient (not yet included).
Upvotes: 3
Views: 791
Reputation: 3135
Ended up going a different route, which IMHO is a lot cleaner of an implementation.
Kept the objects that used to be my go-between objects, but they are now the objects that I actually store the data using and they (for ease of use) inherit a base class;
public class SerializableBrush
{
public virtual Brush ToBrush()
{
return null;
}
}
public class SerializableColorBrush : SerializableBrush
{
public Color Color{ get; set; }
public SerializableColorBrush(SolidColorBrush Brush)
{
this.Color = Brush.Color;
}
public override Brush ToBrush()
{
SolidColorBrush brush = new SolidColorBrush(this.Color);
return brush;
}
}
public class SerializableImageBrush : SerializableBrush
{
public ImageSource ImageSource { get; set; }
public TileMode TileMode { get; set; }
public Stretch Stretch { get; set; }
public AlignmentX AlignmentX { get; set; }
public AlignmentY AlignmentY { get; set; }
public SerializableImageBrush(ImageBrush Brush)
{
this.ImageSource = Brush.ImageSource;
this.TileMode = Brush.TileMode;
this.Stretch = Brush.Stretch;
this.AlignmentX = Brush.AlignmentX;
this.AlignmentY = Brush.AlignmentY;
}
public override Brush ToBrush()
{
ImageBrush brush = new ImageBrush();
brush.ImageSource = ImageSource;
brush.TileMode = TileMode;
brush.Stretch = Stretch;
brush.AlignmentX = AlignmentX;
brush.AlignmentY = AlignmentY;
return brush;
}
}
Then instead of binding the objects directly to a brush in XAML, I created a converter that turned my custom generic objects into brush's (this is where the base class makes things really easy);
public class SerializableBrushToBrush : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (value as Config.SerializableBrush).ToBrush();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Of course my template class that stored the brush info needed to use my new types, I also added a OnDeserialized callback as my template would not show if I populated the Json after the main window showed;
public class Template : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private SerializableBrush _WindowBG = new SerializableColorBrush(SystemColors.AppWorkspaceBrush);
[JsonProperty(TypeNameHandling = TypeNameHandling.All)]
public SerializableBrush WindowBG { get { return _WindowBG; } set { if (value != _WindowBG) { _WindowBG = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("WindowBG")); } } }
[OnDeserialized]
internal void OnDeserializedMethod(StreamingContext context)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(string.Empty));
}
}
And of course finally my window needed to use the new converter;
<Window.Resources>
<local:SerializableBrushToBrush x:Key="SerializableBrushToBrush"/>
</Window.Resources>
<Window.Background>
<Binding Converter="{StaticResource SerializableBrushToBrush}" Path="Template.WindowBG" Source="{x:Static config:Global.store}"/>
</Window.Background>
Now my Json looks nice and clean and easy to manipulate
"Template": {
"WindowBG": {
"$type": "Config.SerializableImageBrush, Config",
"ImageSource": "C:\\Users\\jhebb\\Pictures\\20150805_150241.jpg",
"TileMode": 0,
"Stretch": 1,
"AlignmentX": 1,
"AlignmentY": 1
},
"WindowFG": {
"$type": "Config.SerializableColorBrush, Config",
"Color": "#FF000000"
},
"DeviceBarBG": {
"$type": "Config.SerializableColorBrush, Config",
"Color": "#FFA0A0A0"
},
"DeviceBarFG": {
"$type": "Config.SerializableColorBrush, Config",
"Color": "#FF000000"
},
"WorkspaceBG": {
"$type": "Config.SerializableColorBrush, Config",
"Color": "#FFFFFFFF"
},
"MainTabFG": {
"$type": "Config.SerializableColorBrush, Config",
"Color": "#FF000000"
},
"MenuBG": {
"$type": "Config.SerializableColorBrush, Config",
"Color": "#FFF0F0F0"
},
"MenuFG": {
"$type": "Config.SerializableColorBrush, Config",
"Color": "#FF000000"
}
}
Upvotes: 2