Using custom ControlBuilder to parse deep nested sub controls?

S

Sky

Hello: Been struggling a little bit today trying to understand how to use
the ControlBuilder to make a nested menu system.

If the HTML should look something like:

<CC:OutlookBar runat=server id=TheMenu>
<OutlookBarItem IsFolder=true Label=FolderA>
<OutlookBarItem IsFolder=FALSE Label=Item A.1/>
<OutlookBarItem IsFolder=FALSE Label=Item A.2/>
<OutlookBarItem IsFolder=FALSE Label=Item A.3/>
</OutlookBarItem>
<OutlookBarItem IsFolder=true Label=FolderB>
<OutlookBarItem IsFolder=FALSE Label=Item B.1/>
<OutlookBarItem IsFolder=FALSE Label=Item B.2/>
<OutlookBarItem IsFolder=FALSE Label=Item B.3/>
</OutlookBarItem>
<CC:OutlookBar>


I found that it was correctly able to handle the HTML parsing as long as
there was no second level nested layers (ie Items) -- only Folders... (which
doesn't forbode well for my future ideas of making a recursive menu ....)

I'm not sure which way to go in the code that follows:
a) If I keep the ParseChildren(true," OUTLOOKBARITEM) on the OutlookItem,
it correctly adds nested tags to the _Children IList -- but as
LiteralControls. Not as OutlookItems...so fails at rendering later.
b) If I remove it, I get an error saying "TEMPLATES cannot have Properties"
c) I am wondering if there is a way to append a MyBuilder to the children so
that it can recurse deeper -- I would think AppendSubBuilder is just for
that -- but no examples of such on the web.
d) ...?


The code parts are as follows:

[ParseChildren(ChildrenAsProperties =
false),PersistChildren(true),ControlBuilderAttribute(typeof(MyBuilder))]
public class OutlookBar(){
.. . .
protected override void AddParsedSubObject(Object obj) {
if (obj is XOutlookBarItem){_Items.Add((XOutlookBarItem)obj);}
}
.. . .
}//Class:End

[ParseChildren(true,"OUTLOOKBARITEM"),PersistChildren(true),ControlBuilderAt
tribute(typeof(MyBuilder))]
public class XOutlookBarItem {
//private fields
bool _IsFolder=false;
string _Label = string.Empty;
string _Url = string.Empty;
string _ImgUrl = string.Empty;
ArrayList _List = new ArrayList();
//public properties
public bool IsFolder {get {return _IsFolder;}set{_IsFolder =
value;}}
public string Label {get {return _Label;}set{_Label = value;}}
public string Url {get {return _Url;}set{_Url = value;}}
public string ImgUrl {get {return _ImgUrl;}set{_ImgUrl = value;}}
public IList OUTLOOKBARITEM{get{return _List;}}}
//Constructor:
public XOutlookBarItem(){}
}



And the builder as:

public class MyBuilder : ControlBuilder {
public override Type GetChildControlType(string tagName, IDictionary
attribs) {
if (tagName.ToUpper().EndsWith("OUTLOOKBARITEM")){
return typeof(XOutlookBarItem);
}
return null;
}
public override void AppendLiteralString(string s) {}// Ignores literals
between rows.
public override bool AllowWhitespaceLiterals(){return false;}
public override void AppendSubBuilder(System.Web.UI.ControlBuilder
subBuilder){
string tCheck = subBuilder.GetType().ToString();
//subBuilder = new MyBuilder();
}
}


(Are you still there after such a long intro =;-)?


PS: I have no idea if it makes any sense to add the
PersistChildren(true),ControlBuilderAttribute(typeof(MyBuilder)) attributes
to the OutlookBarItem class, as it is not a Control. Only works with
controls, right? And that would be handled by the same attributes on the
parent control (OutlookBar), so it is no use, right? Just checking.


Thanks!
 
K

Kevin Bilbee

How did you get this far? What documentation did you read to create your
control to this point????

