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

May 22, 2011

2 comments

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

But first, lets 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 reach the root element, then finishing by searching at the Application level resources.

So now that we have an idea, lets look at the ImplicitContentTemplateBehavior attached behavior.

Code Snippet
  1. public class ImplicitContentTemplateBehavior : Behavior<ContentControl>
  2. {
  3.     protected override void OnAttached()
  4.     {
  5.         var binding = new Binding("Content")
  6.         {
  7.             Mode = BindingMode.OneWay,
  8.             Source = AssociatedObject,
  9.             Converter = new DataTemplateConverter(AssociatedObject),
  10.         };
  11.  
  12.         BindingOperations.SetBinding(AssociatedObject, ContentControl.ContentTemplateProperty, binding);
  13.             
  14.         base.OnAttached();
  15.     }
  16.  
  17.     private class DataTemplateConverter : IValueConverter
  18.     {
  19.         private ContentControl _contentControl;
  20.  
  21.         public DataTemplateConverter(ContentControl contentControl)
  22.         {
  23.             this._contentControl = contentControl;
  24.         }
  25.  
  26.         #region IValueConverter Members
  27.  
  28.         public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  29.         {
  30.             return ImplicitDataTemplateResolver.Resolve(_contentControl);
  31.         }
  32.  
  33.         public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  34.         {
  35.             throw new NotSupportedException();
  36.         }
  37.  
  38.         #endregion
  39.     }
  40. }

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.

Lets look at the ImplicitDataTemplateResolver helper:

Code Snippet
  1. internal static class ImplicitDataTemplateResolver
  2. {
  3.     internal static DataTemplate Resolve(ContentPresenter contentPresenter)
  4.     {
  5.         return Resolve(contentPresenter, contentPresenter.Content);
  6.     }
  7.  
  8.     internal static DataTemplate Resolve(ContentControl contentControl)
  9.     {
  10.         return Resolve(contentControl, contentControl.Content);
  11.     }
  12.  
  13.     private static DataTemplate Resolve(FrameworkElement contentElement, object content)
  14.     {
  15.         DataTemplate resolvedDataTemplate = null;
  16.         if (content != null)
  17.         {
  18.             resolvedDataTemplate = InternalResolve(contentElement, content.GetType().FullName);
  19.         }
  20.  
  21.         return resolvedDataTemplate;
  22.     }
  23.  
  24.     private static DataTemplate InternalResolve(FrameworkElement element, string contentTypeName)
  25.     {
  26.         if (element == null)
  27.         {
  28.             return TryFindDataTemplate(Application.Current.Resources, contentTypeName);
  29.         }
  30.  
  31.         var dataTemplate = TryFindDataTemplate(element.Resources, contentTypeName);
  32.         if (dataTemplate == null)
  33.         {
  34.             var parent = VisualTreeHelper.GetParent(element) as FrameworkElement;
  35.             dataTemplate = InternalResolve(parent, contentTypeName);
  36.         }
  37.  
  38.         return dataTemplate;
  39.     }
  40.  
  41.     private static DataTemplate TryFindDataTemplate(ResourceDictionary resourceDictionary, string contentTypeName)
  42.     {
  43.         DataTemplate dataTemplate = null;
  44.         if (resourceDictionary.Contains(contentTypeName))
  45.         {
  46.             dataTemplate = resourceDictionary[contentTypeName] as DataTemplate;
  47.         }
  48.  
  49.         return dataTemplate;
  50.     }        
  51. }

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 that a Data Template was found, it returns it. If not, keep searching up until reaching the root element. Then 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 did I implemented the ImplicitItemTemplateBehavior attached behavior, which uses the same ImplicitDataTemplateResolver helper class but with a different approach.

 

Stay tuned.

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

2 comments

  1. TonyJune 9, 2011 ב 10:27

    Hey, Brilliant!

    However, can you give me a tip on the code samples because ImplicitItemTemplateBehavior.cs doesn’t load by default, and when I add it, it has an object new ImplicitDataTemplateResources() which doesn’t exist, I thought it may be a typo for new ImplicitDataTemplateResolver()but that throws errors.

    Basically, what I have done is follow your previous blog, then needed this to implement (my behaviors: instead of your ts:)


    Looking forward to it!

    Kind regards!

    Reply
  2. Tomer ShamamJune 17, 2011 ב 09:57

    Hi Tony,

    Sorry for the late reply. I’m editing new post with the full demo code. Try to use it.

    Reply