I've created a custom BoundField for use with a DetailsView in Edit and Insert modes to utilize Twitter Bootstrap.

The control works fine, except when I add in extra LiteralControls to surround the TextBox with some HTML during InitializeDataCell(...). Code related to this toward the end.

The HTML generated is below seems to be identical to what is generated with a TemplateField. But when I fire an Update, anytime it adds the extra div / span seen here, the value in the TextBox is blank upon PostBack.

<div class="input-prepend">
    <span class="add-on">$</span>
    <input name="ctl00$ctl00$MainContent$MainContent$pi$dvPackage$tbPrice" type="text" value="0" size="5" id="MainContent_MainContent_pi_dvPackage_tbPrice" title="Price">
</div>

Here is some examples of what I'm doing within the front ASP.NET code and what HTML is generated.

Doesn't work - custom field Value inside text box is unset after submitting via Update command, ONLY when I add the input-append and add-on part.

Field inside DetailsView

<my:ValidationField DataField="Price" HeaderText="Price" TextPrepend="$" />

HTML generated

<td>
    <div class="input-prepend">
        <span class="add-on">$</span>
        <input name="ctl00$ctl00$MainContent$MainContent$pi$dvPackage$tbPrice" type="text" value="0" size="5" id="MainContent_MainContent_pi_dvPackage_tbPrice" title="Price">
    </div>
</td>

Does work - normal TemplateField Works fine

Field inside DetailsView

<asp:TemplateField>
    <EditItemTemplate>
        <div class="input-prepend">
            <span class="add-on">$</span>
            <asp:TextBox ID="tbCost" runat="server" Text='<%# Bind("Cost") %>'></asp:TextBox>
        </div>
    </EditItemTemplate>
</asp:TemplateField>

HTML generated - identical to what is above

<td>
    <div class="input-prepend">
        <span class="add-on">$</span>
        <input name="ctl00$ctl00$MainContent$MainContent$pi$dvPackage$tbPrice" type="text" value="0" size="5" id="MainContent_MainContent_pi_dvPackage_tbPrice" title="Price">
    </div>
</td>

Does work - HTML generated with custom field Value inside text box is correctly submitted via Update command when I don't add the TextPrepend field.

Field inside DetailsView

<my:ValidationField DataField="Price" HeaderText="Price" />

HTML generated without extra span / div

<td>
    <input name="ctl00$ctl00$MainContent$MainContent$pi$dvPackage$tbPrice" type="text" value="0" size="5" id="MainContent_MainContent_pi_dvPackage_tbPrice" title="Price">
</td>

InitializeDataCell parts related to this code creation

I believe this is due to something with the InitializeDataCell(...) implementation.

protected override void InitializeDataCell(DataControlFieldCell cell, DataControlRowState rowState)
{
    base.InitializeDataCell(cell, rowState);

    // Find the text box to validate
    TextBox text = FindTextBox(cell);

    if (text != null)
    {
        text.ID = "tb" + DataField;
        text.MaxLength = MaxLength;
        text.TextMode = TextMode;
        text.Text = DataField;

        string cellCss = string.Empty;
        bool prepend = !string.IsNullOrEmpty(this.TextPrepend);
        bool append = !string.IsNullOrEmpty(this.TextAppend);
        bool addon = prepend || append;

        if (prepend == true)
            cellCss = this.ConcatenateCss(cellCss, "input-prepend");

        if (append == true)
            cellCss = this.ConcatenateCss(cellCss, "input-append");

        if (addon == true)
        {
            int textIndex = cell.Controls.IndexOf(text);

            Literal container = new Literal();
            container.Text = "<div class=\"" + cellCss + "\">";
            cell.Controls.AddAt(textIndex, container);
        }

        if (prepend == true)
        {
            int textIndex = cell.Controls.IndexOf(text);

            Literal units = new Literal();
            units.Text = "<span class=\"add-on\">" + this.Prepend() + "</span>";
            cell.Controls.AddAt(textIndex, units);
        }

        if (append == true)
        {
            Literal units = new Literal();
            units.Text = "<span class=\"add-on\">" + this.Append() + "</span>";
            cell.Controls.Add(units);
        }

        if (addon == true)
        {
            Literal container = new Literal();
            container.Text = "</div>";
            cell.Controls.Add(container);
        }
    }
}

