自定义BoundField失去对PostBack的控制权

时间:2014-01-30 15:39:12

标签: c# asp.net data-binding webforms asp.net-4.5

我已经实现了一个简单的控件,它会呈现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功能:它不包含UpdatePanels和ScriptManagers。

我已经研究了很多,发现了this未回答的问题以及这个other问题,该问题指出必须覆盖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本身{@ 3}},则Controls集合为空< / strong>(因此,为控件提供ID并尝试使用FindControl无法返回null)。显然,DataBind事件不会在回发时调用,但鉴于ViewState在生命周期的这个阶段处于活动状态,我本来希望控件在回发时保持不变。

任何指针都将非常感激。提前谢谢。

1 个答案:

答案 0 :(得分:0)

有点过期,但如果这有助于某人,我通过启动反编译器(DotPeek)并分析调用InitializeDataCell时基本控件正在做什么来解决这个问题。事实证明,此方法为protected,并在初始化后从public方法InitializeCell依次调用。此外,它还负责将OnDataBindField方法订阅到DataBinding事件。对OnDataBindField的进一步检查显示,此方法负责设置Text的{​​{1}}属性,因此,在回发之后,我留下的所有内容都是日期的字符串表示。 / p>

我将Initialize方法更改为TableCell并覆盖InitializeCell(不调用其基本对应方式),如下所示:

OnDataBindField

public class TimeAgoBoundField : System.Web.UI.WebControls.BoundField { public override bool ReadOnly { get { return true; } } public override void InitializeCell(DataControlFieldCell cell, DataControlCellType cellType, DataControlRowState rowState, int rowIndex) { base.InitializeCell(cell, cellType, rowState, rowIndex); if (cellType == DataControlCellType.DataCell) { cell.Controls.Add(new TimeAgoControl()); } } protected override void OnDataBindField(object sender, EventArgs e) { if (sender is TableCell) { var cell = (TableCell)sender; var cellValue = this.GetValue(cell.NamingContainer); if (cellValue != null) { var timeAgoControl = (TimeAgoControl) cell.Controls[0]; var dateTimeValue = (DateTime) cellValue; var utcDateTime = dateTimeValue.Kind != DateTimeKind.Utc ? dateTimeValue.ToUniversalTime() : dateTimeValue; timeAgoControl.ISO8601Timestamp = utcDateTime.ToString("s") + "Z"; } else if (this.NullDisplayText != null) { cell.Text = this.NullDisplayText; } } } } 的{​​{3}}现在包含此注释:

  

对继承者的说明   扩展BoundField类时,可以覆盖此方法以执行自定义绑定例程。