Any help would be great. I am stuck and do not know if I am even in the
write direction with my code. I would like to read a tutorial about the
subject with full examples. I have the Developing MS ASP.NET Server
Controls book that I inherited from a former employee but there is no CD and
the book kkps refering to the CD for the full example.


Kevin Bilbee

Sky said:
Hello: Been struggling a little bit today trying to understand how to use
the ControlBuilder to make a nested menu system.

If the HTML should look something like:

<CC:OutlookBar runat=server id=TheMenu>
<OutlookBarItem IsFolder=true Label=FolderA>
<OutlookBarItem IsFolder=FALSE Label=Item A.1/>
<OutlookBarItem IsFolder=FALSE Label=Item A.2/>
<OutlookBarItem IsFolder=FALSE Label=Item A.3/>
</OutlookBarItem>
<OutlookBarItem IsFolder=true Label=FolderB>
<OutlookBarItem IsFolder=FALSE Label=Item B.1/>
<OutlookBarItem IsFolder=FALSE Label=Item B.2/>
<OutlookBarItem IsFolder=FALSE Label=Item B.3/>
</OutlookBarItem>
<CC:OutlookBar>


I found that it was correctly able to handle the HTML parsing as long as
there was no second level nested layers (ie Items) -- only Folders... (which
doesn't forbode well for my future ideas of making a recursive menu ....)

I'm not sure which way to go in the code that follows:
a) If I keep the ParseChildren(true," OUTLOOKBARITEM) on the OutlookItem,
it correctly adds nested tags to the _Children IList -- but as
LiteralControls. Not as OutlookItems...so fails at rendering later.
b) If I remove it, I get an error saying "TEMPLATES cannot have Properties"
c) I am wondering if there is a way to append a MyBuilder to the children so
that it can recurse deeper -- I would think AppendSubBuilder is just for
that -- but no examples of such on the web.
d) ...?


The code parts are as follows:

[ParseChildren(ChildrenAsProperties =
false),PersistChildren(true),ControlBuilderAttribute(typeof(MyBuilder))]
public class OutlookBar(){
. . .
protected override void AddParsedSubObject(Object obj) {
if (obj is XOutlookBarItem){_Items.Add((XOutlookBarItem)obj);}
}
. . .
}//Class:End

[ParseChildren(true,"OUTLOOKBARITEM"),PersistChildren(true),ControlBuilderAt
tribute(typeof(MyBuilder))]
public class XOutlookBarItem {
//private fields
bool _IsFolder=false;
string _Label = string.Empty;
string _Url = string.Empty;
string _ImgUrl = string.Empty;
ArrayList _List = new ArrayList();
//public properties
public bool IsFolder {get {return _IsFolder;}set{_IsFolder =
value;}}
public string Label {get {return _Label;}set{_Label = value;}}
public string Url {get {return _Url;}set{_Url = value;}}
public string ImgUrl {get {return _ImgUrl;}set{_ImgUrl = value;}}
public IList OUTLOOKBARITEM{get{return _List;}}}
//Constructor:
public XOutlookBarItem(){}
}



And the builder as:

public class MyBuilder : ControlBuilder {
public override Type GetChildControlType(string tagName, IDictionary
attribs) {
if (tagName.ToUpper().EndsWith("OUTLOOKBARITEM")){
return typeof(XOutlookBarItem);
}
return null;
}
public override void AppendLiteralString(string s) {}// Ignores literals
between rows.
public override bool AllowWhitespaceLiterals(){return false;}
public override void AppendSubBuilder(System.Web.UI.ControlBuilder
subBuilder){
string tCheck = subBuilder.GetType().ToString();
//subBuilder = new MyBuilder();
}
}


(Are you still there after such a long intro =;-)?


PS: I have no idea if it makes any sense to add the
PersistChildren(true),ControlBuilderAttribute(typeof(MyBuilder)) attributes
to the OutlookBarItem class, as it is not a Control. Only works with
controls, right? And that would be handled by the same attributes on the
parent control (OutlookBar), so it is no use, right? Just checking.