Entire code in case it's useful to anyone trying to use Twitter Bootstrap, or if my code is wrong elsewhere.

public class ValidationField : BoundField
{
    #region Properties
    public virtual string EditTextCssClass
    {
        get
        {
            return (string)(ViewState["EditTextCssClass"] ?? string.Empty);
        }

        set
        {
            ViewState["EditTextCssClass"] = value;
            OnFieldChanged();
        }
    }

    public virtual ValidatorDisplay ErrorDisplay
    {
        get
        {
            return (ValidatorDisplay)(ViewState["ErrorDisplay"] ?? ValidatorDisplay.Dynamic);
        }

        set
        {
            ViewState["ErrorDisplay"] = value;
            OnFieldChanged();
        }
    }

    public virtual string ErrorMessage
    {
        get
        {
            return (string)(ViewState["ErrorMessage"] ?? "Invalid value entered");
        }

        set
        {
            ViewState["ErrorMessage"] = value;
            OnFieldChanged();
        }
    }

    public virtual string HelpText
    {
        get
        {
            return (string)(ViewState["HelpText"] ?? string.Empty);
        }

        set
        {
            ViewState["HelpText"] = value;
            OnFieldChanged();
        }
    }

    public virtual TwitterBootstrap.WebControls.TextBox.HelpTextDisplayMode HelpDisplay
    {
        get
        {
            return (TwitterBootstrap.WebControls.TextBox.HelpTextDisplayMode)(ViewState["HelpDisplay"] ?? TwitterBootstrap.WebControls.TextBox.HelpTextDisplayMode.Block);
        }

        set
        {
            ViewState["HelpDisplay"] = value;
            OnFieldChanged();
        }
    }

    public virtual int MaxLength
    {
        get
        {
            return (int)(ViewState["MaxLength"] ?? 0);
        }

        set
        {
            ViewState["MaxLength"] = value;
            OnFieldChanged();
        }
    }

    public virtual string PlaceHolder
    {
        get
        {
            return (string)(ViewState["PlaceHolder"] ?? string.Empty);
        }

        set
        {
            ViewState["PlaceHolder"] = value;
            OnFieldChanged();
        }
    }

    public virtual bool Required
    {
        get
        {
            return (bool)(ViewState["Required"] ?? false);
        }

        set
        {
            ViewState["Required"] = value;
            OnFieldChanged();
        }
    }

    public virtual TextBoxMode TextMode
    {
        get
        {
            return (TextBoxMode)(ViewState["TextMode"] ?? TextBoxMode.SingleLine);
        }

        set
        {
            ViewState["TextMode"] = value;
            OnFieldChanged();
        }
    }

    public virtual string ValidationExpression
    {
        get
        {
            return (string)(ViewState["ValidationExpression"] ?? string.Empty);
        }

        set
        {
            ViewState["ValidationExpression"] = value;
            OnFieldChanged();
        }
    }

    public virtual string ValidationGroup
    {
        get
        {
            return (string)(ViewState["ValidationGroup"] ?? string.Empty);
        }

        set
        {
            ViewState["ValidationGroup"] = value;
            OnFieldChanged();
        }
    }

    public virtual string TextAppend
    {
        get
        {
            object value = ViewState["TextAppend"];
            if (value != null)
                return value.ToString();

            return string.Empty;
        }

        set
        {
            ViewState["TextAppend"] = value;
            OnFieldChanged();
        }
    }

    public virtual string TextPrepend
    {
        get
        {
            object value = ViewState["TextPrepend"];
            if (value != null)
                return value.ToString();

            return string.Empty;
        }

        set
        {
            ViewState["TextPrepend"] = value;
            OnFieldChanged();
        }
    }

    #endregion

    public ValidationField()
    {

    }

    protected override DataControlField CreateField()
    {
        return new ValidationField();
    }

