#949 - Un custom control BoundField con dropdownlist

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);
  }
}


Approfondimenti

Commenti

Esprimi il tuo giudizio su questo script:

Per procedere devi essere autenticato.

Aggiungi un nuovo commento »»»
Per inserire un commento, devi registrarti alla nostra community.




IN EVIDENZA
MISC