Thanks!
 
S

Sky

Hi Kevin:

Read Building ASP.NET Server Controls -- Apress. Damn good.

As for how to get this far (and further since I was able to solve it a
couple of hours after I posted :) ) These are the points I've distilled so
far:

a) There are several levels of controls:
i) basic control that inherits from Control. That means you generally
have to implement IPostBackEventHandler and/or IPostBackDataHandler (i think
those are the names -- atleast they are something like that). Plus you have
to have a pretty good idea of Styles/ViewState...therefore, to solve that
kind of headache, I suggest moving up the food chain to:
ii) basic control that inherits from WebControl. That's pretty easy...
An obvious example would be inherit and extend something like Label.
iii) Composite control -- now things get a little interesting here.
Again, I have experimented with enheriting the wrapper control from
Control -- but frankly, inheriting from WebControl makes it much easier.
iv) Control that has sub-elements. Eg: a SELECT like control that has
OPTIONS (or Asp:ListItem). I'll come back to this in a second.
v) Controls that have sub templates
vi) Controls that have nested Sub-Elements (which is what the original
post was about).


This all took me a little to 'see'/figure out, hence why I am emphasizing
that not all controls are the same -- the last 3 are the ones we are talking
about now.

Lets go back to the iv) first -- I was making a Combo (editable pulldown),
and I was having a lot of trouble on figuring out how to add OPTIONS to
it... Turns out that the secret is the attribute
[ParseChildren] attribute...that you put above the Control : WebControl def.
There are 3 ways it can be written - and they do different things:

a) ParseChildren(false) -- means "Any subelements are NOT Properties - - so
has to fit other criteria".
b) ParseChildren(true) -- means "Parse child tags and try to match them to
Properties. Something like

<cc1:MyControl>
<ForeColor >
</cc1:MyControl>
(or something like that -- frankly I havn't used it this way yet so I'm not
sure how that would look-- but it's something like this...)

and finally
c) ParseChildren(true,"ITEMS") -- which is the beginning of where things get
interesting. This states Parse sub tags as properties -- and if you see
<items> then add then to a collection given by the control.
in other words this would be valid if

<cc1:MyControl>
<asp:listItem Text="John">
<asp:listItem Text="Sam">
<asp:listItem Text="Mary">
</cc1:MyControl>

What this will do is take all sub elements found, and try to add it to a
public property you have provided in your control that must be called Items
since you declared in the attribute that it would be so:

public ArrayList Items {get {return _List;}}

Did you get that part? -- it takes all tags and shoves them in List... in
other words this is valid to -- although probably not at all what you want:
<cc1:MyControl>
<asp:listItem Text="John">
<asp:listItem Text="Sam">
<asp:Label Text="Damn">
</cc1:MyControl>

This will still stick the item in the Items arrayList -- but probably cause
you headaches at Render() time.

Therefore -- although that looked sooo easy -- it's usually a red herring --
so everything I just said ignore, and let's point out a more flexible, but
managaed, way:

This time make the attribute like this
[ParseChildren(ChildrenAsProperties = false)]


AND override the Control's AddParsedSubObject method to only add items of a
specific type:

protected override void AddParsedSubObject(Object obj) {
if (obj is ListItem){_Items.Add((ListItem)obj);}else {/*ignore
me*/}
}

See where this is going? AddParsedSubObject always happened - -but now you
are controlling which items to add... You can tell it to ignore the Label --
or any Literals that got in there by accident/whatever.

We are starting to get where we want -- although it ticks me off that I have
to use "asp:ListItem" rather than OPTION -- it's much more verbose, and
frankly I'm sure that I will type it wrong...so...I have to figure out a way
to get it to accept tags that start with <OPTION....But...


