views:

898

answers:

2

I managed to override the Boundfield to display a dropdownlist if I put it in a Gridview.

protected override void InitializeDataCell(DataControlFieldCell cell, DataControlRowState rowState)
    {
        Control child = null;
        Control cellControl = null;

        if ((((rowState & DataControlRowState.Edit) != DataControlRowState.Normal) && !this.ReadOnly) 
            || ((rowState & DataControlRowState.Insert) != DataControlRowState.Normal))
        {
            // If data cell is in edit mode, create DropDownList editor for this cell
            // and set data properties.

            DropDownList box = new DropDownList();                
            box.Items.Add(DefaultValueText);               

            box.DataSource = this.GetDataSource();
            box.DataMember = this.BusinessObjectName;
            box.DataTextField = this.DataTextField;
            box.DataValueField = this.DataValueField;
            box.AppendDataBoundItems = true;
            box.ToolTip = this.HeaderText;

            cell.Controls.Add(box);
            box.DataBind();
            // if in edit mode, prepare dropdown for binding
            if ((this.DataField.Length != 0) && ((rowState & DataControlRowState.Edit) != DataControlRowState.Normal))
            {
                cellControl = box;
            }
        }
        else if (this.DataField.Length != 0)    // if in read only mode, prepare cell for binding
        {
            cellControl = cell;
        }

        if ((cellControl != null) && base.Visible)
        {
            cellControl.DataBinding += new EventHandler(this.OnDataBindField);
        }
    }


    protected override void OnDataBindField(object sender, EventArgs e)
    {
        Control control = (Control)sender;
        Control namingContainer = control.NamingContainer;
        object dataValue = this.GetValue(namingContainer);
        bool encode = (this.SupportsHtmlEncode && this.HtmlEncode) && (control is TableCell);
        string str = this.FormatDataValue(dataValue, encode);
        if (control is TableCell)
        {
            if (str.Length == 0)
            {
                str = " ";
            }
            ((TableCell)control).Text = str;
        }
        else
        {
            //If data cell is in edit mode, set selected value of DropDownList 
            if (dataValue != null)
            {
                DropDownList dropDownList = (DropDownList) control;

                ListItem itm = dropDownList.Items.FindByText(dataValue.ToString());
                if (itm != null)
                {
                    dropDownList.Text = itm.Value;
                }
                else
                    ((DropDownList)control).Text = DefaultValueText;
            }
        }
    }

The last feature I added is a default value/ additional item to display if nothing has been selected, like "please select" for example. I can set this through the property DefaultValueText in the OnDataBind event.

Now here's the problem I am facing:

In InitializeDataCell, if I set

box.AppendDataBoundItems = true;

and call

box.DataBind();

The dropdownlist has all the items plus the additional default item. It also works nicely in the OnDataBind event, where I can now select the default if the databound item does not contain a value.

But when the dropdownlist is displayed in the gridview, it contains the default value plus everything from the datasource TWICE, because I set AppendDataBoundItems = true, which leads the dropdown to NOT clear it's items when items are added The gridview must be calling databind twice, but it's only registering once in the OnDataBind event method. I only see one call there, and in that moment, everything is fine, the dropdown contains the default item plus one of each item from the datasource.

Any suggestions where or how I can handle the databinding so that I have full control over the databinding?

A: 

Hey,

Well, I know in some situations it does bind multiple times (upon changing criteria, etc.) so you are bound to deal with this issue again... can you clear the list and rebind?

Brian
The only way I see to influence the databinding is by attaching an event handler to DropDownList.DataBinding. And this is called only once (from what I see in debug mode), so who is copying the items a second time? The getModes() methods associated with the dropdownlist receives the number of hits equivalent to the number of rows I have (once for each dropdownlist initialized). Could it be coming from viewstate? I think I might try to turn that off, if possible
Niels Schultz
+1  A: 

I believe I found a way, at least for now it's working as expected.

I moved all the code for setting the selectedValue to the DataBound event of the DropDownList. In this event, the databinding already happened and the list of values is available for me to set the selectedValue. I don't call DataBind myself anymore, since it's being called anyway on the control. I only add the "make a selection" item at the beginning and set AppendDataBoundItems to true.

