Search code examples
c#.netwpfxamldisplayname-attribute

Get DisplayName of property c#


I'm currently trying to get the displayname of a property in XAML (WPF) and tried to implement the answer of this question "Access DisplayName in xaml".

To test this, I created a class with a property and the DisplayAttribute (I use localization with .resx files to get the localized string),

    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);
        }

modified the MarkupExtension from the other question to allow null values,

    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;
        }
    }

and tried to call this with:

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

(Employee is part of another project, references are correct)

Now when I stop the app and look into the prop property when calling the method, the correct property is determined but no attributes exist here even though I have set the DisplayAttribute. View inside prop with debugger: View inside prop with debugger

Am I missing something obvious here or is the solution to get the attributes a bit more complex?

EDIT: Thanks to @EldHasp for pointing out that I need to look at CustomAttributes and not Attributes. Now I see the real problem of my implementation: The key of the translation from the .resx file is used as display name and not the translation itself. I thought that it works like the RequiredAttribute but this does not seem to be the case:

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

used entries of translation


Solution

  • Your implementation of the ProvideValue method returns the key.

    It should perform the resource lookup based on that key and return the localized value.

    There is a GetString method of the ResourceManager that you can use for this:

    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;
    }
    

    Just replace "strings" with the name of your .resx file and the auto-generated class.