There is still one hitch though....ASP is not very smart: It will recognize
any item that is ASP: tagged (eg: ListItems) but is stupid as a plank if you
want it to recognize something like plain HTML. And anything it doesn't
recognize it will treat as HtmlGenericControl.

In other words, if you typed something like

<cc1:MyControl>
<option Text="John"/>
<option Text="Sam"/>
<option Text="Mary"/>
</cc1:MyControl>

AddParsedSubObject WILL be called -- but not 3 times -- only once!!!, being
passed one big HtmlGenericControl whose contents are '<option
Text="John"/><option Text="Sam"/><option Text="Mary"/>"...
Ie bluddy useless.

The problem is because ASP has a default controlBuilder -- a fancy name for
customparser? -- that only looks for Asp tags. You have to make one that is
a bit smarter, looking out for your own tags.
Good news is that it is really simple -- in essense you are asking it to
look out for startTags that match what you are looking for:

a) Stick this on your control btw:
ControlBuilderAttribute(typeof(MyBuilder))

b) An example of a Builder

With this in place you can then type:

<cc1:MyControl>
<option Text="John"/>
<ITEM Text="Sam"/>
<option Text="Mary"/>
<asp:listitem Text="Mary"/>
<asp:Label Text="Discard me">
</cc1:MyControl>


You will end up with AppendLiteralControl being called 3 times -- and
ignoreing the Label. Much nicer.


You just have to finish up a rendering that does something like

PreRender(){
foreach (ListItem X in _Items){
MySelect.Items.Add(X);
}
}



I think that just about covers Type 3.

As for the more complicated version -- a Tree that parses its children
deeper than 1 -- something like a Menu control as I was writting this
week -- It's so similar -- I just couldn't see it at first.

The error was two part:
a) I should have made the attribute:

[ParseChildren(true,"OUTLOOKBARITEM"),PersistChildren(true),ControlBuilderAt
tribute(typeof(MyBuilder))]

on the OutlookBarItem... It's not at all what is happening...

It should have been

[ParseChildren(false),PersistChildren(true),
ControlBuilderAttribute(typeof(MyBuilder))]

AND

I should have realized that the OutlookItem -- which is my custom ListItem
on steroids -- needs to be (duh) a WebControl for it to parse/render.







Anyway -- the corrected (and working -- although I have not added the JS and
CSS to make it work) code is attached below -- and it parses correctly as I
intended...



I hope that these notes helped rather than confuse you even more...:) Let
me know -- and I'll try to make another stab at explaining it if I was as
clear as mud :)

But buy the book. Was money well spent in my opiou nion. It doesn't answer
everything (for example I had to figure out how to read nested sets by my
self) but I don't think I could have without a leg up from the book.

Plus, As is amply clear by now -- I am neither good with code in the first
place, nor a good writer -- they are :)



Very best and good luck,

Sky

Ps: one last point -- there is still one last part to work out that i havn't
figure out: it renders in runtime -- but it's giving me the dreaded gray box
in the IDE. Something not's instantiated in Design mode? Grrr.








The corrected code is as follows:
public class MyBuilder : ControlBuilder {

public override Type GetChildControlType(string tagName, IDictionary
attribs) {

if ((tagName.ToUpper().IndexOf("FOLDER")>-1)||

(tagName.ToUpper().IndexOf("ITEM")>-1)||

(tagName.ToUpper().IndexOf("NODE")>-1)){

return typeof(XOutlookBarItem);


}

return null;

}

public override void AppendLiteralString(string s) {}

public override bool AllowWhitespaceLiterals(){return false;}

}

[ParseChildren(false),PersistChildren(true)]

[ ControlBuilderAttribute(typeof(MyBuilder))]

