Crad's .NET Blog

L'UGIblog di Marco De Sanctis
posts - 190, comments - 457, trackbacks - 70

BindingWrapper per classi che espongono solo fields pubblici

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:

  1. Scriviamo a mano dei wrapper per tali oggetti (naaaaa, noioso e lungo )
  2. Realizziamo un nostro tool che faccia per noi il lavoro (come descritto in quest'articolo su CodeProject)
  3. 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

Print | posted on lunedì 3 luglio 2006 16:38 | Filed Under [ .Net 2.0 ]

Powered by:
Powered By Subtext Powered By ASP.NET