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






4.20/5 (2投票s)
我如何使用 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
辅助类,但采用不同的方法。
敬请期待。