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:
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))]
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.