0

是否可以在数据模板中发现所有动态资源 - 无论是在数据模板本身内,还是在将其应用于某些 ContentPresenter 之后?

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

4

2 回答 2

1

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

于 2014-02-12T10:53:36.737 回答
0

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

但是,一种可能性是使用反射搜索 DataTemplate。动态资源存储在System.Windows.ChildValueLookupLookupType 为“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;
            }
        }
    }
}
于 2014-02-13T07:05:14.810 回答