How can you programmatically get the DataTemplate used for a specific element in a user interface hierarchy?

We need to determine in the code which template will be automatically applied to the related element, given the specific data type and this element.

We are not looking for a DataTemplateSelector, like the one used to indicate the user interface which template to use for this object based on user logic. Instead, we ask the user interface which template it will use for a particular data type and interface element.

In other words, we are looking for any WPF applied based on templates defined in the resource section of a window that can be overridden by the resources of a control in that window, which can be overridden by explicitly setting a DataTemplate or providing a DataTemplateSelector directly on that element.

In addition, we tried the default implementation of SelectTemplate, but this returns null, so we cannot go this route either.

The test should be to set the item without data patterns or selectors defined anywhere in the UI "How would you display this value?" and hopefully it will return a DataTemplate containing the definition for the TextBlock, with the text property set to the ToString method on this object, which is displayed by default when nothing is defined.

+4
source share
3 answers

Thomas Levesque's unread decision didn't work for me, but it provided an excellent starting point. In our case, the argument "container" is not always in the visual tree, so first we go to the logical tree until we find the visual one. This, combined with the excellent MarqueIV offer, leads to a fairly simple solution.

The following code works for me in production. Your mileage may vary. :)

public static DataTemplate FindTemplateForType(Type dataType, DependencyObject container) { var frameworkElement = container as FrameworkElement; if (frameworkElement != null) { DataTemplateKey key = new DataTemplateKey(dataType); var template = frameworkElement.FindResource(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; } 
+1
source

I think you can try to manually reproduce the logic that WPF uses to find the appropriate DataTemplate by going through the visual tree and looking for the resource with the corresponding key. Here's a possible implementation (untested):

 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; } 
0
source

I used the same path as karfus and added a search database in baseType in case there is a corresponding data table for 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 
0
source

Source: https://habr.com/ru/post/1499853/


All Articles