4

我们需要在代码中确定在给定特定数据类型和该元素的情况下,将自动将哪个模板应用于绑定元素。

我们不是在寻找 DataTemplateSelector,因为它用于根据自定义逻辑告诉UI 为给定对象使用哪个模板。相反,我们询问UI它将为给定的数据类型和 UI 元素使用哪个模板。

换句话说,我们正在寻找基于窗口资源部分中定义的模板应用的任何 WPF,该模板可以被该窗口上控件的资源覆盖,可以通过显式设置 DataTemplate 或直接在该元素上提供 DataTemplateSelector。

此外,我们尝试了 SelectTemplate 的默认实现,但它返回 null,所以我们也不能走那条路。

一个测试是询问一个在 UI 中任何地方都没有定义数据模板或选择器的元素“你将如何显示这个值?” 并希望它会返回一个包含 TextBlock 定义的 DataTemplate,其中 text 属性设置为该对象的 ToString 方法,这是在未定义其他任何内容时默认显示的内容。

4

3 回答 3

1

Thomas Levesque 的未经测试的解决方案对我来说不太适用,但提供了一个很好的起点。在我们的例子中,“容器”参数并不总是在视觉树中,所以首先我们沿着逻辑树走,直到找到视觉树。这与 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;
}
于 2015-11-02T23:11:01.117 回答
0

我想您可以尝试手动重现 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;
}
于 2013-08-31T10:23:17.017 回答
0

我使用与 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
于 2016-12-10T17:29:58.003 回答