2014-01-30 30 views
1

我已經實現了一個簡單的控件,該控件呈現abbr標籤以便使用jQuery TimeAgo插件。自定義BoundField失去對回傳的控制

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,我只需傳遞包含要渲染的DateTime值的DataField的名稱即可。很簡單,從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> 

但是,當頁面做了回發,控制消失了,所有我留下的是

<td>29.01.2014 16:17:17</td> 

請注意,的頁面不使用Ajax功能:它不包含UpdatePanel和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本身的建議here,該Controls收集(作爲一個後果,給控件的ID和嘗試使用FindControl失敗,返回null)。很顯然,DataBind事件不會在回發中調用,但是鑑於ViewState在生命週期的這個階段處於活動狀態,我會希望控件在回發時保持不變。

任何指針將不勝感激。提前致謝。

回答

0

有點過期了,但如果這有助於某人,我通過啓動反編譯器(DotPeek)並分析調用InitializeDataCell時基本控件的功能來解決此問題。事實證明,這種方法是protected,而在一些初始化後,依次從public方法InitializeCell開始依次調用。此外,它負責訂閱OnDataBindField方法到DataBinding事件。對OnDataBindField的進一步檢查表明,此方法負責設置TableCellText屬性,因此,在回發之後,我留下的所有內容都是日期的字符串表示形式。

我改變了初始化方法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; 
      } 
     } 
    } 
} 

msdn documentationBoundField.OnDataBindField現在包含這樣一個字條:

註釋繼承者 擴展BoundField類時,可以重寫此方法以執行自定義綁定例程。