65.9K
CodeProject 正在变化。 阅读更多。
Home

Silverlight 4 复合 UI 中的隐式 DataTemplate – 为 Silverlight 5 做好准备(第 II 部分)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.20/5 (2投票s)

2011年5月24日

CPOL

2分钟阅读

viewsIcon

14507

我如何使用 ImplicitContentTemplateBehavior 附加行为实现了 ContentControl 的隐式 data-template

在这篇文章中,我将展示我是如何使用 ImplicitContentTemplateBehavior 附加行为来实现 ContentControl 隐式数据模板的。

但首先,让我们讨论一下 WPF 如何根据内容搜索隐式数据模板,以便我们可以在 Silverlight 中模拟这种行为。

假设有一个 ContentControl,其 Content 设置为 Circle 类型的实例,WPF 会查看 ControlControl.ItemTemplate。 如果没有找到该模板,并且没有 ItemTemplateSelector,它会改变策略,并尝试通过遍历逻辑树向上查找,在每个元素的资源字典中寻找一个以 Circle 类型(或基类 – 但不是接口!)作为键的模板(显式或隐式设置)。

首先,它会查看 ContentControl 的资源,如果找不到,它会向上移动一个级别,并在父级别搜索,直到找到一个模板或到达根元素,然后通过搜索应用程序级别的资源来结束。

现在我们已经了解了大致思路,让我们来看看 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
    }
}

正如你所看到的,我通过创建一个绑定扩展来实现 ContentControl(关联对象)的内容模板属性与隐式数据模板的绑定,使用一个值转换器来实际向上遍历树来搜索数据模板。

每当 Content 属性发生变化时,都会调用转换器 Convert 方法。 在这里,我使用一个辅助类来查找数据模板。

让我们看看 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;
    }        
}

Resolve static 方法调用一个带有新内容的 Resolve private 方法。 这会调用 InternalResolve 方法,并将内容的完整类型名称传递给它(我使用这个作为唯一的键,因为我们无法在 Silverlight 4 中使用 x:Type),然后它会使用 VisualTreeHelper 辅助类向上遍历视觉树。 如果找到数据模板,它将返回该模板。 否则,继续向上搜索,直到到达根元素。 然后,它将应用程序资源作为最后的手段进行搜索。

注意:为了简单起见,我没有尝试查找为基类定义的数据模板,但如果你需要,可以随时添加。

这里是这个示例的代码。

在我的下一篇文章中,我将展示我是如何实现 ImplicitItemTemplateBehavior 附加行为的,它使用相同的 ImplicitDataTemplateResolver 辅助类,但采用不同的方法。

敬请期待。

© . All rights reserved.