在DataTemplate中发现DynamicResource绑定

时间:2014-02-12 10:38:06

标签: c# wpf datatemplate dynamicresource

是否有可能发现datatemplate中的所有动态资源 - 在datatemplate本身内,还是在应用于某些ContentPresenter之后?

我的想法是创建某种属性编辑器来编辑wpf对象的外观(可能使用XamlReader动态创建)并显示 - 对于某个对象,只显示相应DataTemplate中使用的资源条目。

2 个答案:

答案 0 :(得分:1)

请查看Snoop实用程序。您可以查看源代码,了解如何查看任何对象的样式/模板并更改其外观。

答案 1 :(得分:0)

到目前为止,我还没有找到一种“干净”的方法来使用框架方法获取动态资源。

然而,一种可能性是使用反射搜索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;
            }
        }
    }
}