Hi Natty:
Was able to analyse your code, and I do understand the notion of injecting
prefix and suffix code around the base.Render(writer); statement.
But that -- although simple enough to accomplish -- completly destroys any
chance of injecting working buttons above and below.
What I am saying is that what I would like to accomplish is the following:
writer.Write("<div stuff above the enherited controls/><asp:button
runat=server onClick=ClickMe......./>");
base.Render(writer);
writer.Write("<asp:button runat=server...../></div>");
but obviously since these were injected after InstantiateControls(), they
are completly 'dead'.
They appear as buttons (maybe) but have never been delegated back to the
forms attached ClickMe1() event...
Parts of the puzzle that I have figured out to doing this include the
following steps...
Create and instantiate controls dynamically within the OnInit() event so
that later InstantiateControls() wires them up correctly to be
parts of the webform's visualstate,etc. wiring.
Just before getting out of OnInit() -- one has to move the descendant
Controls INTO the container we want -- by a combination of
Remove() and AddAt().
So far so good. If we stopped here, we would have mostly all the parts of
visual enheritence.
The only problem is that any controls created dynamically in the descendant
control -- such as the TextBox control you created in the descendant's
OnLoad() -- would not exist at this point, and therefore could not be moved
into place...
So, in the Render() event we have to repeat the Remove/AddAt process
here...(for some reason, it doesn't work at all if you don't do the
Add/Remove
loop in the OnInit()...nothing shows up on page).
At this point with the base classes code below, I have got the controls to
show up in the right place -- and correctly wired...but...well...I'm not
satisfied.
"What?! It works! Move on!" Yeah...well...true...but I don't understand
really why it works. Which means that I will lose a ton of time again on
another
project, repeating all my mistakes...etc.
So I have two questions.
a) In the OnInit() event, when I've created all the new controls dynamically
BUT NOT ADDED them to the page -- and you have only one DropDownList
in yours, does it report that Controls.Count = 2??? More disturbing, if you
look at their ID's..why are they BOTH THE SAME element?!?!
b) Why does this solution fail if I do not do the Loop/AddAt() -- to move
the children controls into the right location -- in BOTH OnInit and later
Render().
It appears to me that it should work fine if I just put it in the Render()
event. no?
c) And how could I improve the loop mechanism? In other words -- if class b
enherits from a -- is there any way that I can distinguish a.class[0] as
being from a rather than b? "wot?!" ...I think if you look at the loop you
will understand:
for (int i=(Controls.Count-1);i>-1;i--)
{
tC = Controls
;
if ((tC != ID_DivOuter) && (ID_DivLower.Controls.Contains(tC)==false))
{
try
{
// Controls.Remove(tC);
ID_DivLower.Controls.AddAt(0,tC);
}
catch {
s = "SHIT! " + tC.GetType().ToString();
}
}
}
Ok. The full class that works...but still puzzles me, is here:
namespace WebApplication3
{
using System;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
/// <summary>
/// Summary description for baseUC.
/// </summary>
public class baseUC : System.Web.UI.UserControl
{
private System.Collections.ArrayList _SubControls;
protected System.Web.UI.WebControls.ImageButton ID_ExpandBtn;
protected System.Web.UI.HtmlControls.HtmlGenericControl ID_DivOuter;
protected System.Web.UI.HtmlControls.HtmlGenericControl ID_DivUpper;
protected System.Web.UI.HtmlControls.HtmlGenericControl ID_DivLower;
public bool Expanded = true;
public bool Editable = true;
public string BgColor="";
public string Header="";
public string Subject="";
public string Body = "";
public System.Collections.ArrayList SubControls = new
System.Collections.ArrayList();
public System.Web.UI.Control SubControl = null;
public string SubControlSrc="";
private void Page_Load(object sender, System.EventArgs e)
{
// Put user code to initialize the page here
if (Session[this.UniqueID + "Expanded"]==null){Session[this.UniqueID +
"Expanded"] = Expanded;}
Expanded = (bool)Session[this.UniqueID + "Expanded"];
}
#region Web Form Designer generated code
override protected void OnInit(EventArgs e)
{
//Because the *.ascx file will be ignored, and therefore any and all
//html elements put here, this base classe's html and buttons need to
//created dynamically here:
//Another point: it needs to be created Here -- because if you make and
inject
//them into the page after InitializeComponent runs, you will get buttons --
but
//they won't be wired up into the event handling so are useless.
//Preamble:
//at the top of the page there are the following protected global vars
//so that Render event can re-access what is created here:
//protected System.Web.UI.WebControls.ImageButton ID_ExpandBtn;
//protected System.Web.UI.HtmlControls.HtmlGenericControl ID_DivOuter;
//protected System.Web.UI.HtmlControls.HtmlGenericControl ID_DivUpper;
//protected System.Web.UI.HtmlControls.HtmlGenericControl ID_DivLower;
//Declare local vars:
System.Web.UI.HtmlControls.HtmlGenericControl oDivOuter;
System.Web.UI.HtmlControls.HtmlGenericControl oDivUpper;
System.Web.UI.HtmlControls.HtmlGenericControl oDivLower;
System.Web.UI.HtmlControls.HtmlTable oTable;
System.Web.UI.HtmlControls.HtmlTableRow oRow;
System.Web.UI.HtmlControls.HtmlTableCell oCell;
System.Web.UI.WebControls.ImageButton oImg;
System.Web.UI.HtmlControls.HtmlGenericControl oBR;
System.Web.UI.Control tC;
string s;
//Create the Outer div, that will contain two inner divs:
oDivOuter = ID_DivOuter = new
System.Web.UI.HtmlControls.HtmlGenericControl();
oDivOuter.TagName = "div";
oDivOuter.ID = "DivOuter";
//Create the Upper-Inner div that will contain a table
//to hold icons, and container "Toolbar/Title"
oDivUpper = new System.Web.UI.HtmlControls.HtmlGenericControl();
oDivUpper.TagName = "div";
oDivUpper.ID = "ID_DivUpper";
oDivUpper.Attributes.Add("class","HEADER GRADIENT");
oDivOuter.Controls.Add(oDivUpper);
oTable=new System.Web.UI.HtmlControls.HtmlTable();
oTable.Border = 1;
oTable.ID ="TheTable";
oTable.Style.Add("width","100%");
oTable.Style.Add("background-color","#A0FFA0");
oDivUpper.Controls.Add(oTable);
oRow = new System.Web.UI.HtmlControls.HtmlTableRow();
oRow.ID = "TheRow";
oTable.Rows.Add(oRow);
oCell = new System.Web.UI.HtmlControls.HtmlTableCell();
oCell.Attributes.Add ("style","margin:0;padding:0;width:1;");
oCell.ID = "TheCell";
oRow.Cells.Add(oCell);
//Put the Expand/Collapse Icon in cell 1:
oImg = ID_ExpandBtn = new System.Web.UI.WebControls.ImageButton();
oImg.ID = "TheImg";
oImg.ID = "ID_ExpandBtn";
ID_ExpandBtn.Click += new System.Web.UI.ImageClickEventHandler
(OnClick_ExpandMe);
oImg.Attributes.Add("style","cursor:hand;width:16px;height:16px;margin:0;pad
ding:0;margin-left:4px;border:0;");
oImg.Attributes["title"]= "Expanded:"+Expanded.ToString();
oCell.Controls.Add(oImg);
//Put the Container's Title in Cell2:
oCell = new System.Web.UI.HtmlControls.HtmlTableCell();
oCell.InnerHtml = this.Subject; //Set text of Container TitleBar
oRow.Cells.Add(oCell);
//Move on to the Inner-Bottom Div -- in which we want to inject sub
controls...
oDivLower = ID_DivLower = new
System.Web.UI.HtmlControls.HtmlGenericControl();
oDivLower.TagName = "div";
oDivLower.ID = "ID_DivLower";
oDivLower.Style.Add("background-color","#A0A0FF");
oDivLower.InnerText = "This is the lower div... where the controls should
end up!";
oDivOuter.Controls.Add(oDivLower);
//Close with a break so that each control lines up vertically within the
uc_page_panel.ascx
oBR = new System.Web.UI.HtmlControls.HtmlGenericControl();
oBR.TagName = "br";
oBR.ID = "TheBR";
oDivOuter.Controls.Add(oBR);
int tTest = Controls.Count;
//WHY is tTEST=2 ??? I only have one enherited control visible from this
page at this point
//in time. Plus! both appears to be the same control...
for (int i=0;i<Controls.Count;i++)
{
tC = Controls;
s=(tC.ID);
}
//Move any controls already created in children into this component...
//***DESIGN FLAW***:
//But if you do it here, you won't catch any controls dynamically injected
//later by descendant control
//One other point. It's wierd, but if you remove this code, and rely on
doing
//it all in the Render() event -- you won't get squat. It seems you have to
//do it in 2 'halves'...one here, one repeat job in Render()
int iMax = Controls.Count -1;
for (int i=iMax;i>-1;i--)
{
tC = Controls;
//Remove from Controls -- and inject into one only:
// this.Controls.Remove(tC);
ID_DivLower.Controls.AddAt(0,tC);
}
this.Controls.Add(oDivOuter);
//
// CODEGEN: This call is required by the ASP.NET Web Form Designer.
//
InitializeComponent();
base.OnInit(e);
}
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.Load += new System.EventHandler(this.Page_Load);
// this.PreRender += new System.EventHandler(this.Page_PreRender);
}
public void OnClick_ExpandMe(object sender,
System.Web.UI.ImageClickEventArgs e)
{
Expanded = ! Expanded;
Session[this.UniqueID + "Expanded"] = Expanded;
}
private void Page_PreRender(object sender, System.EventArgs e)
{
}
protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
//We 'could' use the PreRender event to do this -- but then we would have to
wire in
//all descendant controls to point to it?
if (Expanded)
{
ID_ExpandBtn.ImageUrl =
"http://localhost/xlib/xlib_graphics/btn13/btn13_arrow_1a_down.gif";
ID_DivLower.Style["display"] = "inline";
}
else
{
ID_ExpandBtn.ImageUrl =
"http://localhost/xlib/xlib_graphics/btn13/btn13_arrow_1a_right.gif";
ID_DivLower.Style["display"] = "none";
}
System.Web.UI.Control tC;
string s = "";
for (int i=(Controls.Count-1);i>-1;i--)
{
tC = Controls;
if ((tC != ID_DivOuter) && (ID_DivLower.Controls.Contains(tC)==false))
{
try
{
// Controls.Remove(tC);
ID_DivLower.Controls.AddAt(0,tC);
}
catch {
s = "ERROR! " + tC.GetType().ToString();
}
}
}
/*
* MS SaMPLE:
if (HasControls()) {
for (int i=0; i < Controls.Count; i++) {
Controls.RenderControl(writer);
}
}
*/
base.Render(writer);
}
#endregion
}
}