XAML 获取属性c#的DisplayName

ghhkc1vu  于 2023-09-28  发布在  C#
关注(0)|答案(2)|浏览(126)

我目前正在尝试获取XAML(WPF)中的属性的显示名称,并试图实现this question "Access DisplayName in xaml"的答案。
为了测试这一点,我创建了一个带有属性和DisplayAttribute的类(我对.resx文件使用本地化来获取本地化的字符串),

public partial class Employee
    {
        public Employee()
        {

        }

        private string? firstName;

        [Display(Name = nameof(Strings.firstName), ResourceType = typeof(Strings))]
        public string? FirstName
        {
            get => firstName;
            set => SetProperty(ref firstName, value, true);
        }

修改了另一个问题中的MarkupExtension以允许null值,

internal class DisplayNameExtension : MarkupExtension
    {
        public Type Type { get; set; }

        public string PropertyName { get; set; }

        public DisplayNameExtension() { }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            if (Type.GetProperty(PropertyName) is PropertyInfo prop)
            {
                if (prop.GetCustomAttributes(typeof(DisplayAttribute), false).FirstOrDefault() is DisplayAttribute attribute && !string.IsNullOrEmpty(attribute.Name))
                {
                    return attribute.Name;
                }
            }
            return string.Empty;
        }
    }

并试图用以下方式来描述:

<TextBlock Text="{local:DisplayName PropertyName=FirstName, Type=model:Employee}"/>

Employee是另一个项目的一部分,引用是正确的)
现在,当我停止应用程序并在调用方法时查看prop属性时,确定了正确的属性,但即使我设置了DisplayAttribute,这里也不存在任何属性。使用调试器查看内部prop:

我在这里错过了一些显而易见的东西,还是获得属性的解决方案有点复杂?

**编辑:**感谢@EldHasp指出我需要查看CustomAttributes而不是Attributes。现在我看到了我实现的真实的问题:.resx文件的转换键用作显示名称,而不是转换本身。我认为它的工作原理与RequiredAttribute类似,但事实似乎并非如此:

[Required(ErrorMessageResourceName = nameof(Strings.employeeFirstNameRequired), ErrorMessageResourceType = typeof(Strings))]

jdgnovmf

jdgnovmf1#

ProvideValue方法的实现将返回密钥。
它应该基于该键执行资源查找并返回本地化值。
有一个ResourceManagerGetString方法,你可以使用它:

public override object ProvideValue(IServiceProvider serviceProvider)
{
    if (Type.GetProperty(PropertyName) is PropertyInfo prop)
    {
        if (prop.GetCustomAttributes(typeof(DisplayAttribute), false).FirstOrDefault() is DisplayAttribute attribute && !string.IsNullOrEmpty(attribute.Name))
        {
            return strings.ResourceManager.GetString(attribute.Name);
        }
    }
    return string.Empty;
}

只需将“strings”替换为.resx文件和自动生成的类的名称。

qxsslcnc

qxsslcnc2#

我不太明白你的问题。我觉得你找错地方了。您需要查看CustomAttributes集合。

**P.S.**略有改进的实现(不检查错误和异常):

public class DisplayNameExtension : MarkupExtension
    {
        public DisplayNameExtension() { }

        [TypeConverter(typeof(PropertyInfoConverter))]
        public PropertyInfo? Property { get; set; }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
                if (Property!.GetCustomAttributes(typeof(DisplayAttribute), false).FirstOrDefault() is DisplayAttribute attribute && !string.IsNullOrEmpty(attribute.Name))
                {
                    return attribute.Name;
                }
            return string.Empty;
        }
    }

    public class PropertyInfoConverter : TypeConverter
    {
        public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
        {
            int index;
            if (value is string text && (index = text.LastIndexOf('.')) >0) 
            {
                string propName = text[(index + 1)..];
                string typeName = text[..index];
                IXamlTypeResolver xamlTypeResolver = (IXamlTypeResolver) context!.GetService(typeof(IXamlTypeResolver))!;
                Type type = xamlTypeResolver.Resolve(typeName);
                var prop = type.GetProperty(propName);
                return prop;
            }
            return base.ConvertFrom(context, culture, value);
        }
    }
<TextBlock Text="{local:DisplayName Property=local:Employee.FirstName}"/>

相关问题