Auteur de questions
Comment changer dynamiquement la valeur d'un attribut d'une instance ?

Question
-
Bonjour,
En ce moment, je travail sur l’utilisation (affichage et édition) des attributs des propriétés d'un objet. L'objet est lui bindé directement sur une propertyGrid.
Pour info, le propertyGrid présente 1 objet « personne » qui est composé de 2 objets « adresse ». Les « adresses » sont aussi accessible dans la propertygrid par l’utilisation de l’instruction suivante : [TypeConverter(typeof(ExpandableObjectConverter))]
La classe static ci-dessous me sert de boite à outil pour modifier dynamiquement les valeurs des attributs.
Mon probleme est le suivant, lorsque je change dynamiquement la valeur (ex : code postal) d’un attribut de l’adresse1, l’attribut de l’adresse2 est modifié lui aussi.
En faite, le TypeDescriptor travail sur les type d’objet alors qu’il faudrait que je puisse travailler sur l’instance de l’objet.
Merci par avance de votre aide.
public static class PropertyDescriptorDynamic { public static void uSetReadOnly(object instance, string property, bool isReadOnly) { if (instance != null) { PropertyDescriptor descriptor = TypeDescriptor.GetProperties(instance.GetType())[property]; ReadOnlyAttribute attribute = descriptor.Attributes[typeof(ReadOnlyAttribute)] as ReadOnlyAttribute; FieldInfo field = attribute.GetType().GetField("isReadOnly", BindingFlags.NonPublic | BindingFlags.Instance); field.SetValue(attribute, isReadOnly, BindingFlags.NonPublic | BindingFlags.Instance, null, null); } } } … // changement dynamique des valeur de l’attribut pour ADRESSE1 PropertyDescriptorDynamic.uSetReadOnly(ADRESSE1, "CODEPOSTAL", false); PropertyDescriptorDynamic.uSetReadOnly(ADRESSE1, "VILLE", false); …
Cordialement,
Cédric
Toutes les réponses
-
Bonjour,
la méthode SetValue de l'objet FieldInfo permet de travailler sur l'instance.
public abstract void SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, CultureInfo culture )
Vous ne devriez donc pas avoir
field.SetValue(attribute, isReadOnly, ...
mais
field.SetValue(instance, valeur, ...
Cordialement
-
Bonjour nikhos,
Merci pour ta réponse mais l'utilisation field.SetValue(instance, valeur, ... ne fonction pas dans ce cas.
Voici une petite démo qui expose bien le problème.
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using ControlLibrary; using System.Reflection; namespace WindowsFormsMocking { public partial class Form1 : Form { private ContactInfo contactInfo = null; public Form1() { InitializeComponent(); contactInfo = new ContactInfo(); contactInfo.SousContact = new ContactInfo(); propertyGrid1.SelectedObject = contactInfo; } private void button1_Click(object sender, EventArgs e) { ContactInfo.SetMobileEdit(contactInfo, "Mobile", false); propertyGrid1.Refresh(); } } [TypeConverter(typeof(ExpandableObjectConverter))] public class ContactInfo { [ReadOnly(false)] [Category("Contact Info")] public string Mobile { get; set; } [Category("Contact Info")] public string Name { get; set; } [Category("Mon sous contact Info")] public ContactInfo SousContact { get; set; } static public void SetMobileEdit(object instance, string property, bool allowEdit) { PropertyDescriptor descriptor = TypeDescriptor.GetProperties(instance.GetType())[property]; ReadOnlyAttribute attrib = (ReadOnlyAttribute)descriptor.Attributes[typeof(ReadOnlyAttribute)]; FieldInfo isReadOnly = instance.GetType().GetField("isReadOnly", BindingFlags.NonPublic | BindingFlags.Instance); // the property Mobile become Readonly for both contactInfo and souContactInfo // I'm finding a way to modify only contactInfo instance isReadOnly.SetValue(attrib, !allowEdit); } } }
Cordialement,
Cédric
-
Quelle est la condition qui fait que cette propriété est modifiable ou pas ? Pour moi ce n'est pas un attribut à modifier (un attribut s'applique dans une définitition de classe pour préciser soin comportement) mais une règle de gestion à mettre en place (par exemple sous forme d'une méthode CanUpdate qui indique si l'instance peut non être modifiée par l'utilisateur en cours si c'est ce que l'on cherche à mettre en place).
Merci d'éviter de créer plusieurs conversations sur le même sujet pour faciliter le suivi de la discussion.
Please always mark whatever response solved your issue so that the thread is properly marked as "Answered". -
Merci Patrice pour ta réponse. Ci-dessous, j’explique pourquoi j’utilise les attributs.
Dans mon cas, l'attribut ReadOnly permet de spécifier dynamiquement un comportement, c'est-à-dire d'indiquer si la propriété "Mobile" de mon objet "ContactInfo " est peut-être modifiable ou pas selon un droit d'accès par exemple.
En faite, je ne connais pas d'autre moyen pour interdire l'accès (en modification) à une propriété affiché dans un propertyGrid. L'exemple dans le post précédent présente bien le problème.
Sur le click du bouton, je souhaite interdire l'accès en modification à la propriété "Mobile" de mon objet "ContactInfo", malheureusement j'interdis aussi l'accès à la propriété "Mobile" de mon objet "SousContactInfo".
Je suis preneur de toutes suggestions,
Merci.
Cédric
-
Hériter de la classe pour pouvoir appliquer une autre valeur de cet attribut ? Un atttribut n'est pas spécifique à une instance mais à une classe et donc avoir deux classes permettrait peut-être de pallier ce problème. Ce qui me gène également est l'aspect dynamique ("sur clic d'un bouton") ce qui ne me parait pas très habituel pour des attributs.
C'est pourquoi je cherche à comprendre le but exact. J'ai l'impression par exemple que l'on voudrait avoir une PropertyGrid en lecture seule puis pouvoir la passer en modifier sur clic d'un bouton "Modifier" ?
Dans ce cas, j'aurais plutôt tendance à essayer de me créer un nouveau contrôle à partir de la propertygrid de base pour pouvoir implanter ce comportement de lecture seule, modifier les attributs étant plutôt une "bidouille" qui posera sans doute problème tôt ou tard (par exemple impossible d'afficher deux porpertygrid sur des objets de la même classe mais l'un étant en lecture seule).
Please always mark whatever response solved your issue so that the thread is properly marked as "Answered". -
Bonjour Patrice,
Le but exact est d'interdire dynamiquement l'accès (en modification) d'une propriété affiché dans un propertyGrid.
Le fait, et que je ne parviens pas à réaliser cette interdiction que par l'utilisation de l'attribut ReadOnly.
(Ex de règle de gestion : si le type sexe=F alors interdire la saisie de l'âge.)
Si une personne connaît l'astuce je suis preneur.
Merci
Cédric
-
Je n'ai malheureusement pas la réponse (je me la pose en ce moment même)
Mais pour personnaliser une Property Grid je trouve plus ergonomique de masquer dynamiquement certaines propriétés auxquelles on ne veut pas que l’utilisateur ait accès plutôt que de les mettre en Read Only, (par exemple un ID)
Et pour cela l'attribut [Browsable(false)] est parfait.
Si quelqu'un sait comment modifier tout cela dynamiquement, ce serait un véritable héros des temps (très) modernes
-
Bonjour,
Je me disais, pourquoi ne pas gérer les règles dans le setter
ou bien créer deux propriétés
J'ai beau chercher, j'ai du mal à trouver une solution
voici peut être une piste:
using System; namespace cs_setter { class Program { static void Main(string[] args) { Action<string> affiche = s => Console.WriteLine(s); personne Betty = new personne(); Betty._name1 = "Super Betty"; affiche(Betty._name1); Console.ReadKey(); } public class personne { private char _sexe; public string _name1 { get; set; } public string _name2 { get; private set; } public personne(char sexe = 'f', string name = "noName") { _sexe = sexe; if (sexe == 'f') { _name2 = ""; } else { _name1 = name; } } } } }
fred -
Bonjour,
En survolant le sujet je vois qu'il y a une interface ICustomTypeDescriptor qui permettrait à un objet de fournir sa description du type ? Il serait alors possible à chaque instance de ne pas forcément fournir la même description ?
A suivre...
Donc pour l'instant j'implémente ICustomTypeDescriptor en appelant à chaque fois TypeDescriptor.GetXX(this,true);
Pour les propriétés je fais :
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) { if (Nom == "A") return TypeDescriptor.GetProperties(this, true); PropertyDescriptorCollection p1 = TypeDescriptor.GetProperties(this, true); PropertyDescriptor[] a = new PropertyDescriptor[1]; a[0] = p1[0]; PropertyDescriptorCollection p2 = new PropertyDescriptorCollection(a); return p2; }
pour retourner toutes les propriétés si le Nom est A, que la première si le nom est B. Et dans ma grille de propriétés j'ai bien soit toutes les propriétés où seulement la première qui est affichée. On doit pouvoir sinon jouer sur les attributes de chaque propriété.
Sinon il doit être possible également d'utiliser un attribut pour associer à la classe concernée, la classe qui va décrire ce type ce qui évité d'avoir à implémenter l'interface directement au niveau du type.
En résumé cette approche consiste à fournir nous-même la description du type pour guider l'affichage final dans la grille de propriétés (attention cette description sera utilisée partout)...
Please always mark whatever response solved your issue so that the thread is properly marked as "Answered".- Modifié Patrice ScribeMVP, Moderator mardi 2 août 2011 18:15 Ca commence à donner qq chose...