Dynamic Data: attributo DisplayColumn e override di ToString()

Nei Dynamic Data quando abbiamo una relazione uno a molti possiamo utilizzare l'attributo DisplayColumn per determinare quale tra le proprietà della classe sulla parte uno della relazione viene utilizzata nel testo del link.

[MetadataType(typeof(CustomerMetadata))]
[DisplayColumn("CompanyName", "CompanyName", false)]
public partial class Customer
{
   ...
}

Analizzando nel dettaglio il controllo ForeignKey.ascx che visualizza il campo chiave esterna notiamo che il testo del link viene popolato utilizzando il metodo GetDisplayString della classe MetaTable. Utilizzando Reflector vediamo come si comporta questo metodo:

public string GetDisplayString(object row)
{
  if (row != null)
  {
    row = this.PreprocessRowObject(row);
    if (this.HasToStringOverride)
    {
      return row.ToString();
    }
    object propertyValue = 
      DataBinder.GetPropertyValue(row, this.DisplayColumn.Name);
    if (propertyValue != null)
    {
      return propertyValue.ToString();
    }
  }
  return string.Empty;
}

Dall'analisi del codice notiamo come l'attributo DisplayColumn non abbia effetto se abbiamo eseguito l'override del metodo ToString() nella classe della chiave esterna.

In questo modo se nella classe Customer dell'esempio andiamo ad eseguire l'override del metodo ToString() nel seguente modo...

public override string ToString()
{
  return String.Format("{0} - ({1})", this.CompanyName, this.Country);
}

... l'effetto che otteniamo è il seguente:

DisplayColumn

Ovviamente possiamo ovviare a questo comportamento modificando opportunamente il controllo ForeignKey affinché utilizzi direttamente l'attributo DisplayColumn.


Passaggio di parametri ad un dynamic data control tramite l'attributo UIHint

Nei Dynamic Data possiamo, attraverso l'attributo UIHint, impostare il FieldTemplate da utilizzare per un determinato campo dati, e non solo, tramite la proprietà ControlParameters possiamo passare delle informazioni ai controlli, per esempio per impostarne delle proprietà. Vediamo come:

public IDictionary ControlParameters { get; private set; }

La proprietà ControlParameters è un insieme di coppie chiave/valore che possiamo popolare direttamente nella decorazione del campo nella classe dei metadati intervallando chiavi e valori separati da una virgola:

// ControlParameters populating: key1, value1, key2, value2 ...
[UIHint("Text", null, "Columns", 75 )
public string ContactName { ge; set; }

Nell'esempio sopra utilizziamo per il campo ContactName il template Text al quale passiamo una coppia chiave/valore con la quale vogliamo impostare la lunghezza della TextBox a 75 colonne.

Nel FieldTemplate andiamo ad intercettare l'evento PreRender e ciclando sulla collezione dei parametri utilizziamo la chiave per ottenere la proprietà corrispondente per il controllo TextBox esposto dalla proprietà DataControl. A questo punto assegnamo alla proprietà il valore utilizzando l'opportuno convertitore ottenuto dal tipo attraverso la classe TypeDescriptor.

public partial class Text_EditField 
     : System.Web.DynamicData.FieldTemplateUserControl
{
  ...
  protected void OnPreRender(EventArgs e)
  {
    UIHintAttribute hint = null;
    hint=(UIHintAttribute)this.Column.Attributes[typeof(UIHintAttribute)];
    if (hint != null)
    {
      foreach (KeyValuePair kvp in hint.ControlParameters)
      {
        PropertyInfo prop = DataControl.GetType().GetProperty(kvp.Key);
        String value = kvp.Value.ToString();
        var converter = TypeDescriptor.GetConverter(prop.PropertyType);
        if (prop != null)
        {
          prop.SetValue(DataControl, 
             converter.ConvertFromInvariantString(value), null);
        }
      }
    }
  }
  protected override void ExtractValues(IOrderedDictionary dictionary)
  {
    dictionary[Column.Name] = ConvertEditedValue(TextBox1.Text);
  }
  public override Control DataControl
  {
    get
    {
      return TextBox1;
    }
  }
}

In questo esempio ho utilizzato il FieldTemplate Text_Edit.ascx, ma se vogliamo estendere tale funzionalità a tutti i controlli possiamo pensare di creare una nostra classe che erediti direttamente da FieldTemplateUserControl ed inserire questo codice nell'override del metodo OnPreRender. Ovviamente questo implica (per questa soluzione) che le proprietà impostate nella ControlParameters vengano propagate per tutti i controlli utilizzati per renderizzare i dati del campo così decorato (ad esempio Text.ascx, Text_Edit.ascx e se esiste Text_Insert.ascx).