Neste artigo, irei mostrar
como eu implementei o ContentControl no modelo de dados implícito usando o comportamento anexado
ImplicitContentTemplateBehavior.
Mas, primeiro, vamos falar um
pouco sobre como o WPF busca um data template implícito, dado um conteúdo, para
que possamos copiar esse comportamento no Silverlight.
Tendo um ContentControl com um Content atribuído a uma
instância do tipo Cricle, o WPF olha para o ControlControl.ItemTemplate. No
caso de estar faltando um, e não haver nenhum ItemTemplateSelector, ele muda sua
estratégia, e tenta encontrar um DataTemplate percorrendo toda a árvore
lógica, procurando dentro do dicionário de recursos de cada elemento por um template
que tem o tipo Circle (ou classes base – mas não interfaces!) como chave
(configurada implícita ou explicitamente).
Primeiramente, ele procura nos
recursos do ContentControl e, se não
encontra, ele sobe um nível, e busca no nível pai até que um seja encontrado ou
até que ele alcance o elemento raiz, terminando por procurar no nível de
recursos da Aplicação.
Agora que já temos
uma ideia, vamos dar uma olhada no comportamento anexado ImplicitContentTemplateBehavior.
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
}
}
Como você pode ver, eu
implementei um método OnAttached do comportamento anexado ao criar uma
extensão Binding para conectar a propriedade ContentControl do Content Template (o objeto associado) com o template de dados implícito, usando um
conversor de valor que busca pelo data
template ascendente na árvore.
Sempre que a propriedade do
Content estiver mudando, o método de conversão Convert é chamado. Aqui estou
usando uma classe helper para encontrar o data template.
Vamos dar uma olhada
no helper ImplicitDataTemplateResolver:
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;
}
}
O método Resolve static chama
um método Resolve privado com o novo content. Este chama o InternalResolve com
o nome completo do InternalResolve (estou usando isso como uma chave única, uma
vez que não podemos usar x:Type in Silverlight 4), que, por sua vez, percorre
ascendentemente a árvore Visual usando a classe helper VisualTreeHelper. No caso de o DataTemplate
ser encontrado, ele o retorna. Se não, ele continua procurando até encontrar o
elemento raiz. Então ele procura nos recursos da aplicação como último recurso.
Nota: Para simplificar, eu
não tentei encontrar o DataTemplate para bases de classes, mas sinta-se à
vontade para adicionar se estiver faltando.
Aqui está o código para este exemplo.
?
Texto original disponível em http://blogs.microsoft.co.il/blogs/tomershamam/archive/2011/05/22/implicit-datatemplate-in-silverlight-4-composite-ui-get-ready-for-silverlight-5-part-ii.aspx







