Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Implicit DataTemplate in Silverlight 4 Composite UI – Get Ready for Silverlight 5 (Part II)

0.00/5 (No votes)
24 May 2011 1  
How I implemented the ContentControl implicit data-template using the ImplicitContentTemplateBehavior attached behavior

In this post, I'll show how I implemented the ContentControl implicit data-template using the ImplicitContentTemplateBehavior attached behavior.

But first, let's talk a bit about how WPF searches for an implicit data template, given a content, so we can mimic that behavior in Silverlight.

Having a ContentControl with a Content set to an instance of type Circle, WPF looks at the ControlControl.ItemTemplate. In case that one is missing, and there is no ItemTemplateSelector, it changes strategy, and tries finding a Data Template by traversing upward the logical-tree, looking inside each element resource dictionary for a template which has the type of Circle (or base classes – but not interfaces!) as the key (implicitly or explicitly set).

First, it looks at the ContentControl resources, if not found, it goes one level up, and searches at the parent level until one is found or until it reaches the root element, then finishes by searching at the Application level resources.

So now that we have an idea, let's look at the ImplicitContentTemplateBehavior attached behavior.

public class ImplicitContentTemplateBehavior : Behavior<ContentControl>
{
    protected override void OnAttached()
    {
        var binding = new Binding("Content")
        {
            Mode = BindingMode.OneWay,
            Source = AssociatedObject,
            Converter = new DataTemplateConverter(AssociatedObject),
        };
 
        BindingOperations.SetBinding
        (AssociatedObject, ContentControl.ContentTemplateProperty, binding);
            
        base.OnAttached();
    }
 
    private class DataTemplateConverter : IValueConverter
    {
        private ContentControl _contentControl;
 
        public DataTemplateConverter(ContentControl contentControl)
        {
            this._contentControl = contentControl;
        }
 
        #region IValueConverter Members
 
        public object Convert(object value, Type targetType, 
        object parameter, System.Globalization.CultureInfo culture)
        {
            return ImplicitDataTemplateResolver.Resolve(_contentControl);
        }
 
        public object ConvertBack(object value, Type targetType, 
        object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotSupportedException();
        }
 
        #endregion
    }
}

As you can see, I've implemented the OnAttached method of the attached behavior by creating a Binding extension to bind the ContentControl (the associated object) Content Template property with the implicit data template, using a value converter which actually searches for the data template upward the tree.

Whenever the Content property is changing, the converter Convert method is called. Here, I'm using a helper class to find the data template.

Let's look at the ImplicitDataTemplateResolver helper:

internal static class ImplicitDataTemplateResolver
{
    internal static DataTemplate Resolve(ContentPresenter contentPresenter)
    {
        return Resolve(contentPresenter, contentPresenter.Content);
    }
 
    internal static DataTemplate Resolve(ContentControl contentControl)
    {
        return Resolve(contentControl, contentControl.Content);
    }
 
    private static DataTemplate Resolve(FrameworkElement contentElement, object content)
    {
        DataTemplate resolvedDataTemplate = null;
        if (content != null)
        {
            resolvedDataTemplate = InternalResolve(contentElement, content.GetType().FullName);
        }
 
        return resolvedDataTemplate;
    }
 
    private static DataTemplate InternalResolve(FrameworkElement element, string contentTypeName)
    {
        if (element == null)
        {
            return TryFindDataTemplate(Application.Current.Resources, contentTypeName);
        }
 
        var dataTemplate = TryFindDataTemplate(element.Resources, contentTypeName);
        if (dataTemplate == null)
        {
            var parent = VisualTreeHelper.GetParent(element) as FrameworkElement;
            dataTemplate = InternalResolve(parent, contentTypeName);
        }
 
        return dataTemplate;
    }
 
    private static DataTemplate TryFindDataTemplate
    (ResourceDictionary resourceDictionary, string contentTypeName)
    {
        DataTemplate dataTemplate = null;
        if (resourceDictionary.Contains(contentTypeName))
        {
            dataTemplate = resourceDictionary[contentTypeName] as DataTemplate;
        }
 
        return dataTemplate;
    }        
}

The Resolve static method, calls a Resolve private method with the new content. This calls the InternalResolve with the content full type name (I'm using this as a unique key since we can't use x:Type in Silverlight 4), which in turn, traverse upward the Visual Tree using the VisualTreeHelper helper class. In case a Data Template was found, it returns it. If not, keep searching up until reaching the root element. Then it looks at the Application resources as last resort.

Note: For simplicity, I didn't try to find Data Template defined for base classes, but feel free to add that if missing.

Here is the code of this sample.

In my next post, I'll show how I implemented the ImplicitItemTemplateBehavior attached behavior, which uses the same ImplicitDataTemplateResolver helper class, but with a different approach.

Stay tuned.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here