public class XOutlookBarItem : Control {

//==========================================================

//PRIVATE FIELDS

//==========================================================

XOutlookBar _ParentMenu =null;

XOutlookBarItem _ParentNode = null;

bool _IsFolder=false;

string _Label = string.Empty;

string _Url = string.Empty;

string _ImgUrl = string.Empty;

string _Target = "_self";

string _ToolTip = string.Empty;

//==========================================================

//PUBLIC PROPERTIES

//==========================================================

//----------------------------------------------------------

[Category(" XAct Appearance")]

public string ImgUrl {get {return _ImgUrl;}set{_ImgUrl = value;}}

[Category(" XAct Appearance")]

public string ImageUrl {get {return _ImgUrl;}set{_ImgUrl = value;}}

[Category(" XAct Appearance")]

public bool IsFolder {get {return _IsFolder;}set{_IsFolder = value;}}

[Category(" XAct Data")]

public string Label {get {return _Label;}set{_Label = value;}}

[Category(" XAct Data")]

public string Url {get {return _Url;}set{_Url = value;}}

[Category(" XAct Data")]

public string Target {get {return _Target;}set{_Target = value;}}

[Category(" XAct Data")]

public string Title {get {return _ToolTip;}set {_ToolTip =value;}}

[Category(" XAct Data")]

public string ToolTip {get {return _ToolTip;}set {_ToolTip =value;}}

[Browsable(false)]

public XOutlookBar ParentMenu {get {if (_ParentMenu!=null){return
_ParentMenu;}else{if (_ParentNode!=null){return
_ParentNode.ParentMenu;}}return null;}set{_ParentMenu = value;}}

[Browsable(false)]

public XOutlookBarItem ParentNode {get {return _ParentNode;}set{_ParentNode
= value;}}

//==========================================================

//CONSTRUCTOR

//==========================================================

public XOutlookBarItem():base(){

this.PreRender += new EventHandler(Page_PreRender);

}

//==========================================================

//LIFECYCLE

//==========================================================

protected override void AddParsedSubObject(Object obj) {

//Only allow Nodes as children:

if (obj is XOutlookBarItem){

XOutlookBarItem oNode = (XOutlookBarItem)obj;

string tCheck = oNode.Label;

oNode._ParentNode = this;

oNode._ParentMenu = this.ParentMenu;

this.Controls.Add(oNode);

}

}

protected void Page_PreRender(object sender, EventArgs e){

this.Controls.Add(_Render());

}

protected override void Render(HtmlTextWriter output) {

if ((this.Site != null) && (this.Site.DesignMode)){

this.ChildControlsCreated=false;

this.EnsureChildControls();

try{this.OnPreRender(EventArgs.Empty);}catch{}

}

base.Render(output);

}

//==========================================================

//PRIVATE METHODS

//==========================================================

private WebControl _Render(){

if (this.IsFolder){return _RenderAsFolder();}else{return _RenderAsItem();}

}

private WebControl _RenderAsFolder(){

Panel oDO = new Panel();

Panel oDIT = new Panel();oDO.Controls.Add(oDIT);

Label oLabel = new Label();oDIT.Controls.Add(oLabel);

oLabel.Text = this.Label;

Panel oDIB = new Panel();oDO.Controls.Add(oDIB);

oDO.CssClass = this.ParentMenu.CssClassFolder;

oDIT.CssClass = this.ParentMenu.CssClassFolderButton;

oLabel.CssClass = this.ParentMenu.CssClassFolderButtonLabel;

oDIB.CssClass = this.ParentMenu.CssClassFolderItemArea;

while (this.Controls.Count>0){

XOutlookBarItem oChild = (XOutlookBarItem)this.Controls[0];

//this.Controls.Remove(oChild); //Doesn't look like this is needed...

oChild.ParentMenu = this.ParentMenu;

oDIB.Controls.Add(oChild);

}

if (this._ToolTip != string.Empty){

oDIT.ToolTip = _ToolTip;

oDIT.Style["CURSOR"]="hand";

}

if (this.Url!=string.Empty){oDIT.Attributes["Url"] = this.Url;}

if (this.Target!=string.Empty){oDIT.Attributes["Target"] = this.Target;}

return oDO;

}

private WebControl _RenderAsItem(){

Panel oDO = new Panel();

oDO.CssClass = this.ParentMenu.CssClassItem;

Label oLabel = new Label();oLabel.Text = this.Label;

oLabel.CssClass = this.ParentMenu.CssClassItemLabel;

if (this.ParentMenu.ShowItemImages){

Image oImg = new Image();

if (this.ParentMenu.ItemImageAlignment == eAlign.Top){

oDO.Controls.Add(oImg);

oDO.Controls.Add(new LiteralControl("<br/>"));

oDO.Controls.Add(oLabel);

}else if (this.ParentMenu.ItemImageAlignment == eAlign.Right){

oDO.Controls.Add(oLabel);

oDO.Controls.Add(oImg);

}

else if (this.ParentMenu.ItemImageAlignment == eAlign.Left){

oDO.Controls.Add(oImg);

oDO.Controls.Add(oLabel);

}

else if (this.ParentMenu.ItemImageAlignment == eAlign.Bottom){

oDO.Controls.Add(oLabel);

oDO.Controls.Add(new LiteralControl("<br/>"));

oDO.Controls.Add(oImg);

}

if (this.ImgUrl!=string.Empty){oImg.ImageUrl=this.ImgUrl;}

oImg.CssClass = this.ParentMenu.CssClassItemImage + " " + "HOVER";

}else{

oDO.Controls.Add(oLabel);

}

if (this._ToolTip != string.Empty){

oDO.ToolTip = _ToolTip;

oDO.Style["CURSOR"]="hand";

}

if (this.Url!=string.Empty){oDO.Attributes["Url"] = this.Url;}

if (this.Target!=string.Empty){oDO.Attributes["Target"] = this.Target;}

return oDO;

}



}


