是否可以在数据模板中发现所有动态资源 - 无论是在数据模板本身内,还是在将其应用于某些 ContentPresenter 之后?
我的想法是制作某种属性编辑器来编辑 wpf 对象的外观(可能使用 XamlReader 动态创建)并显示 - 对于某个对象,只有在相应 DataTemplate 中使用的资源条目。
是否可以在数据模板中发现所有动态资源 - 无论是在数据模板本身内,还是在将其应用于某些 ContentPresenter 之后?
我的想法是制作某种属性编辑器来编辑 wpf 对象的外观(可能使用 XamlReader 动态创建)并显示 - 对于某个对象,只有在相应 DataTemplate 中使用的资源条目。
请查看Snoop实用程序。您可以查看源代码并了解如何查看任何对象的样式/模板并更改其外观。
到目前为止,我还没有找到使用框架方法获取动态资源的“干净”方法。
但是,一种可能性是使用反射搜索 DataTemplate。动态资源存储在System.Windows.ChildValueLookup
LookupType 为“Resource”的类型的结构中。我编写了一个帮助类来枚举资源:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Windows;
namespace DashboardTest
{
public struct ResourceInfo
{
public ResourceInfo(string resourceKey, DependencyProperty property)
: this()
{
ResourceKey = resourceKey;
Property = property;
}
public string ResourceKey { get; private set; }
public DependencyProperty Property { get; set; }
}
public static class DataTemplateHelper
{
private static readonly Type ChildValueLookupType = GetType("System.Windows.ChildValueLookup");
private static readonly FieldInfo LookupTypeField = ChildValueLookupType.GetField("LookupType", BindingFlags.Instance | BindingFlags.NonPublic);
private static readonly FieldInfo ValueField = ChildValueLookupType.GetField("Value", BindingFlags.Instance | BindingFlags.NonPublic);
private static readonly FieldInfo PropertyField = ChildValueLookupType.GetField("Property", BindingFlags.Instance | BindingFlags.NonPublic);
private static readonly object LookupTypeResource = Enum.Parse(LookupTypeField.FieldType, "Resource");
public static List<ResourceInfo> FindDynamicResources(DataTemplate template)
{
var recordField = typeof(DataTemplate).GetField("ChildRecordFromChildIndex", BindingFlags.Instance | BindingFlags.NonPublic);
Debug.Assert(recordField != null);
var values = EnumerateObjects(recordField.GetValue(template)).Where(IsDynamicResource).Select(ToResourceInfo).ToList();
return values;
}
private static ResourceInfo ToResourceInfo(object lookup)
{
return new ResourceInfo((string)ValueField.GetValue(lookup), (DependencyProperty)PropertyField.GetValue(lookup));
}
private static Type GetType(string typeName)
{
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
Type type = assembly.GetType(typeName);
if (type != null)
return type;
}
return null;
}
private static bool IsDynamicResource(object obj)
{
if (obj == null || obj.GetType() != ChildValueLookupType) return false;
return LookupTypeResource.Equals(LookupTypeField.GetValue(obj));
}
private static IEnumerable<object> EnumerateObjects(object obj)
{
var visited = new HashSet<object>();
EnumerateObjectsInternal(obj, visited, 20);
return visited;
}
private static void EnumerateObjectsInternal(object obj, HashSet<object> visited, int maxDepth)
{
if (obj == null || maxDepth <= 0) return;
var type = obj.GetType();
if (obj is Type || obj is string || obj is DateTime || type.IsPrimitive
|| type.IsEnum || visited.Add(obj) == false) return;
var array = obj as Array;
if (array != null)
{
foreach (var item in array)
{
EnumerateObjectsInternal(item, visited, maxDepth - 1);
}
}
else
{
foreach (var field in GetAllFields(type))
{
var fieldValue = field.GetValue(obj);
EnumerateObjectsInternal(fieldValue, visited, maxDepth - 1);
}
}
}
private static IEnumerable<FieldInfo> GetAllFields(Type type)
{
const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly;
if (type == typeof(object) || type.BaseType == typeof(object))
{
return type.GetFields(bindingFlags);
}
else
{
var fieldInfoList = new List<FieldInfo>();
var currentType = type;
while (currentType != typeof(object))
{
fieldInfoList.AddRange(currentType.GetFields(bindingFlags));
currentType = currentType.BaseType;
}
return fieldInfoList;
}
}
}
}