    protected override void InitializeDataCell(DataControlFieldCell cell, DataControlRowState rowState)
    {
        base.InitializeDataCell(cell, rowState);

        // Find the text box to validate
        TextBox text = FindTextBox(cell);

        if (text != null)
        {
            text.ID = "tb" + DataField;
            text.MaxLength = MaxLength;
            text.TextMode = TextMode;
            text.CssClass = EditTextCssClass;
            text.Text = DataField;

            if (PlaceHolder != string.Empty)
                text.Attributes.Add("placeholder", PlaceHolder);

            string cellCss = string.Empty;
            bool prepend = !string.IsNullOrEmpty(this.TextPrepend);
            bool append = !string.IsNullOrEmpty(this.TextAppend);
            bool addon = prepend || append;

            if (prepend == true)
                cellCss = this.ConcatenateCss(cellCss, "input-prepend");

            if (append == true)
                cellCss = this.ConcatenateCss(cellCss, "input-append");

            if (addon == true)
            {
                int textIndex = cell.Controls.IndexOf(text);

                Literal container = new Literal();
                container.Text = "<div class=\"" + cellCss + "\">";
                cell.Controls.AddAt(textIndex, container);
            }

            if (prepend == true)
            {
                int textIndex = cell.Controls.IndexOf(text);

                Literal units = new Literal();
                units.Text = "<span class=\"add-on\">" + this.Prepend() + "</span>";
                cell.Controls.AddAt(textIndex, units);
            }

            if (append == true)
            {
                Literal units = new Literal();
                units.Text = "<span class=\"add-on\">" + this.Append() + "</span>";
                cell.Controls.Add(units);
            }

            if (addon == true)
            {
                Literal container = new Literal();
                container.Text = "</div>";
                cell.Controls.Add(container);
            }

            if (Required == true)
            {
                Literal required = new Literal();
                required.Text = "<span class=\"required\">*</span>";
                cell.Controls.Add(required);
            }

            if (HelpText != string.Empty)
            {
                Label lblHelpText = new Label();
                if (HelpDisplay == TwitterBootstrap.WebControls.TextBox.HelpTextDisplayMode.Block)
                    lblHelpText.CssClass = "help-block";
                else
                    lblHelpText.CssClass = "help-inline";
                lblHelpText.Text = HelpText;
                cell.Controls.Add(lblHelpText);
            }

            if (Required == true)
            {
                // Add a RequiredFieldValidator
                RequiredFieldValidator required = new RequiredFieldValidator();
                required.ErrorMessage = ErrorMessage;
                required.Display = ErrorDisplay;
                required.ControlToValidate = text.ID;
                required.Text = "";

                if (ValidationGroup != string.Empty)
                    required.ValidationGroup = ValidationGroup;

                cell.Controls.Add(required);
            }

            if (ValidationExpression != string.Empty)
            {
                // Add a RequiredFieldValidator
                RegularExpressionValidator regex = new RegularExpressionValidator();
                regex.ErrorMessage = ErrorMessage;
                regex.Display = ErrorDisplay;
                regex.ControlToValidate = text.ID;
                regex.ValidationExpression = ValidationExpression;

                if (ValidationGroup != string.Empty)
                    regex.ValidationGroup = ValidationGroup;

                cell.Controls.Add(regex);
            }
        }
    }

    #region Methods
    private string ConcatenateCss(params string[] classes)
    {
        string result = string.Empty;
        foreach (string s in classes)
            result += s + " ";

        return result.TrimEnd().TrimStart();
    }

    private static TextBox FindTextBox(Control parent)
    {
        TextBox result = null;
        foreach (Control control in parent.Controls)
        {
            if (control is TextBox)
            {
                result = control as TextBox;
                break;
            }
        }
        return result;
    }

    protected virtual string Prepend()
    {
        return this.TextPrepend;
    }

    protected virtual string Append()
    {
        return this.TextAppend;
    }

    #endregion
}
有帮助吗?

解决方案

I managed to figure out why this was happening. Since the Control collection was altered in the field, you must also override _ExtractValuesFromCell(...)to get theTextBox` value.

I have used a slightly altered implementation from ASP.NET Boundfield overridden to support Dropdownlist is missing one final feature and haven't optimized for my use case. However, it works fine now.

public override void ExtractValuesFromCell(System.Collections.Specialized.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)
        {
            foreach (Control c in cell.Controls)
            {
                control = cell.Controls[0];

                if (c is TextBox)
                {
                    text = ((TextBox)c).Text;
                } 
            }
        }
        else if (includeReadOnly)
        {
            string s = cell.Text;
            if (s == "&nbsp;")
            {
                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);
            }
        }
    }
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top