Search code examples
c#.netwpfserializationbrush

How to serialize a Windows.Media.Brush


The Windows.Media.Brush is not a serializable class so I am wondering how to go about serializing it.

I am putting the [Serializable] attribute on the class itself and [DataContract] as well along with [DataMember(Name = "PropertyName")] on each property so it looks something like this:

[Serializable]
[DataContract]
public class ClassName: SerializeableBase<ClassName>
{
    // Color
    [DataMember(Name = "Color")]
    private Brush _color;
    public Brush Color
    {
        get { return _color; }
        set
        {
            _color = value;
        }
    }
}

My first thought is that I serialize a hex string which I can then convert back to be a Brush. This would work if I could convert the Brush to a hex as well so that I can update the string before serializing it as well as retrieve the color after deserializing the string. Can one convert the Brush to get a hex string out of it? Or is there a better way of serializing this class?


Solution

  • To serialize an unserializable property it is best to use a helper property that is serializable and do a conversion between the two. You have to write the code to sync both properties together so they are updated by the time of serialization. This can be done through the setter. Remember to add the NonSerialized attribute to the Brush and any property that is not serializable.

    using System;
    using System.Runtime.Serialization;
    using WindowsMedia = System.Windows.Media;
    
    namespace Something.Something.DarkSide
    {
        [NonSerialized]
        private readonly WindowsMedia.BrushConverter _colorConverter = new WindowsMedia.BrushConverter();
    
        [Serializable]
        [DataContract]
        public class ClassName: SerializeableBase<ClassName>
        {
            [DataMember(Name = "ColorString")]
            private string _colorString;
            public string ColorString
            {
                get { return _colorString; }
                set
                {
                    _colorString = value;
                    _color = (WindowsMedia.Brush)_colorConverter.ConvertFrom(value);
                    OnPropertyChanged();
                }
            }
    
            // Color
            [NonSerialized]
            private WindowsMedia.Brush _color = WindowsMedia.Brushes.Yellow;
            public WindowsMedia.Brush Color
            {
                get { return _color; }
                set
                {
                    _color = value;
                    _colorString = _colorConverter.ConvertToString(value);
                    OnPropertyChanged();
                }
            }
    
            // This triggered when deserializing.
            // When deserializing we will have the _color property as null since
            // nothing is setting it. 
            // This ensures we initialize the _color when deserializing from the ColorString property.
            [OnDeserialized]
            private void SetValuesOnDeserialized(StreamingContext context)
            {
                _colorConverter = new WindowsMedia.BrushConverter();
                _color = (WindowsMedia.Brush)_colorConverter.ConvertFrom(ColorString);
            }
    
            public Annotation(string colorHexValue = null)
            {
                var colorBrush = (WindowsMedia.Brush)_colorConverter.ConvertFrom(colorHexValue);
                Color = colorBrush ?? WindowsMedia.Brushes.Yellow;
            }
    
            public Annotation(WindowsMedia.Brush colorBrush = null)
            {
                Color = colorBrush ?? WindowsMedia.Brushes.Yellow;
            }
        }
    }
    

    *NOTE: When loading the serialized file the Color will be null and the constructor will not be called. You must then make a new instance of the object and re-construct it using the loaded properties so that it will set the Color from the ColorString. You may also create a helper method in the class and call it on the property to trigger this change, but do remember that it does NOT happen on serialization load time.

    var className = ClassName.LoadFromXmlFile(filePath);
    // We use the ColorString property because that's what we get after loading
    // the serialized file. The Color will be null at this point.
    className = new ClassName(className.ColorString);
    

    I've had a little problem trying to load it back so I used this:

    public static SerializableType LoadFromXmlFile(string filename)
    {
        using (var stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read, 1024))
        {
            using (var reader = XmlDictionaryReader.Create(stream))
            {
                var serializer = new DataContractSerializer(typeof(SerializableType));
    
                return (SerializableType)serializer.ReadObject(reader);
            }
        }
    }