问题描述:

I have a collection of generated custom controls that extend CompositeControl, defined as:

[PersistChildren(true)]

[ToolboxData("<{0}:ContractControl runat=server></{0}:ContractControl>")]

public class ContractControl : CompositeControl

{

private int contractID = 0;

private ContractTileControl tileControl = null;

private ContractDetailControl detailControl = null;

private HtmlGenericControl contractMainDiv = null;

public int ContractID

{

get { return this.contractID; }

set { this.contractID = value; }

}

public ContractTileControl TileControl

{

get { return this.tileControl; }

set { this.tileControl = value; }

}

public ContractDetailControl DetailControl

{

get { return this.detailControl; }

set { this.detailControl = value; }

}

public ContractControl()

{

this.contractMainDiv = new HtmlGenericControl("div");

this.contractMainDiv.ID = "contractMainDiv";

this.contractMainDiv.Attributes.Add("class", "contractMain");

}

#region protected override void OnPreRender(EventArgs e)

protected override void OnPreRender(EventArgs e)

{

base.OnPreRender(e);

//CreateChildControls();

}

#endregion

#region protected override void CreateChildControls()

protected override void CreateChildControls()

{

base.CreateChildControls();

if (tileControl != null)

{

this.contractMainDiv.Controls.Add(tileControl);

}

if (detailControl != null)

{

this.contractMainDiv.Controls.Add(detailControl);

}

this.Controls.Add(contractMainDiv);

//base.CreateChildControls();

}

#endregion

protected override void OnLoad(EventArgs e)

{

base.OnLoad(e);

CreateChildControls();

}

protected override void OnInit(EventArgs e)

{

base.OnLoad(e);

EnsureChildControls();

}

}

Where ContractTileControl and ContractDetailControl are another custom controls derived from CompositeControl.

When I add them to a asp:PlaceHolder control set they render fine, but when I define a repeater like:

<asp:Repeater ID="myRepeater" runat="server" >

<HeaderTemplate>

<table border="0" cellpadding="0" cellspacing="0">

</HeaderTemplate>

<ItemTemplate>

<tr><td><easit:ContractControl ID="contractControl" runat="server" />

</td></tr>

</ItemTemplate>

<FooterTemplate>

</table>

</FooterTemplate>

</asp:Repeater>

And bind them to it:

private void FillContractPlaceHolder()

{

List<ContractControl> controls = new List<ContractControl>();

foreach(KeyValuePair<Customer, List<TFSContract>> pair in contractList)

{

Label customerNameLbl = new Label();

customerNameLbl.ID = "customerNameLbl";

customerNameLbl.CssClass = "customerName";

customerNameLbl.Text = pair.Key.Name;

contractListPlaceHolder.Controls.Add(customerNameLbl);

foreach (TFSContract contract in pair.Value)

{

ContractStatusBarControl status = new ContractStatusBarControl();

status.WidthPercent = GetFillPercent(contract.NumberOfTasks, contract.NumberOfFinishedTasks);

string[] contractNameParts = Regex.Split(contract.Contract.Name, @"[A-Z]{3}-[0-9|A-Z]{2}-[0-9|A-Z]{2}", RegexOptions.IgnoreCase);

ContractDetailControl detail = new ContractDetailControl();

detail.ContractName = contractNameParts.Last();

detail.DateStarted = contract.StartDate;

detail.DateFinished = contract.FinishDate;

detail.StatusBar = status;

ContractTileControl tile = new ContractTileControl();

Match match = Regex.Match(contract.Contract.Name, @"[A-Z]{3}-[0-9|A-Z]{2}-[0-9|A-Z]{2}", RegexOptions.IgnoreCase);

if (match.Value.Length != 0)

{

tile.ContractNumber = match.Value;

}

tile.ContractTasksFinished = contract.NumberOfFinishedTasks;

tile.ContractTasksTotal = contract.NumberOfTasks;

ContractControl contractControl = new ContractControl();

contractControl.ContractID = contract.Contract.Id;

contractControl.TileControl = tile;

contractControl.DetailControl = detail;

//contractListPlaceHolder.Controls.Add(contractControl);

controls.Add(contractControl);

}

}

myRepeater.DataSource = controls;

myRepeater.DataBind();

}

The table gets created, but only the non-composite part contractMainDiv of ContractControl gets rendered, as the Repeater insists that both tileControl and detailControl are null, even though they are properly set to instances of their respective types.

网友答案:

When the Repeater is data-bound, it creates an instance of the ItemTemplate for each item in the data-source, set its DataItem to the item from the data-source, and data-binds the children.

In this case, the item from the data-source is an instance of your ContractControl, and your ItemTemplate has no data-binding, so you'll end up with a blank instance of the ContractControl for each item you've added to the list.

The quick and dirty solution is to add a handler for the ItemDataBound event of your Repeater, and copy the properties to the real control:

protected void myRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
   switch (e.Item.ItemType)
   {
      case ListItemType.Item:
      case ListItemType.AlternatingItem:
      case ListItemType.SelectedItem:
      case ListItemType.EditItem:
      {
         var source = (ContractControl)e.Item.DataItem;
         var destination = (ContractControl)e.Item.FindControl("contractControl");
         destination.ContractID = source.ContractID;
         destination.TileControl = source.TileControl;
         destination.DetailControl = source.DetailControl;
         break;
      }
   }
}

A better solution would be to bind your Repeater to a list of TFSContract objects, and moving the code to build the ContractControl into the ItemDataBound event handler.

EDIT
Updated to only process real items, ignoring headers, footers, etc.

相关阅读:
Top