是否可以發現數據模板中的所有動態資源 - 在數據模板本身內,還是在將其應用於某個ContentPresenter之後?在DataTemplate中發現DynamicResource綁定
我的想法是讓某種屬性編輯器編輯wpf對象的外觀(可能使用XamlReader動態創建),並顯示 - 對於某個對象,只顯示相應DataTemplate中使用的資源項。
是否可以發現數據模板中的所有動態資源 - 在數據模板本身內,還是在將其應用於某個ContentPresenter之後?在DataTemplate中發現DynamicResource綁定
我的想法是讓某種屬性編輯器編輯wpf對象的外觀(可能使用XamlReader動態創建),並顯示 - 對於某個對象,只顯示相應DataTemplate中使用的資源項。
到目前爲止,我還沒有找到一個「乾淨」的方式來使用框架方法來獲取動態資源。
但是,有一種可能性是使用反射搜索DataTemplate。動態資源存儲在類型爲System.Windows.ChildValueLookup
的結構中,其中LookupType是「資源」。我寫了一個輔助類來枚舉資源:
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;
}
}
}
}
請看Snoop實用程序。您可以查看源代碼並查看如何查看任何對象的樣式/模板並更改其外觀。
史努比顯示我的價值來源唯一的「ParentTemplate」,這是DependencyPropertyHelper.GetValueSource(..)的結果。但是,我沒有看到將DynamicResourceExtension附加到屬性的方法... – LionAM