There might be unhandled situations now in certain read only states, because I don't handle any Cell.Databinding() events.

Complete sourcecode for those who are interested...

It's based on the example from Javad Zarrinabadi at CodeProjct

usage:

DropDownBoundField dropDownBoundField = new DropDownBoundField();
        dropDownBoundField.HeaderText = "Modus";
        dropDownBoundField.BusinessObjectName = "BusinessLogic.Mitarbeiter.ModusCtrl";
        dropDownBoundField.SelectMethod = "GetList";
        dropDownBoundField.DataTextField = "Name";
        dropDownBoundField.DataValueField = "Id";
        dropDownBoundField.DataField = "Modus";
        dropDownBoundField.DefaultValueText = "make a selection";

        GridView.Columns.Add(dropDownBoundField);

Class:

 using System;
 using System.Web.UI.WebControls;
 using System.Web.UI;
 using System.ComponentModel;
 using System.Web;
 using System.Collections.Specialized;

namespace IDVCode.GridViews
{
public class DropDownField : BoundField
{
    #region fields
    private string _listDataSourceID;
    private string _listDataMember;
    private string _listDataTextField;
    private string _listDataValueField;
    #endregion 

    #region eventHandler

    protected override void InitializeDataCell(DataControlFieldCell cell, DataControlRowState rowState)
    {
            DropDownList dropDownList = new DropDownList();
            dropDownList.ToolTip = HeaderText;
            dropDownList.DataSourceID = ListDataSourceID;
            dropDownList.DataMember = ListDataMember;
            dropDownList.DataTextField = ListDataTextField;
            dropDownList.DataValueField = ListDataValueField;
            dropDownList.Enabled = !ReadOnly;
            cell.Controls.Add(dropDownList);

            if (rowState == DataControlRowState.Normal || rowState == DataControlRowState.Alternate || ReadOnly)
            {
                dropDownList.Enabled = false;
            }
            if (DataField.Length != 0) // && ((rowState & DataControlRowState.Edit) != DataControlRowState.Normal))
            {
                dropDownList.DataBound += new EventHandler(OnDataBindField);
            }
       }

    protected override void OnDataBindField(object sender, EventArgs e)
    {
        Control control = (Control)sender;
        Control namingContainer = control.NamingContainer;
        object dataValue = GetValue(namingContainer);
        bool encode = (SupportsHtmlEncode && HtmlEncode) && (control is TableCell);
        string str = FormatDataValue(dataValue, encode);
        if (control is TableCell)
        {
            if (str.Length == 0)
            {
                str = " ";
            }
            ((TableCell)control).Text = str;
        }
        else
        {
            if (!(control is DropDownList))
            {
                throw new HttpException("BoundField_WrongControlType");
            }
            if (((DropDownList)control).Items.Count > 0)    // Don't call selectedValue if empty
            {
                if (dataValue != null)
                {
                    DropDownList dropDownList = (DropDownList)control;

                    ListItem item = null;
                    if (FindBy == SetSelectedValueBy.Value)
                    {
                        item = dropDownList.Items.FindByValue(dataValue.ToString());
                    }
                    else
                    {
                        item = dropDownList.Items.FindByText(dataValue.ToString());
                    }

                    if (item != null)
                        dropDownList.Text = item.Value;
                    else
                    {
                        ListItem defaultItem = dropDownList.Items.FindByText(DefaultValueText);
                        if (defaultItem != null)
                            dropDownList.SelectedValue = defaultItem.Value;
                    }
                }
            }
        }
    }

