【发布时间】:2014-01-30 15:39:12
【问题描述】:
我已经实现了一个简单的控件,它呈现一个abbr 标签,以便使用jQuery TimeAgo 插件。
public class TimeAgoControl : System.Web.UI.WebControls.WebControl
{
[Bindable(true)]
[DefaultValue(null)]
public string Iso8601Timestamp
{
get { return (string)ViewState["Iso8601Timestamp"]; }
set { ViewState["Iso8601Timestamp"] = value; }
}
[Bindable(true)]
[DefaultValue(null)]
public override string ToolTip
{
get { return (string)ViewState["ToolTip"]; }
set { ViewState["ToolTip"] = value; }
}
public override void RenderControl(HtmlTextWriter writer)
{
writer.AddAttribute("class", "timeago");
writer.AddAttribute("title", Iso8601Timestamp);
writer.RenderBeginTag("abbr");
RenderContents(writer);
writer.RenderEndTag();
}
protected override void RenderContents(HtmlTextWriter writer)
{
writer.WriteEncodedText(ToolTip);
}
}
这很好用。我可以在GridView TemplateField 中使用上述控件,而且效果也很好:
<asp:TemplateField>
<ItemTemplate>
<my:TimeAgoControl runat="server" Iso8601Timestamp='<%# Item.LastImportDate.ToString("s") %>'
ToolTip='<%# Item.LastImportDate.ToString("f") %>' />
</ItemTemplate>
</asp:TemplateField>
当然,我不想每次我想在 GridView 中使用控件时都输入以上所有内容,所以我想我会将上述逻辑抽象为自定义 BoundField,我只需在其中传递DataField 的名称,其中包含要呈现的 DateTime 值。很简单,从System.Web.UI.WebControls.BoundField 扩展并覆盖InitializeDataCell 方法,将控件添加到其中的DataControlFieldCell,并将处理程序附加到DataBinding 事件。它可以工作并生成正确的标记:
<td><abbr class="timeago" title="Wednesday, January 29, 2014 16:17">about 23 hours ago</abbr></td>
但是当页面执行 PostBack 时,控件消失了,我只剩下
<td>29.01.2014 16:17:17</td>
请注意,该页面不使用 Ajax 功能:它不包含 UpdatePanel 或 ScriptManager。
我已经研究了很多,发现 this unanswered question 以及这个 other question 指出必须覆盖 ExtractValuesFromCell 方法,在我的情况下从未调用过。这是我的实现
public class TimeAgoBoundField : System.Web.UI.WebControls.BoundField
{
protected override void InitializeDataCell(System.Web.UI.WebControls.DataControlFieldCell cell, System.Web.UI.WebControls.DataControlRowState rowState)
{
base.InitializeDataCell(cell, rowState);
cell.Controls.Add(new TimeAgoControl());
cell.DataBinding += (sender, e) =>
{
var c = (DataControlFieldCell)sender;
//how do I get the TimeAgoControl within this scope? c.Controls.Count is 0
var dateTimeValue = (DateTime?)DataBinder.GetPropertyValue(DataBinder.GetDataItem(c.NamingContainer), this.DataField);
c.Controls.Add(new TimeAgoControl
{
Iso8601Timestamp = dateTimeValue.HasValue ? dateTimeValue.Value.ToLocalTime().ToString("s") : this.NullDisplayText,
ToolTip = DateTimeValue.HasValue ? dateTimeValue.Value.ToLocalTime().ToString("f") : this.NullDisplayText
});
};
}
}
请注意,如果我不是在DataBind 事件处理程序中添加控件,而是在InitializeDataCell 本身中添加控件,则Controls 集合为空(因此,给控件一个 ID 并尝试使用 FindControl 失败返回 null)。显然DataBind 事件不会在回发时调用,但鉴于ViewState 在生命周期的这个阶段处于活动状态,我本来希望控件在回发时保持不变。
任何指针将不胜感激。提前致谢。
【问题讨论】:
标签: c# asp.net data-binding webforms asp.net-4.5