public enum eAlign{

Top,

Right,

Bottom,

Left

}


/// <summary>

/// Description résumée de XOutlookBar.

/// </summary>

[ParseChildren(ChildrenAsProperties = false)]

[PersistChildren(true)]

[ControlBuilderAttribute(typeof(MyBuilder))]

public class XOutlookBar : WebControl {

//==========================================================

//EVENT HANDLING

//==========================================================

//==========================================================

//SUB ELEMENTS

//==========================================================

//==========================================================

//FIELDS

//==========================================================

//Javascript:

private const string _JSClassDefName = "XOutlookBar";

private const string _JSClassDefFileName = _JSClassDefName + ".js";

private string _JSClassDefPath = "XAct.Resources.aspx?Assembly="+
System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Assembly.GetNa
me().Name + "&" + "Res=";

static int _JSClassInstanceCounter = 0;

private string _JSClassInstanceName = string.Empty;

//----------------------------------------------------------

//Css:

private string _CssPath = "";

private string _CssClassFolder = "XOB_FOLDER";

private string _CssClassFolderButton = "XOB_FOLDER_BUTTON";

private string _CssClassFolderButtonLabel = "XOB_FOLDER_BUTTON_LABEL";

private string _CssClassFolderButtonImage = "XOB_FOLDER_BUTTON_IMAGE";

private string _CssClassFolderItemArea = "XOB_FOLDER_ITEMAREA";

private string _CssClassItem = "XOB_ITEM";

private string _CssClassItemImage = "XOB_ITEM_IMAGE";

private string _CssClassItemLabel = "XOB_ITEM_LABEL";

//----------------------------------------------------------

//Layout:

private bool _ShowFolderImages = true;

private bool _ShowItemImages = true;

private eAlign _FolderImageAlignment = eAlign.Right;

private eAlign _ItemImageAlignment = eAlign.Top;

//----------------------------------------------------------

//Folder Tracking:

private int _CurrentFolderID = 0;

//==========================================================

//PROPERTIES

//==========================================================

//----------------------------------------------------------

[Category(" XAct Behavior")]

public int CurrentFolderID {get {return _CurrentFolderID;}set
{_CurrentFolderID = value;}}

//----------------------------------------------------------

[Category(" XAct Appearance")]

public bool ShowFolderImages {get {return _ShowFolderImages;}set
{_ShowFolderImages = value;}}

[Category(" XAct Appearance")]

public bool ShowItemImages {get {return _ShowItemImages;}set
{_ShowItemImages = value;}}

[Category(" XAct Appearance")]

public eAlign FolderImageAlignment {get {return
_FolderImageAlignment;}set{_FolderImageAlignment = value;}}

[Category(" XAct Appearance")]

public eAlign ItemImageAlignment {get {return
_ItemImageAlignment;}set{_ItemImageAlignment = value;}}

//----------------------------------------------------------

[Category(" XAct Appearance - CSS")]

public string CssPath {get {return _CssPath;}set{_CssPath = value;}}

[Category(" XAct Appearance - CSS")]

public string CssClassFolder {get {return
_CssClassFolder;}set{_CssClassFolder = value;}}

[Category(" XAct Appearance - CSS")]

public string CssClassFolderButton {get {return
_CssClassFolderButton;}set{_CssClassFolderButton = value;}}

[Category(" XAct Appearance - CSS")]

public string CssClassFolderButtonLabel {get {return
_CssClassFolderButtonLabel;}set{_CssClassFolderButtonLabel = value;}}

[Category(" XAct Appearance - CSS")]

public string CssClassFolderButtonImage {get {return
_CssClassFolderButtonImage;}set{_CssClassFolderButtonImage = value;}}

[Category(" XAct Appearance - CSS")]

public string CssClassFolderItemArea {get {return
_CssClassFolderItemArea;}set{_CssClassFolderItemArea = value;}}

[Category(" XAct Appearance - CSS")]

public string CssClassItem {get {return _CssClassItem;}set{_CssClassItem =
value;}}

[Category(" XAct Appearance - CSS")]

public string CssClassItemImage {get {return
_CssClassItemImage;}set{_CssClassItemImage = value;}}

[Category(" XAct Appearance - CSS")]

public string CssClassItemLabel {get {return
_CssClassItemLabel;}set{_CssClassItemLabel = value;}}

//----------------------------------------------------------

//[TypeConverter(GetType(System.Drawing.ColorConverter))]




//==========================================================

//CONSTRUCTOR

//==========================================================

public XOutlookBar():base(HtmlTextWriterTag.Div){

ResSrvHandler.Install();

//Init Js:

_JSClassInstanceName = _JSClassDefName +
_JSClassInstanceCounter;_JSClassInstanceCounter +=1;

//Define path to Css Resource:

if (_CssPath == string.Empty){_CssPath = _JSClassDefPath +
_JSClassDefName+".css";}


//Wire up Handlers for Control Events:

this.PreRender += new System.EventHandler(this.Page_PreRender);

//Create Controls:

this.EnsureChildControls();

}

//==========================================================

//LIFECYCLE

//==========================================================

protected override void AddParsedSubObject(Object obj) {

if (obj is XOutlookBarItem){

//Add to Controls only if of the right type:

XOutlookBarItem oNode = (XOutlookBarItem)obj;

//This time I added them directly...no private _List . Seems to work fine.

this.Controls.Add(oNode);

}else{

//Ignore and discard...

}

}

protected override void CreateChildControls(){

//No Controls to add --- it's all done by AddParsedSubObject

ChildControlsCreated=true;

}

private void Page_PreRender(object sender, EventArgs e){

foreach (XOutlookBarItem oChild in this.Controls){

//Wire it up -- the sub items will need a pointer to me to get CSS layout
info

oChild.ParentMenu = this;

}

_Embed_JS();

_Embed_CSS();

}

protected override void Render(HtmlTextWriter output) {

if ((this.Site != null) &&
(this.Site.DesignMode)){try{this.OnPreRender(EventArgs.Empty);}catch{}}

base.Render(output);


}

//==========================================================

//PRIVATE METHODS

//==========================================================

private void _Embed_JS(){

Tools.EmbedScriptClassDefResource(false,this.Page,_JSClassDefName,
_JSClassDefFileName, _JSClassDefPath);

string tJS_Specific =

string.Format(

"<script>\n"+

"//CLASSINIT:BEGIN----------------------------------------------------------
-\n"+

_JSClassInstanceName + " = new
"+_JSClassDefName+"('{0}','{1}','{2}','{3}','{4}','{5}','{6}','{7}','{8}','{
9}','{10}');\n"+

"//CLASSINIT:END------------------------------------------------------------
-\n"+

"</script>\n",

this.ClientID,

_CurrentFolderID,

string.Empty,

this._CssClassFolder,

this._CssClassFolderButton,

this._CssClassFolderButtonLabel,

this._CssClassFolderButtonImage,

this._CssClassFolderItemArea,

this._CssClassItem,

this._CssClassItemLabel,

this._CssClassItemImage

);

Page.RegisterStartupScript(_JSClassInstanceName, tJS_Specific);

}



private void _Embed_CSS(){

if (!Page.IsStartupScriptRegistered(_JSClassDefName + "_CSS")){

string tCSS = string.Format("<link type=\"text/css\" rel=\"StyleSheet\"
href=\"{0}\"/>\n", _CssPath);

Page.RegisterStartupScript(_JSClassDefName + "_CSS", tCSS);

}

}

}
 
