Try this:
Section.cs:
[ToolboxData("<{0}:Section runat=\"server\" />")]
public class Section : WebControl, INamingContainer
{
private SectionPartCollection _parts;
[Browsable(false), PersistenceMode(PersistenceMode.InnerProperty)]
public SectionPartCollection Parts
{
get
{
if (this._parts == null)
{
this._parts = new SectionPartCollection();
if (this.IsTrackingViewState)
((IStateManager)this._parts).TrackViewState();
}
return this._parts;
}
}
[Browsable(false), PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate LayoutTemplate { get; set; }
protected override void CreateChildControls()
{
base.CreateChildControls();
if (this.LayoutTemplate != null)
{
this.LayoutTemplate.InstantiateIn(this);
foreach (SectionPart part in this.Parts)
{
Control placeHolder = this.FindControl(part.PlaceHolderID);
if (placeHolder != null)
if (part.ContentTemplate != null)
part.ContentTemplate.InstantiateIn(placeHolder);
}
}
}
protected override void LoadViewState(object savedState)
{
object[] states = (object[])savedState;
base.LoadViewState(states[0]);
if (states[1] != null)
((IStateManager)this.Parts).LoadViewState(states[1]);
}
protected override object SaveViewState()
{
object[] states = new object[2];
states[0] = base.SaveViewState();
if (this._parts != null)
states[1] = ((IStateManager)this.Parts).SaveViewState();
return states;
}
protected override void TrackViewState()
{
base.TrackViewState();
if (this._parts != null)
((IStateManager)this._parts).TrackViewState();
}
}
SectionPart.cs:
[DefaultProperty("PartName")]
public class SectionPart : IStateManager
{
private StateBag _viewState;
private bool _isTrackingViewState;
[DefaultValue("")]
public string PlaceHolderID
{
get { return (string)this.ViewState["PlaceHolderID"] ?? string.Empty; }
set { this.ViewState["PlaceHolderID"] = value; }
}
[Browsable(false), PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate ContentTemplate { get; set; }
public void SetDirty()
{
if (this._viewState != null)
this.ViewState.SetDirty(true);
}
[Browsable(false)]
protected StateBag ViewState
{
get
{
if (this._viewState == null)
{
this._viewState = new StateBag(false);
if (this._isTrackingViewState)
((IStateManager)this._viewState).TrackViewState();
}
return this._viewState;
}
}
protected virtual bool IsTrackingViewState
{
get { return this._isTrackingViewState; }
}
protected virtual void LoadViewState(object state)
{
if (state != null)
((IStateManager)this.ViewState).LoadViewState(state);
}
protected virtual object SaveViewState()
{
if (this._viewState != null)
return ((IStateManager)this._viewState).SaveViewState();
return null;
}
protected virtual void TrackViewState()
{
this._isTrackingViewState = true;
if (this._viewState != null)
((IStateManager)this._viewState).TrackViewState();
}
bool IStateManager.IsTrackingViewState
{
get { return this.IsTrackingViewState; }
}
void IStateManager.LoadViewState(object state)
{
this.LoadViewState(state);
}
object IStateManager.SaveViewState()
{
return this.SaveViewState();
}
void IStateManager.TrackViewState()
{
this.TrackViewState();
}
}
SectionPartCollection.cs:
public class SectionPartCollection : StateManagedCollection
{
public SectionPart this[int index]
{
get { return (SectionPart)((IList)this)[index]; }
}
public void Add(SectionPart part)
{
if (part == null)
throw new ArgumentNullException("part");
((IList)this).Add(part);
}
public void Insert(int index, SectionPart part)
{
if (part == null)
throw new ArgumentNullException("part");
((IList)this).Insert(index, part);
}
public void Remove(SectionPart part)
{
if (part == null)
throw new ArgumentNullException("part");
((IList)this).Remove(part);
}
public void RemoveAt(int index)
{
((IList)this).RemoveAt(index);
}
protected override void SetDirtyObject(object o)
{
((SectionPart)o).SetDirty();
}
}
Example:
<uc:Section ID="Section1" runat="server">
<LayoutTemplate>
<table>
<tr>
<td id="TitlePlaceHolder" runat="server">
</td>
</tr>
<tr>
<td id="BodyPlaceHolder" runat="server">
</td>
</tr>
</table>
</LayoutTemplate>
<Parts>
<uc:SectionPart PlaceHolderID="TitlePlaceHolder">
<ContentTemplate>
<span>Title</span>
</ContentTemplate>
</uc:SectionPart>
<uc:SectionPart PlaceHolderID="BodyPlaceHolder">
<ContentTemplate>
<p>
Some content...</p>
</ContentTemplate>
</uc:SectionPart>
</Parts>
</uc:Section>