Пользовательский BoundField не сохраняет данные между PostBacks с добавленным LiteralControl
Я создал кастом BoundField
для использования с DetailsView
в режимах редактирования и вставки, чтобы использовать Twitter Bootstrap.
Управление работает нормально, кроме случаев, когда я добавляю в LiteralControl
чтобы окружить TextBox
с некоторым HTML во время InitializeDataCell (...). Код связан с этим ближе к концу.
Сгенерированный HTML ниже, кажется, идентичен тому, что генерируется с TemplateField
, Но когда я запускаю Обновление, в любое время он добавляет дополнительный div / span, показанный здесь, значение в TextBox
пусто на 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>
Вот несколько примеров того, что я делаю в коде ASP.NET и что генерируется HTML.
Не работает - настраиваемое поле Значение внутри текстового поля сбрасывается после отправки с помощью команды Обновить, ТОЛЬКО при добавлении части ввода-добавления и дополнения.
Поле внутри DetailsView
<my:ValidationField DataField="Price" HeaderText="Price" TextPrepend="$" />
Сгенерированный HTML
<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>
Работает- нормально TemplateField Работает нормально
Поле внутри 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 генерируется - идентично тому, что выше
<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>
Работает - HTML, созданный с помощью настраиваемого поля Значение внутри текстового поля, корректно передается с помощью команды " Обновить", когда я не добавляю поле TextPrepend.
Поле внутри DetailsView
<my:ValidationField DataField="Price" HeaderText="Price" />
HTML генерируется без дополнительного 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, связанные с созданием этого кода
Я полагаю, что это связано с реализацией InitializeDataCell (...).
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);
}
}
}
Весь код на тот случай, если он пригодится любому, кто пытается использовать Twitter Bootstrap, или если мой код неверен в другом месте.
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
}
1 ответ
Мне удалось выяснить, почему это происходит. Так как Control
коллекция была изменена в поле, вы также должны переопределить _ExtractValuesFromCell(...) to get the
Значение TextBox`.
Я использовал слегка измененную реализацию из переопределенного ASP.NET Boundfield для поддержки Dropdownlist, в которой отсутствует одна последняя функция и не оптимизирован для моего варианта использования. Тем не менее, теперь он работает нормально.
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 == " ")
{
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);
}
}
}
}