S

sica

Hi Sky!

This is very usufull information indeed.It help me a lot to understnad
more about server controls.

One thing that I'm not sure how to do it is how to control the order
the sub-controls are render.Let me show you an exemple:

<cc1:MyControl>
<option Text="John"/>
<ITEM Text="Sam"/>
<option Text="Mary"/>
<asp:listitem Text="Mary"/>
<asp:Label Text="Discard me">
</cc1:MyControl>

I would always like to render listitem-controls first,then label and
so on.Do you have an ideea how can I do that?Is it
ControlBuilderAttribute that govern this if I implemet my own Control
builder?

Thanks in advance!

Regards,
Sica
 
S

Sky Sigal

Hi Sica:
I am really happy you got something from what I wrote -- most of this stuff
is ...well, I don't yet a firm grip on it all. It's getting better -- but I
still sometimes start a new control, and I think I understand it -- and then
I try something simple ...and it doesn't work.

Anyway...

I think I understand your question -- but let me point out that in the
example given I was just trying to show that using a ControlBuilder one
could make a custom "SELECT" replacement control that could accept several
types of syntax for OPTION elements without bugging out... Ie, via the
ControlBuilder one could control the 'meaning' of the parsing -- and that
whether it were an asp:net element (eg: asp:listitem) which it will
automatically recognize as being an object of type listitem, or a custom tag
that it knows nothing of (eg 'option' or 'item', and it would have
translated it to a LiteralControl if you didn't provide a Builder) you can
force it to be 'seen/recognized' as being a ListItem, or any other Control
for that matter....In other words, the example shows that the ControlBuilder
that I supplied would take each item and make it a ListItem -- no matter if
I wrote asp:listitem, Option, or Item... The ControlBuilder also shows that
it can be made to ignore any other tags -- in other words, that asp:label
will be ignored.

This doesn't handle sorting though -- in fact once it has been parsed, and
added (or rejected/ignored) the stuff is no longer an Option, Item, or
whatever -- that was just some "text/xml" tag -- it is discarded I think
once it has been parsed, and it is now a Control of type ListItem (atleast
in terms of the example given).... And I don't know of a way to get back to
the original WRITTEN tag from that point...it's gone. History.
So -- sorting by html tag is not possible... I think one could only
rearrange these elements by their values at this point -- (ie .Text, or
other property)... with IComparable,etc. ?

If you do find that it is possible, could you post back your solution?
Thanks!

Sky
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,904
Latest member
HealthyVisionsCBDPrice

Latest Threads

Top