Un custom control BoundField con dropdownlist

di Marco Leoncini, in ASP.NET 2.0,

ASP.NET ha una nutrita serie di controlli studiati per le più disparate esigenze di visualizzazione dati, ciò nonostante è del tutto assente un campo per la visualizzazione di una DropDownList in un Data Control, come GridView o DetailsView.

Benché il problema sia facilmente risolvibile utilizzando la malleabile TemplateField, alla lunga tale soluzione è scomoda specialmente se costretti a ripetere il medesimo markup in più pagine.
Per evitare di cadere in errori causati da copia/incolla, la soluzione migliore consiste nel realizzare un field come custom contro.

Il primo passo è decidere la classe da derivare, nel nostro caso la scelta più adatta è la classe BoundField perché dispone già di un metodo per formattare i dati, FormatDataValue, ed uno per recuperare dalla fonte dati il campo da visualizzare, GetValue.
I metodi di cui è indispensabile eseguire l'override sono tre: InizializzeDataCell, ExtractValuesFromCell e OnDataBindField.
InizializzeDataCell viene eseguito ogniqualvolta è necessario aggiungere a una cella il controllo o i controlli necessari a visualizzare i dati provenienti dalla fonte dati e non viene richiamato per creare l'header o il footer.

Per realizzare la nostra field custom è necessario aggiungere alla cella una DropDownList, qualora si trovi in modalità di Edit o Insert, e registrare per l'evento DataBound un event handler che punti al metodo OnDataBindField. Infine per recuperare la selezione dell'utente basta sfruttare l'override del metodo ExtractValuesFromCell.

protected override void InitializeDataCell(DataControlFieldCell cell, DataControlRowState rowState)
{
  // controllo se devo aggiungere la DropDownList
  if (IsInputMode(rowState))
  {
    // la istanzio
    DropDownList _dropDownList = new DropDownList();

    // e imposto le proprietà necessarie
    _dropDownList.DataTextField = DataTextField;
    _dropDownList.DataValueField = DataValueField;
    _dropDownList.AppendDataBoundItems = true;

    //aggiungo un elemento neutro
    _dropDownList.Items.Add(new ListItem(ItemTextEmptyValue, ItemValuetEmptyValue));

    // aggiungo il controllo alla cella
    cell.Controls.Add(_dropDownList);

    //se sono in DesignMode
    if (DesignMode)
    {
      if (!string.IsNullOrEmpty(DataSourceID))
      _dropDownList.Items.Add("DataBound");
    }
    else
    {
      //imposto la sorgente dati
      _dropDownList.DataSourceID = DataSourceID;

      //per la modalità Insert non ho bisogno di gesire l'evento
      if ((rowState & DataControlRowState.Insert) != DataControlRowState.Insert)
    _dropDownList.DataBound += new EventHandler(OnDataBindField);

    }
  }
  else
  {
    cell.DataBinding += new EventHandler(OnDataBindField);
  }
}

In seguito eseguiamo l'override del metodo OnDataBindField, eseguito come risposta all'evento DataBound della DropDownList (modalita di Edit e Insert) e per quello di DataBind della cella.

Nel caso non sia trovata corrispondenza tra il campo della fonte dati associata al GridView e i valori dell'item della DropDownList, questo viene memorizzato nel campo OccasionalField.

protected override void OnDataBindField(object sender, EventArgs e)
{
  //casto l'oggetto che a gererato l'evento ad un generico riferimento a Control
  Control _control = (Control)sender;

  DropDownList _dropDownList = sender as DropDownList;

  DataControlFieldCell _dataControlFieldCell = sender as DataControlFieldCell;

  //controllo se è necessatio encodare i dati
  bool encode = (SupportsHtmlEncode && HtmlEncode) && (sender is TableCell);

  //estraggo il volore
  _occasionalOldValue = this.GetValue(_control.NamingContainer);

  //se il sender è una DropDownList
  if (_dropDownList != null)
  {
    //recupero l'idice del item usando come chiave di ricerca il valore recuperato dallo sorgente dati
    int _index = _dropDownList.Items.IndexOf(_dropDownList.Items.FindByValue(_occasionalOldValue.ToString()));

    //se l'intem è presente nella fonte dati
    if (_index > -1)
      _occasionalOldValue = null;

    //imposto l'indice selezionato
    _dropDownList.SelectedIndex = _index;
  }
  
  //se il sender è una cella con dati
  else if (_dataControlFieldCell != null)
  {
    if (this.DesignMode)
      _dataControlFieldCell.Text = "abc";
    else
      //formatto il valore
      _dataControlFieldCell.Text = FormatDataValue(_occasionalOldValue, encode);

  }
}

Infine non rimane che eseguire l'override del metodo ExtractValuesFromCell allo scopo di recuperare il valore selezionato dalla DropDownList:

public override void ExtractValuesFromCell(System.Collections.Specialized.IOrderedDictionary dictionary, DataControlFieldCell cell, DataControlRowState rowState, bool includeReadOnly)
{
  //se la DropDownList è stata aggiunta
  if (cell.Controls.Count > 0)
  {
    //recupero il riferimento
    DropDownList _dropDownList = cell.Controls[0] as DropDownList;

    if (_dropDownList != null)
    {
      // recupero il valore (siaprecedente che attuale)
      object _dataField = _occasionalOldValue == null ? _dropDownList.SelectedValue : _occasionalOldValue;

      //l'aggiungo alla collezione dei parametri
      if (dictionary.Contains(DataField))
      {
        dictionary[DataField] = _dataField;
      }
      else
      {
        dictionary.Add(DataField, _dataField);
      }
    }
  }
  else
  {
    base.ExtractValuesFromCell(dictionary, cell, rowState, includeReadOnly);
  }
}

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti

I più letti di oggi