Ok, I did it: I tried to do it the easy way — serialization! And I quickly ran into the following issue: Serializing DependencyObjects. There are a lot of sources saying “you can’t serialize dependency objects”, which is true, but you can get pretty close rather quickly and it might not be the worst decision.
In this article, we’ll explore if and how reflection magic can assist us in doing it anyway, how we can locate DependencyProperties via reflection and what pitfalls remain. Moreover, I’ll give an outlook why the most desireable solution (that almost suggests itself) fails.
Using DPs in your business logic layer is a disputable decision. The application I work on has very unsual demands and I am still happy with the decision I made. I don’t want to discuss that here, though, since it is out of scope.
The Issue
When serializing objects, you can prevent certain properties from being serialized using the [NonSerialized] property, but if the base class is not marked as [Serializable], such as the DependencyObject class, attempting to serialize will throw an exception.
However, implementing ISerializable manually does the job:
[Serializable]
public class SerializableDependencyObject : DependencyObject, ISerializable
{
// Note that this does not call base(info, context) since the base class does not have
// a deserialization constructor
public DagNode(SerializationInfo info, StreamingContext context)
{ }
// again, the base class is not an ISerializable
public virtual void GetObjectData(SerializationInfo info,
StreamingContext context)
{}
// ...
}
What bugs me, however, is that now, all derived classes need to implement GetObjectData and the deserialization constructor explicitly, producing both stupid and error-prone code:
public class StupidClass : SerializableDependencyObject
{
#region ISerializable Members
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("PropertyA", mPropertyA);
info.AddValue("PropertyB", mPropertyB);
info.AddValue("PropertyC", mPropertyC);
}
public StupidClass (SerializationInfo info, StreamingContext context)
{
mPropertyA = info.GetDouble("PropertyA");
mPropertyB = info.GetDouble("PropertyB");
mPropertyC = info.GetDouble("PropertyC");
}
#endregion
}
We obviously don’t want this, since it produces a lot of manually written, error-prone code that needs to be adjusted constantly when code changes are made – a lot of work when you consider we’re not necessarily adding functionality yet.
One Step Back: .NET Serialization
It took me some time to realize that .NET has quite a number of techniques aimed at serialization. The process is described quite well at http://msdn.microsoft.com/en-us/library/tyf8zbfk.aspx, but it is still simplified since there are serialization attributes introduced in .NET 2.0 that allow you to hook up additional methods during the serialization/deserialization process:
“If the class being deserialized implements the IDeserilizationCallback, the OnDeserialization method is automatically called when the entire object graph has been deserialized.” (see http://msdn.microsoft.com/en-us/library/ty01×675(VS.71).aspx)
These concepts are somewhat different and allow you to control the process of serialization and deserialization to a certain degree.
You might be tempted to ask: “Why should I worry about .NET serialization if it is this complicated – isn’t it easier to do it myself?”, but the answer is probably “no”, since the BinaryFormatter really solves all the itty-gritty problems for you in a convenient manner. These are:
- Solving dependency chains and circular links
- Creating unique identifier and associating them w/ object instances
- Serializing generic containers
- Constructing objects on de-serialization and re-assigning pointers
All this does not help in serializing DependencyProperties, however, since your owning class is derived from DependencyObject, that is not marked as [Serializable] – unless you want to do it manually in each derived class.
An intelligent base class
How about a very intelligent base class that uses reflection to mimick the behaviour we’d expect?
We’d certainly need a class that is responsible for this behaviour, we might want to call it SerializableDependencyObject again:
[Serializable]
public class SerializableDependencyObject : DependencyObject, ISerializable
{
}
Now, the class needs to implement ISerializable in a sufficient manner, that is, in such way that derived classes behave just like normal serializable classes:
- The value of
DependencyPropertiesis being stored and restored automatically DependencyPropertiesmarked as[NonSerialized]are not being serialized
The GetObjectData method
Let’s try to fetch all fields and properties from the current object instance (this) in the base class. We want to add all these fields to the SerializationInfo instance:
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
PropertyDescriptorCollection descriptors = TypeDescriptor.GetProperties(GetType(),
new Attribute[] {
new PropertyFilterAttribute(PropertyFilterOptions.SetValues |
PropertyFilterOptions.UnsetValues |
PropertyFilterOptions.Valid ) });
List<FieldInfo> fieldInformation =
ReflectionHelper.GetFieldInformation(GetType());
foreach (FieldInfo fiRover in fieldInformation)
{
object[] attribs =
fiRover.GetCustomAttributes(typeof(NonSerializedAttribute), false);
if (attribs.Length > 0)
continue; // Don't serialize this
info.AddValue(fiRover.Name, fiRover.GetValue(this));
}
foreach (PropertyDescriptor propertyDescriptor in descriptors)
{
if (propertyDescriptor.Name == "Dispatcher" ||
propertyDescriptor.Name == "DependencyObjectType" ||
propertyDescriptor.Name == "IsSealed" )
continue;
SerializeProperty(info, propertyDescriptor);
}
}
The first loop of the method will serialize all fields, resembling the functionality we have when not implementing ISerializable manually. The second part, adding functionality, cares for DependencyProperties. The ReflectionHelper is just a simple wrapper around the System.Type.GetFields() method with some performance improvements and minor tweaks.
Let’s have a look at the serialization constructor of SerializableDependencyObject:
public SerializableDependencyObject(SerializationInfo info,
StreamingContext context)
{
Type thisType = GetType();
// De-Serialize Fields
List<FieldInfo> fieldInformation =
ReflectionHelper.GetFieldInformation(thisType);
foreach (FieldInfo fiRover in fieldInformation)
{
object[] attribs =
fiRover.GetCustomAttributes(typeof(NonSerializedAttribute), false);
if (attribs.Length > 0)
continue; // Don't try to deserialize this
fiRover.SetValue(this, info.GetValue(fiRover.Name, fiRover.FieldType));
}
// De-Serialize DependencyProperties
PropertyDescriptorCollection descriptors = TypeDescriptor.GetProperties(thisType,
new Attribute[] {
new PropertyFilterAttribute(PropertyFilterOptions.SetValues |
PropertyFilterOptions.UnsetValues |
PropertyFilterOptions.Valid) });
foreach (PropertyDescriptor propertyDescriptor in descriptors)
{
if (propertyDescriptor.Name == "Dispatcher" ||
propertyDescriptor.Name == "DependencyObjectType" ||
propertyDescriptor.Name == "IsSealed")
continue;
DependencyProperty dp =
DependencyPropertyHelper.FindDependencyProperty(this, propertyDescriptor.Name);
if (null != dp)
SetValue(dp, info.GetValue(propertyDescriptor.Name, propertyDescriptor.PropertyType));
else
throw new SerializationException(String.Format(@"Failed to deserialize
property '{0}' on object of type '{1}'. Property could not be found.
Version Conflict?",
propertyDescriptor.Name, thisType));
}
}
Again, the first loop handles simple fields while the second part adds functionality for DependencyProperties. One line in the second part deserves some attention:
DependencyProperty dp =
DependencyPropertyHelper.FindDependencyProperty(this, propertyDescriptor.Name);
Remember DependencyProperties are static members which are registered in some application-wide registry, for example:
public static DependencyProperty NameProperty =
DependencyProperty.Register("Name",
typeof(string),
typeof(GraphNode),
new PropertyMetadata(String.Empty));
Unfortunately, there is no DependencyProperty.Find() method. That is where the DependencyPropertyHelper comes into play:
class DependencyPropertyHelper
{
/// <summary>
/// Finds a dependency property on an object by its name.
///
/// Motivation: There is no accesible DP registry. When creating DPs, one must register them using
/// DependencyProperty.Register(), but there is no DependencyProperty.FindProperty()
/// </summary>
/// <param name="_object">The object that supposedly has a property of the supplied name</param>
/// <param name="_propertyName">The name of the property to find, e.g. "Height". NOTE: The corresponding property is expected to be named "HeightProperty" in this case.</param>
/// <returns></returns>
public static DependencyProperty FindDependencyProperty(object _object, string _propertyName)
{
if (null != _object && !String.IsNullOrEmpty(_propertyName))
{
Type typeAttachedTo = _object.GetType();
FieldInfo fi = null;
while (null == fi && typeAttachedTo != null)
{
fi = typeAttachedTo.GetField(_propertyName + "Property");
typeAttachedTo = typeAttachedTo.BaseType;
}
if (null != fi)
{
return fi.GetValue(null) as DependencyProperty;
}
}
return null;
}
}
First results
At this point, we can supply a base class SerializableDependencyObject that allows simple serialization of DependencyObjects with their associated DependencyProperties. However, quite a number of dangerous pitfalls remain:
- Setting
DependencyValuescan be problematic: We might trigger callbacks (PropertyChangedCallbacks,CoerceValueCallback) - Triggered callbacks might rely on yet unitialized data
- We rely on the “Property” naming convention (e.g. the DP of property
"Name"is called"NameProperty") - You need to define a deserialization constructor for each and every derived class which needs to call the base class’ deserialization constructor. Failing to do so throws a run-time exception.
- The current code does not handle versioning
Outlook
Serializing dependency properties is indeed cumbersome. However, INotifyPropertyChanged might not be an alternative, and reinventing the wheel might not be desirable either. While basic functionality of DependencyProperties is probably quickly implemented, there are some obvious drawbacks, first and foremost the integration with other components that simply require a DependencyObject to work with.
Synchronizing a complex DataModel with sets of equally complex ViewModels can be a pain, too, so for those having such unusual demands, it’s their time to choose their poison.
In the second part, we’ll have a look at the ISerializationSurrogate interface and why we can’t implement it the same way we implemented the SerializableDependencyObject. We will also make the acquaintance of an infamous InvalidOperationException: “Current local value enumeration is outdated because one or more local values have been set since its creation.” which esentially keeps us (and probably microsoft) from implementing it just the way we did in here.


Interesting article. Unfortunately, you don’t provide full source code or a demo for. So, the question is Can you provide code for all referenced methods, classes? Also, have you published part II of the article.
Thank you, anyway!
I agree with David