    public override void ExtractValuesFromCell(IOrderedDictionary dictionary, DataControlFieldCell cell,
        DataControlRowState rowState, bool includeReadOnly)
    {
        Control control = null;
        string dataField = DataField;
        object text = null;
        string nullDisplayText = NullDisplayText;
        if (((rowState & DataControlRowState.Insert) == DataControlRowState.Normal) || InsertVisible)
        {
            if (cell.Controls.Count > 0)
            {
                control = cell.Controls[0];
                DropDownList box = control as DropDownList;
                if (box != null)
                {
                    text = box.SelectedValue;
                }
            }
            else if (includeReadOnly)
            {
                string s = cell.Text;
                if (s == " ")
                {
                    text = string.Empty;
                }
                else if (SupportsHtmlEncode && HtmlEncode)
                {
                    text = HttpUtility.HtmlDecode(s);
                }
                else
                {
                    text = s;
                }
            }
            if (text != null)
            {
                if (((text is string) && (((string)text).Length == 0)) && ConvertEmptyStringToNull)
                {
                    text = null;
                }
                if (((text is string) && (((string)text) == nullDisplayText)) && (nullDisplayText.Length > 0))
                {
                    text = null;
                }
                if (dictionary.Contains(dataField))
                {
                    dictionary[dataField] = text;
                }
                else
                {
                    dictionary.Add(dataField, text);
                }
            }
        }
    }

    #endregion

    #region Properties

    public virtual string ListDataSourceID
    {
        get
        {
            if (_listDataSourceID == null)
            {
                object stateBag = ViewState["ListDataSourceID"];
                if (stateBag != null)
                {
                    _listDataSourceID = (string)stateBag;
                }
                else
                {
                    _listDataSourceID = string.Empty;
                }
            }
            return _listDataSourceID;
        }
        set
        {
            if (!object.Equals(value, ViewState["ListDataSourceID"]))
            {
                ViewState["ListDataSourceID"] = value;
                _listDataSourceID = value;
                OnFieldChanged();
            }
        }
    }

    public virtual string ListDataMember
    {
        get
        {
            if (_listDataMember == null)
            {
                object stateBag = ViewState["ListDataMember"];
                if (stateBag != null)
                {
                    _listDataMember = (string)stateBag;
                }
                else
                {
                    _listDataMember = string.Empty;
                }
            }
            return _listDataMember;
        }
        set
        {
            if (!object.Equals(value, ViewState["ListDataMember"]))
            {
                ViewState["ListDataMember"] = value;
                _listDataMember = value;
                OnFieldChanged();
            }
        }
    }

    public virtual string ListDataTextField
    {
        get
        {
            if (_listDataTextField == null)
            {
                object stateBag = ViewState["ListDataTextField"];
                if (stateBag != null)
                {
                    _listDataTextField = (string)stateBag;
                }
                else
                {
                    _listDataTextField = string.Empty;
                }
            }
            return _listDataTextField;
        }
        set
        {
            if (!object.Equals(value, ViewState["ListDataTextField"]))
            {
                ViewState["ListDataTextField"] = value;
                _listDataTextField = value;
                OnFieldChanged();
            }
        }
    }

    public virtual string ListDataValueField
    {
        get
        {
            if (_listDataValueField == null)
            {
                object stateBag = ViewState["ListDataValueField"];
                if (stateBag != null)
                {
                    _listDataValueField = (string)stateBag;
                }
                else
                {
                    _listDataValueField = string.Empty;
                }
            }
            return _listDataValueField;
        }
        set
        {
            if (!object.Equals(value, ViewState["ListDataValueField"]))
            {
                ViewState["ListDataValueField"] = value;
                _listDataValueField = value;
                OnFieldChanged();
            }
        }
    }

    [Description("Sets a default value if applicable")]
    [Category("Appearance")]
    public string DefaultValueText
    {
        get
        {
            object val = ViewState["DefaultValueText"];
            if (val != null)
            {
                return (string)val;
            }
            return (string.Empty);
        }

        set
        {
            ViewState["DefaultValueText"] = value;
        }
    }

    [Description("Defines how the SelectedValue is set")]
    [Category("Data")]
    [DefaultValue(SetSelectedValueBy.Value)]
    public SetSelectedValueBy FindBy
    {
        get
        {
            object val = ViewState["SetSelectedValueBy"];
            return val != null ? (SetSelectedValueBy) val : SetSelectedValueBy.Value;
        }
        set
        {
            ViewState["SetSelectedValueBy"] = value;
        }
    }

    public enum SetSelectedValueBy
    {
        Text,
        Value
    }

    #endregion
}

}

Niels Schultz