[WPF] Comment récupérer la valeur d'un attribut personnalisé lors du DataBinding ?
J'ai répondu récemment à une question, sur les forums de Developpez.com, qui était forte intéressante et qui valait bien un petit post sur mon blog.
Pour faire simple, le demandeur était dans le cas suivant:
- Une listbox liée à une liste de personne
- Un attribut personnalisé sur les propriétés de la classe Person
Son besoin était simple: il voulait pouvoir afficher, lors d'un binding, la valeur d'une des propriétés de l'attribut personnalisé. En terme de code, nous avions donc ceci:
public class Person
{
[PersonProperties("LastName : ")]
public string LastName { get; set; }
[PersonProperties("FirstName : ")]
public string FirstName { get; set; }
}
public class PersonCollection : ObservableCollection<Person>
{
}
[global::System.AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
sealed class PersonPropertiesAttribute : Attribute
{
public string Header { get; private set; }
public PersonPropertiesAttribute(string header)
{
this.Header = header;
}
}
Il faut à présent remplir notre collection de personne, par exemple lors du chargement de la fenêtre:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
ObjectDataProvider odp = this.TryFindResource("PersonCollectionDS") as ObjectDataProvider;
if (odp != null)
{
PersonCollection pc = odp.Data as PersonCollection;
if (pc != null)
{
pc.Add(new Person() { LastName = "LEBRUN", FirstName = "Thomas" });
pc.Add(new Person() { LastName = "SANTIN", FirstName = "Florent" });
pc.Add(new Person() { LastName = "SENTENAC", FirstName = "Philippe" });
pc.Add(new Person() { LastName = "FURUTA", FirstName = "Mitsuru" });
}
}
}
Maintenant, il faut faire en sorte que, lors du binding, on arrive à accéder à l'attribut "PersonProperties" (et à sa valeur) et à l'afficher. Pour cela, il faut déjà mettre en place le binding, en utilisant un convertisseur auquel on va passer un paramètre:
<TextBlock HorizontalAlignment="Right" Margin="0,17,8,0" VerticalAlignment="Top" Width="126" Height="16" Text="{Binding Path=SelectedItem, Converter={StaticResource GetPersonAttributeConverter}, ConverterParameter=LastName, ElementName=lbPersons, Mode=Default}" TextWrapping="Wrap" x:Name="tbLastName"/>
<TextBlock HorizontalAlignment="Right" Margin="0,52,8,0" VerticalAlignment="Top" Width="126" Height="16" Text="{Binding Path=SelectedItem, Converter={StaticResource GetPersonAttributeConverter}, ConverterParameter=FirstName, ElementName=lbPersons, Mode=Default}" TextWrapping="Wrap" x:Name="tbFirstName"/>
Comme vous pouvez en douter, toute la magie va se passer dans le convertisseur, qui va tout simplement faire un peu de reflection sur l'objet passé en paramètre (un objet de type Person) et accéder aux attributs personnalisés:
public class GetPersonAttributeConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string result = string.Empty;
if (value != null)
{
Person p = value as Person;
if (p != null)
{
var properties = p.GetType().GetProperties();
foreach (var propertyInfo in properties)
{
var attributes = propertyInfo.GetCustomAttributes(typeof(PersonPropertiesAttribute), true);
if (((PersonPropertiesAttribute)attributes.GetValue(0)) != null)
{
string header = ((PersonPropertiesAttribute)attributes.GetValue(0)).Header;
string parameterToUse = parameter as string;
if (parameterToUse != null)
{
if (header.Contains(parameterToUse))
{
switch (parameterToUse)
{
case "LastName":
result = string.Concat(header, p.LastName);
break;
case "FirstName":
result = string.Concat(header, p.FirstName);
break;
default:
break;
}
}
}
}
}
}
}
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
#endregion
}
A l'exécution, le biding est bien mis en place et grâce à la reflection, nous accédons à la valeur des propriétés de notre attribut personnalisé:
Pour ceux qui veulent tester, vous trouverez le code source de la démonstration ici: Source
A+
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :