We need to determine, in code, what template would automatically be applied to a bound element, given a specific data type and that element.
We are not looking for a DataTemplateSelector as that's used to tell the UI which template to use for a given object based on custom logic. We're instead asking the UI which template will it use for a given data type and UI element.
In other words, we're looking for whatever WPF would apply based on the templates defined in the resources section of a window, which can be overridden by the resources of a control on that window, which can be overridden by explicitly setting the DataTemplate or providing a DataTemplateSelector directly on that element.
Also, we tried the default implementation of SelectTemplate but that returns null, so we can't go that route either.
A test would be to ask an element with no data templates or selectors defined anywhere in the UI 'How would you display this value?' and hopefully it will return a DataTemplate containing the definition for a TextBlock with the text property set to the ToString method on that object, which is what's displayed by default when nothing else is defined.
Thomas Levesque's untested solution didn't quite work for me but provided a great starting point. In our case the "container" argument is not always in the visual tree so first we walk up the logical tree until we find a visual. That, combined with MarqueIV's excellent suggestion, leads to a fairly simple solution.
The following code is working for me in production. Your mileage may vary. :)
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;
}