Markup Extensions (ME) are the bees knees. Xaml parsers use them to compute the values assigned to properties when constructing object trees. StaticResource for example in an extension that provides an value from a resource dictionary given a key.
There is one downside to MEs, they are evaluated once at parse time (slightly false, but true enough for now). Which means if the computation takes a long time it can hold up constructing your object tree. Not a good thing - especially if it prevents your application from loading quickly. Obviously it would be nice to evaluate the ME out of band and defer providing a value. One really useful scenario for deferred ME's is getting images downloaded, decoded and frozen in a worker thread to prevent stutter in you application UI.
Well there's some hope. Althought not explicitly designed to work this way it is possible to defer ME values in a supported way. The secret is to take advantage of some of the services passed to the markup extension in its ProvideValue callback.
The IProvideValueTarget service contains information about the object and property that will recieve the ME result. You defer ProvideValue by caching this information and using it to apply the value when it's ready.
Code wise this looks something like
public abstract class DeferredMarkupExtension<T> : MarkupExtension
{
public sealed override object ProvideValue(IServiceProvider serviceProvider)
object result = default(T);
if (serviceProvider != null)
IProvideValueTarget provideValueTarget = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
if (provideValueTarget != null)
object targetObject = provideValueTarget.TargetObject;
object targetProperty = provideValueTarget.TargetProperty;
// Schedule some background worker to set targetObject.targetProperty
// when value is ready
DeferProvideValue(targetObject, targetProperty);
}
return result;
WPF's Xaml parser usualy passes targetObject, targetProperty pairs of type
The one remaining kink in this whole scheme is Templates. When WPF templates are parsed the template is sealed to prevent modification. This allows WPF to share as much of the template as possible across all expanded instances of the template. Template sealing and sharing has some implications
Remember when I told you I was lying earlier about ME evaluation? The truth is that you can get an ME to evaluate more than once. While template parsing if you return an ME as the result of ProvideValue that ME will be evaluated again every time the template is expanded.
You can deal with the templates corner case with the following heuristic -
If the targetProperty is not a known supported type then you are probably in a Template. Return *this* to cache your ME in the template and later its ProvideValue will be called again during template expansion.
A library with a complete implementation and a full demo application are available at codeplex http://ifeanyie.codeplex.com/SourceControl/BrowseLatest. The application contains an animation and performs a Bing image search of up to 50 image results. The animation stutters when using the built-in WPF Uri to ImageSource converter but is smooth when using the AsyncImageSourceExtension.
The library is in Projects\Libraries\DeferredMarkupExtension
The sample application in is Projects\Applications\DeferredMarkupExtensionSample
This posting is provided "AS IS" with no warranties, and confers no rights.