Is inheriting from Table a bad idea?
I have an ASP .NET composite server control that I've developed that uses a table as its base. It works correctly at run-time, but at design-time I get some flaky behavior. The first time the control renders on the design surface, everything looks good. But if I change any of the control's properties, then I just get an empty table. This control does not allow the page developer to add new rows--it is basically a positioning container used for a variety of other UI elements that get rendered together.
I set a breakpoint in the RenderContents method. The first line is a call to Me.EnsureChildControls, but CreateChildControls does not get called because Me.ChildControlsCreated is set to True. At runtime, this doesn't happen. The control renders correctly on every pass. At design-time, I have this problem every time the control refreshes itself after changing a property value.
To test, I created a simple TestTable and got similar results. Here's the source for that one:
Public
Class TestTable
Inherits Table
Implements INamingContainer
Protected Overrides Function SaveViewState() As Object
Return MyBase.SaveViewState()
End Function
Protected Overrides Sub LoadViewState(ByVal savedState As Object)
MyBase.LoadViewState(savedState)
End Sub
Protected Overrides Sub TrackViewState()
MyBase.TrackViewState()
End Sub
Protected Overrides Sub CreateChildControls()
Me.Controls.Clear()
Dim tr As New TableRow
Dim tc As New TableCell
tr.Cells.Add(tc)
Me.Rows.Add(tr)
Dim lbl As New Label
lbl.Text = "Test"
tc.Controls.Add(lbl)
End Sub
Protected Overrides Sub RenderContents(ByVal writer As System.Web.UI.HtmlTextWriter)
Me.EnsureChildControls()
MyBase.RenderContents(writer)
End Sub
End
Class
To run this test, just set your breakpoint on Me.EnsureChildControls. The first time the control is rendered in design view, ChildControlsCreated = False, and the CreateChildControls() method gets called. Change any property, and you'll break again; this time ChildControlsCreated will be True, and CreateChildControls() will not be called.
I could fix this in one of two ways that I know of: I could change the control so that it creates a DIV and creates the entire table on every postback; I could also modify RenderContents() as follows:
Protected Overrides Sub RenderContents(ByVal writer As System.Web.UI.HtmlTextWriter)
If Me.ChildControlsCreated And IsNothing(Me.Page.Session) Then
Me.ChildControlsCreated = False
End If
Me.EnsureChildControls()
MyBase.RenderContents(writer)
End Sub
This seems like a hack to me. I'm wondering if anyone else has any different ideas. Is there a reason why it might be a bad idea to inherit from table when I'm just using the table as a positioning element? Is there something else I could do in my code to cause it to recreate the child controls in design mode? If anyone has any better ideas, I'll update this post and properly credit you.
Thanks a lot!
UPDATE
says:
Suggestion from your first code listing... change this line:
Me.EnsureChildControls()
To:
If (HttpContext.Current Is Nothing) Then
CreateChildControls()
Else
EnsureChildControls()
says:
“You shouldn't be deriving from Table when you make a composite control. Derive from WebControl and contain a table.“
Both of which I sort of already thought, as stated in my original post just above the second code sample. Since what I was after at root was some validation that I was on the right track and wasn't overlooking anything obvious, I have to offer my thanks to Andy and Justin. Thanks guys!