如何通过程序检索UI层次结构中特定元素所使用的DataTemplate
本文关键字:元素 DataTemplate 程序 何通过 检索 UI 层次结构 | 更新日期: 2023-09-27 18:29:51
我们需要在代码中确定,给定特定的数据类型和该元素,哪个模板将自动应用于绑定元素。
我们不是在寻找DataTemplateSelector,因为它用于告诉UI基于自定义逻辑为给定对象使用哪个模板。相反,我们询问UI,将哪个模板用于给定的数据类型和UI元素。
换句话说,我们正在寻找基于窗口的resources部分中定义的模板应用的任何WPF,这些模板可以被该窗口上的控件的资源覆盖,可以通过显式设置DataTemplate或直接在该元素上提供DataTemplateSelector来覆盖。
此外,我们尝试了SelectTemplate的默认实现,但它返回null,所以我们也不能走这条路。
测试是询问UI中任何地方都没有定义数据模板或选择器的元素"如何显示此值?"希望它将返回一个DataTemplate,其中包含TextBlock的定义,该对象的文本属性设置为ToString方法,这是在没有定义其他内容时默认显示的内容。
Thomas Levsque的未经测试的解决方案对我来说不太奏效,但它提供了一个很好的起点。在我们的例子中,"容器"参数并不总是在可视化树中,所以首先我们沿着逻辑树走,直到找到可视化的。这一点,再加上MarqueIV的出色建议,导致了一个相当简单的解决方案。
以下代码在生产中对我有效。您的里程数可能有所不同。:)
public static DataTemplate FindTemplateForType(Type dataType, DependencyObject container)
{
var frameworkElement = container as FrameworkElement;
if (frameworkElement != null)
{
var key = new DataTemplateKey(dataType);
var template = frameworkElement.TryFindResource(key) as DataTemplate;
if (template != null)
return template;
}
if (!(container is Visual || container is Visual3D))
{
container = FindClosestVisualParent(container);
return FindTemplateForType(dataType, container);
}
else
{
var parent = VisualTreeHelper.GetParent(container);
if (parent != null)
return FindTemplateForType(dataType, parent);
else
return FindTemplateForType(dataType, Application.Current.Windows[0]);
}
}
public static DependencyObject FindClosestVisualParent(DependencyObject initial)
{
DependencyObject current = initial;
bool found = false;
while (!found)
{
if (current is Visual || current is Visual3D)
{
found = true;
}
else
{
current = LogicalTreeHelper.GetParent(current);
}
}
return current;
}
我想您可以尝试手动复制WPF用于查找适当的DataTemplate
的逻辑,方法是沿着可视化树向上走,并查找具有适当键的资源。以下是一个可能的实现(未经测试):
static DataTemplate FindTemplateForType(Type dataType, DependencyObject container)
{
var frameworkElement = container as FrameworkElement;
if (frameworkElement != null)
{
var template = FindTemplateForType(dataType, frameworkElement.Resources);
if (template != null)
return template;
}
var parent = VisualTreeHelper.GetParent(container);
if (parent != null)
return FindTemplateForType(dataType, parent);
else
return FindTemplateForType(dataType, Application.Current.Resources);
}
static DataTemplate FindTemplateForType(Type dataType, ResourceDictionary resources)
{
var entries =
from DictionaryEntry e in resources
where e.Key is Type && e.Value is DataTemplate
let type = (Type)e.Key
let template = (DataTemplate)e.Value
where dataType.IsAssignableFrom(type)
select template;
var template = entries.FirstOrDefault();
if (template != null)
return template;
foreach(var mergedDic in resources.MergedDictionaries)
{
template = FindTemplateForType(dataType, mergedDic);
if (template != null)
return template;
}
return null;
}
我使用了与karfus相同的方法,并将搜索数据模板添加到baseType如果存在与Type.Basetype 相关的数据模板
<Extension>
Public Function GetDatatemplateForType(container As DependencyObject, dataType As Type) As DataTemplate
Dim dTemplate As DataTemplate = Nothing
Dim currentType As Type = dataType
Do While dTemplate Is Nothing And currentType IsNot Nothing
dTemplate = DataTemplateForType(container, currentType)
currentType = currentType.BaseType
Loop
Return dTemplate
End Function
Private Function DataTemplateForType(Container As DependencyObject, dataType As Type) As DataTemplate
Dim resTemplate As DataTemplate = Nothing
Dim dKey As DataTemplateKey = New DataTemplateKey(dataType)
Dim fm As FrameworkElement = TryCast(Container, FrameworkElement)
If fm IsNot Nothing Then
resTemplate = fm.TryFindResource(dKey)
If resTemplate Is Nothing AndAlso fm.GetVisualParent(Of FrameworkElement) IsNot Nothing Then
Return DataTemplateForType(fm.GetVisualParent(Of FrameworkElement), dataType)
Else
Return resTemplate
End If
End If
Return Nothing
End Function