Quando ci si deve interfacciare con un webservice che
restituisce custom types, il tool WSDL.EXE provvede da solo a definire alcune
classi che rispecchiano il naming dei tipi in origine e che possono essere
direttamente utilizzate nel codice del nostro client. Attenzione, però, perché
ciò che otteniamo non è assolutamente ciò che esiste sul server: tali classi
sono infatti assolutamente prive di logica, quindi niente metodi e/o proprietà,
solo fields pubblici con lo stesso nome dei tipi d'origine.
Problema: non essendoci proprietà, non è possibile effettuare direttamente il
binding di tali oggetti con i controlli web o winforms, che come sappiamo
lavorano a colpi di PropertyDescriptor. Come fare allora? Beh ci sono almeno tre
soluzioni:
- Scriviamo a mano dei wrapper per tali oggetti (naaaaa, noioso e
lungo )
- Realizziamo un nostro tool che faccia per noi il lavoro (come descritto in
quest'articolo su CodeProject)
- Creiamo un wrapper universale sulla falsariga di quanto accade nel binding
delle DataView
Ovviamente qui si parla del punto 3)
Come procediamo? Non c'è niente di complesso, iniziamo con il creare una
classe che implementi ICustomTypeDescriptor:
public class BindingWrapper:
ICustomTypeDescriptor
{
private object innerObject;
public object InnerObject
{
get { return innerObject; }
}
public BindingWrapper(object innerObject)
{
this.innerObject = innerObject;
getObjectData();
}
private void getObjectData()
{
// lo vediamo dopo
}
}
In questa classe intanto salviamo una reference all'oggetto wrappato, poi
richiamiamo un metodo privato getObjectData() che si occupa di memorizzare un
elenco di FieldInfo dello stesso:
private List<FieldPropertyDescriptor> fields =
new List<FieldPropertyDescriptor>();
private void getObjectData()
{
FieldInfo[] objectFields = innerObject.GetType().GetFields(
BindingFlags.Instance | BindingFlags.GetField | BindingFlags.Public);
if (objectFields != null)
foreach (FieldInfo field in objectFields)
{
fields.Add(new FieldPropertyDescriptor(field));
}
}
Questo metodo cicla per tutti i Fields pubblici contenuti nel tipo che
vogliamo wrappare e, per ognuno di essi, aggiunge ad una List interna un
FieldPropertyDescriptor che lo incapsula. Tale collection è restituita
nell'implementazione in BindingWrapper del metodo GetProperties dell'interfaccia
ICustomTypeDescriptor:
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
return new PropertyDescriptorCollection(fields.ToArray());
}
Bene, ci siamo quasi, non resta che scoprire com'è fatto
FieldPropertyDescriptor; esso eredita dalla classe astratta PropertyDescriptor,
mantiene al suo interno una reference al FieldInfo che gli abbiamo assegnato e
si occupa di "simulare" il get e set su tale field come se si trattasse di una
proprietà:
public class FieldPropertyDescriptor:
PropertyDescriptor
{
private FieldInfo field;
public FieldInfo Field
{
get { return field; }
}
public FieldPropertyDescriptor(FieldInfo field):
base(field.Name, null)
{
this.field = field;
}
public override object GetValue(object component)
{
BindingWrapper wrapper = (BindingWrapper)component;
return field.GetValue(wrapper.InnerObject);
}
public override void SetValue(object component, object value)
{
BindingWrapper wrapper = (BindingWrapper)component;
field.SetValue(wrapper.InnerObject, value);
}
// ... more code here....
}
A questo punto il gioco è fatto: finalmente i nostri controlli riusciranno a
digerire anche le classi che espongono field pubblici piuttosto che proprietà!
Il codice completo dell'esempio è disponibile a questo indirizzo.
Spero che possa essere utile
powered by IMHO 1.3