WP7 SurviveTombstone and BindableCollection

Oct 29, 2010 at 5:37 PM

Is there a straightforward way to persist the Items collection of a BindableCollection using the SurviveTombstone process? I love that mechanism and it seems like a very elegant way to handle dealing with that process. However, it doesn't seem to pick up the items in the collection. For instance, I have a property that looks like this:

private BindableCollection<ArticleMetadata> articles = new BindableCollection<ArticleMetadata>();
        [SurviveTombstone]
        public BindableCollection<ArticleMetadata> Articles
        {
            get
            {
                return articles;
            }
            set
            {
                articles = value;
                NotifyOfPropertyChange(() => Articles);
            }
        }

After a Resurrect, the collection is always empty. The SurviveTombstone process hooks other properties in the VM and in watching a trace, it seems to pick the colleciton itself just not the items.

Thought I would ask to see if I'm missing something before I start hacking around or roll my own solution for this type of scenario.

Thanks in advance!
brian

Oct 29, 2010 at 10:06 PM

As far I understood, the SurviveTombstone attribute is not a full serialization mechanism. 
It is meant to restore discrete properties scattered over the ViewModel object graph; the properties, however, should be decorated individually and the target graph should be already fully istantiated.
I guess these choices were made to force the user to be *very* explicit on what to put in the persisted app state, and to discourage using it as a persistance storage. 

I attempted some customization of the attribute to accomplish some small serialization. 
To be honest, it was just a little spike, and I didn't investigate all edge case.
Anyway, this is what I did:

        public class TombstoneSerializable: SurviveTombstone
	{
		protected override bool IsComplexType(Type type)
		{
			return false;
		}
	}
	
	//example:
	public class MyViewModel {
		
		[TombstoneSerializable]
		public MySubject Subject {get;set;}
	}

This caused CM to think that the object exposed by "Subject" property is a "simple" object, thus allowing the whole MySubject instance to be stored in the state dictionary. 
Note that you have to ensure that MySubject is automatically serializable (it has to be a very simple POCO, or you have to decorate it with [DataContract] and [DataMember], which I found very ugly).

Let me know if it works for your scenario.

Nov 4, 2010 at 10:24 PM

Thanks for the explanation and thoughts! For the time being, I'm testing an extended version of the SurviveTombstone attribute that looks for IObservableCollection instances and attempts to serialize and then restore items from the collection. It requires at least the assumption that the items are a DataContract but in my scenario that's a safe assumption as all of the object I'm trying to preserve were just retrieved via a REST based service anyway. For what it's worth, here's my current solution that I'm testing - if anyone sees any significant pitfalls to this approach, I'd love ot hear them!

    public class SurviveTombstoneEx: SurviveTombstone
    {
        #region Overrides

        public override void Tombstone(IPhoneService phoneService, object owner, System.Reflection.PropertyInfo property, object value, string rootKey)
        {
            if (value.GetType().IsImplementationOf(typeof(IObservableCollection<>)))
            {
                int count = 0;
                foreach (var item in ((ICollection)value))
                {
                    count += 1;
                    string key = string.Format("{0}|{1}", rootKey, count);
                    phoneService.State[key] = item;
                }
                phoneService.State[string.Format("{0}|TotalCount", rootKey)] = count;
            }
            else
            {
                base.Tombstone(phoneService, owner, property, value, rootKey);    
            }            
        }

        public override void Resurrect(IPhoneService phoneService, object owner, System.Reflection.PropertyInfo property, object value, string rootKey)
        {
            if (value.GetType().IsImplementationOf(typeof(IObservableCollection<>)))
            {
                base.Resurrect(phoneService, owner, property, value, rootKey);
                
                int totalCount = Convert.ToInt32(phoneService.State[string.Format("{0}|TotalCount", rootKey)]);
                for(int i = 1; i<=totalCount; i++)
                {
                    object item = phoneService.State[string.Format("{0}|{1}", rootKey, i)];
                    ((IList) value).Add(item);
                }
            }
            else
            {
                base.Resurrect(phoneService, owner, property, value, rootKey);
            }
        }

        #endregion
    }
where IsImplementationOf is a simple Extension method as follows:
public static bool IsImplementationOf(this Type baseType, Type interfaceType)
        {
            bool isMatch = baseType.GetInterfaces().Any(x =>
                                                      x.IsGenericType &&
                                                      x.GetGenericTypeDefinition() == interfaceType);
            return isMatch;
        }