CodeVerge.Net Beta


   Explore    Item Entry    Members      Register  Login  
NEWSGROUP
.NET
Algorithms-Data Structures
Asp.Net
C Plus Plus
CSharp
Database
HTML
Javascript
Linq
Other
Regular Expressions
VB.Net
XML

Free Download:




Zone: > NEWSGROUP > Asp.Net Forum > windows_hosting.hosting_open_forum Tags:
Item Type: NewsGroup Date Entered: 6/28/2005 6:41:55 PM Date Modified: Subscribers: 0 Subscribe Alert
Rate It:
(NR, 0)
XPoints: N/A Replies: 1 Views: 30 Favorited: 0 Favorite
Can Reply:  No Members Can Edit: No Online: Yes
2 Items, 1 Pages 1 |< << Go >> >|
skysigal
Asp.Net User
Help with advanced CompositeControl Property Wiring/Forwarding troubles.6/28/2005 6:41:55 PM

0/0

The questions I have are around forwarding/wiring properties intelligently, and flexibly from the outside of a CompositeControl to the child Controls.

The following is pretty long -- but I think required to point out the various holes I've already tried and climbed back out of...


In essense, I've come across two methods to do this (Nikhil's versus ASP.NET's team) -- but each with their own costs.

Can someone help?



To demonstrate the issue that I am trying to solve, let's consider the following CompositeControl:

    • CompositeControl
      • Properties
        • string ButtonText {get;set;}
        • eButtonType ButtonType {get;set;} //Image or Link
        • eListControlType ListControlType {get;set;}
        • ListItemCollection Items {get;}
      • ChildControls
        • Control InnerButton; //Image or Link
        • ListControl InnerListControl;



A disection of the two approaches:

#1: The Nikhil Approach ("Direct Wiring"): EnsureChildControls() everywhere
If you've bought Nikhil's great book on making control's you're going to see the following solution to forwarding these properties:

//simple property
public string ButtonText {
   get {EnsureChildControls();return _InnerButton.Text;}
   set {EnsureChildControls();_InnerButton.Text = value;}
}

//complex property:
public ListItemsCollection Items {
   get {EnsureChildControls();return _InnerListControl.Items;}
}

//ViewStated parameter of this control.
public eListControlType ListControlType {
   get {object o=ViewState["ListControlType"];return (o!=null)?(eListControlType)o:eListControlType.DropDownList;}
   set {
      ViewState["ListControlType"] =value;
      
ChildControlsCreated=false; //if childs already created, have to rebuild everything.
   }
}

public eButtonType ButtonType{
   get {object o=ViewState["ListControlTypeA"];return (o!=null)?(eListControlType)o:eListControlType.DropDownList;}
   set {
      ViewState["ListControlTypeA"] =value;
      ChildControlsCreated=false; //if childs already created, have to rebuild everything.
   }
}



ControlBuilder, and the Order of Processing Attributes
Now, the ControlBuilder, after investigation, turns out that has absolutely no atomic behavior and transfers the parameters from the declarative tags to the control's properties immediately, one by one, in ALPHABETIC order, then doing its nested tags, also in alphabetic order...

In other words, it will parse:

<CC1:MyControl ButtonType="Button" ListControlType="DropDown" ButtonText="Submit">
   <Items>
      <option>Uno</option>
      <option>Dos</option>
      <option>Tres</option>
   </Items>
</CC1>

into C# that does about the same thing as :

   MyControl ctrl = new MyControl();
   //Set properties before adding control to page and triggering its TrackViewState
   ctrl.ButtonText = "Submit"; //'b' comes before 'l'...and CreateChildControls() is called first time...
   
ctrl.ButtonType = eButtonType.Button;//...which resets the ChildControlsCreated flag!
   ctrl.ListControlTypeA = eListControlType.DropDown;//...and CreateChildControls() is called again, then resets the ChildControlsCreated flag!
   ctrl.Items.Add(new ListItem("Uno"));//...and CreateChildControls() is called again...
   ctrl.Items.Add(new ListItem("Due"));
   ctrl.Items.Add(new ListItem("Tres"));
   //add to page, and turn ctrl's tracking on:
   this.Controls.Add(ctrl);

What this means is that, by setting the ListControlType, it's going to reset CreateChildControls() (has to!), and then the Items.Add() statements are going to force EnsureChildControls()->CreateChildControls() to be called again...and in the process, the value of ButtonText is going to get lost.

Solution: Move application of ButtonText to PrepareControlHierarchy()
Yes...so tis true that we could change our code around so that the Attributes (not the Nested Items) are applied later:


//ViewStated parameter of this control:
public eListControlType ButtonText {
   get {object o=ViewState["ButtonText"];return (o!=null)?(string)o:string.Empty;}
   set {ViewState["ButtonText"] =value;
   }
}

in order to apply it much later:
void PrepareControlHierarchy(){
   //Apply cosmetic stuff after ViewState saved:
   ...
   _InnerButton.Text = this.Text;
   ...
}

Yes. That's true. But frankly, this is also a simple composite control, where such a solution is possible. But
in a more real-world scenario, a little more complex, it doesn't.

For example, the new Login control is built as a TemplateContainer+Container scenario, where it has a nested property of Items, then Templates:
<MyControl>
   <Template>
      <Select id="Categories"/>
   </Template> 
   <Items>
      <option>Uno</option>
   </Items>
</MyControl>

Here, as we saw above, the Control builder will ALWAYS read Items before Template -- so it will force EnsureChildControls() in the Items {get;} before it has a user-defined template and use a pre-supplied Defaulttemplate if the control is correctly built..
This means that later , when Template property gets set, it will have to reset ChildControlsCreated...
In other words, I will lose the Items put into the DefaultTemplate.Items!

Solution: Can ControlBuilder be taught to process attributes in a different order?
I've peered in at the code that's in ControlBuilder...and other than a Filtering of some kind going on that I have no idea what/where its coming from/what it does, most of the properties are marked as Internal, so there's not much leeway to surgically teach ControlBuilder how to process first properties that are marked with a custom attribute saying ("MeFirstPlease")...Or is there a way?

Storing local copies of variables set
I've mucked around trying to not lose these properties by doing things like:

public string ButtonText {
   get {return (base.ChildControlsCreated)?_InnerButton.Text:_CopyOfButtonText;}
   set {_CopyOfButtonText=value;EnsureChildControls();
}





The NET Framework approach ("InDirect Wiring"): 2Stepped approach
This seemed to me to be a serious issue, so I Reflected on how the ASP.NET team built their controls in System.Web.UI.WebControls, and I see a different pattern...which I've tentatively named InDirect Wiring.

Basically, the design pattern is based on the fact that the container control becomes the ViewState handler for the child controls, and the property get/set statements look like this:

public string ButtonText {
   get {object o=ViewState["ButtonText"];return (o!=null)?(string)o:string.Empty;}
   set {ViewState["ButtonText"] =value;}
}
public ListItemsCollection Items {
   get {
      if (_Items == null){
         _Items = new ListItemCollection();
         if (this.IsTrackingViewState){ ((IStateManager)_Items).TrackViewState();}
      }
      return _Items;
   }
}
private ListItemCollection _Items;

What's happening here is that even if the inner controls have viewsated Text and Items properties, we're going to remake them
here in the outer container control, viewstating them in the outer control, and  later transfer them to the  nested child controls in
such a way that the child controls don't persist the stuff as well...

protected overrided void CreateChildControls(){
   Button btn = new Button();
   //transfer property from outer to inner before control's TrackViewState is turned on:
   _btn.Text = this.ButtonText;
   //Add, which turns on ctrl's trackviewstate():
   btn.Controls.Add(_InnerButton);

   //Depending on type chosen, make a different ListControl derivative:  
   ListControl listControl = (ListControlType==DropDown)?new DropDownList():new ListBox();
   //transfer items from outer to inner before control is added to its container:
   foreach (ListItem item in this.Items) { listControl.Items.Add(item); }   
   //Add, which turns on ctrl's trackviewstate():
   this.Controls.Add(listControl);
}

At this point, when I had found these sources, I thought that I had found the solution to my problems-- the solution that the ASP.NET team is using would not lose the ButtonText property if eListType was modified...

... but it causes two other nightmares.

The Items Collection is out of Date
First of all, the Items property is only looking at current data up till CreateChildControls(). After that, if I ask for its contents it will show me old stuff (ie what was added declaratively). If I add an Item to the InnerControl's.iTems collection due to an eventhandler, it won't be reflected on the outside.

Solutions Attempted have been allong the following lines:

The 'Moving target' solution:
public ListItemCollection Items {
   get {
      if (!ChildControlsCreated){
         if (_Items == null){
            _Items = new ListItemCollection();
            if (this.IsTrackingViewState()){(IStateManager)_Items).TrackViewState();}
         }
      }else{
         //Children were built, so list contents has been copied to inner control.
         //_Items is no longer relevant.
         _InnerListControl.Items;
      }
   }
}

but that's doesn't pan out as a solution as far as I can tell.
It not only confuses me no end, but I'm probably confusing the computer too.

Deferring to PrepareControlHierarchy:
Instead of transfering the declaratively added items to the inner control in CreateControls(), wait till PrepareControlHierarchy() -- if any databinding must be done in between those two moments, do it to the container's Items, and not the inner ListControl...

The Pros: everything is unified, and appears to actually works for once... the Items is the right list, and the selectedIndex could actually work.
The Cons: upon postback the SelectedIndexChanged will always yield -1 as a result, because the inner control's list has only 0 elements in it at the time of processing the event  (it won't have more elements till PrepareControlHierarchy which happens much later, during Render).


ViewState the ChildControl, not the outer control's list.
This seems better logic to me -- much like Nikhil's Direct Wiring approach in some regards, but I havn't seen how exactly.
For one, it brings back the need for the 'moving target' if/else clauses demonstrated above,
secondly... this approach is going to yield better results when we wrap around a GridView, or any other Templated databound control, simply because there is no IStateManaged object to work with in those cases -- we need the child controls of the GridView to have enough info to build themselves properly in CreateChildControls, rather than late in PrepareChildControls or other solution. Atleast I think that will be an issue.


HELP. HELP.HELP.
In essense I've scanned the whole net framwork for a single control that wraps around a ListControl or GridView or DataGrid, and exposes its SelectedIndex property, and/or Items collection and there isn't one, so I have no examples of how to do it properly.


Very many thanks for reading this far -- I know it was long -- but hopefully I will get a complete answer from someone who has figured it out.
I'm just hoping that I am missing something really obvious, and that it can be done...


Thank you. 
Sky Sigal




 

skysigal
Asp.Net User
Re: Help with advanced CompositeControl Property Wiring/Forwarding troubles.7/2/2005 11:17:13 AM

0/0

There must be an equation somewhere:

"Number of lines in post one puts up, asking for help * The number of days the post is up with no one responding = The dumber you'll feel when you figure it out..."...

Uh...It turns out that ...in the above example Template is parsed before Items... even if alphabetically later... (Contrary to my initial conclusions...)

In fact, turns out that ControlBuilder works with way...it looks for Templates to parse and set first, before attaching the attributes (alphabetically), then the complex nested properties(alphabetically), then the bound properties(alphabetically)...

The reason I was not getting this fortuitous result before was that the above example is a simple one whereas I was working with a more complex control that had several templates: so in good oop tradition I nested them in their own specialized class, resulting in tags that looked like:

<MyControl>
   <Items>
     ...Uno/dos/tres...
   </iTems>
  <Templates>
      <Main>
       ...a template containing a DropDownList for the above Items...
      </Main>
      <Header> </Header>
     <Footer></Footer>
     <Items></ITems>
</Templates>
</MyControl>

So...what was happening was that ControlBuilder scanned immediate children of the outer tags, and saw Templates  as a complex property, not an ITemplate...And it doesn't scan deeper ---- so the result is that it processed attributes, then Complex (ie 'Templates')-- and never did the nested templates till much later.

If I rewrote it as:

<MyControl>
   <Items>
     ...Uno/dos/tres...
   </iTems>
      <Main>
       ...a template containing a DropDownList for the above Items...
      </Main>
      <Header> </Header>
     <Footer></Footer>
     <Items></ITems>
</MyControl>


No problem...sees the templates first, and sets those, and then moves on to others (Items).

Therefore, whew...lucky...
JUST WISH THAT THE DOCUMENTATION MADE REFERENCE TO THIS VERY IMPORTANT PIECE OF INFORMATION. GRRR.


As for the last piece: would still be very useful to force the order of how the attributes are read.
I have a ListControlType property that needs to be parsed BEFORE CreateChildControls is called..

At present, the only way to do this is to force the name to be alphabetically first, by appending a "_" suffix to it... which is really not very kosher.
I would have much preferred being able to set a custom Attribute that told ControlBuilder to deal with it first...

Anyway...feeling elated that this issue is finally resolved -- just wish my cheeks were a little less red from embarrasement
:-)

2 Items, 1 Pages 1 |< << Go >> >|


Free Download:


Web:
CodeProject: Eventing Within Composite Web Custom Controls. Free ... Next, we wire up the event handlers so that all the controls will be .... If you are making a Composite control that doesn't have a Text property to set, ...
Nikhil Kothari's Weblog : Atlas M2.2 - Dynamic UpdatePanels (finally) It is a simple PhotoViewer composite control that contains an Image control, ..... but allow wiring the UpdatePanels to the ProgressPanel (like with ...
TheMSsForum.com >> Asp >> ASP Composite Web Control and Position ... Thanking you in advance visiting and look forward to assist you. Regards, Rath Nepal Tours and ..... I have created a Composite Control in C# 2005 for ASP. ...
AJAX Control Toolkit - Issue Tracker Create a composite control that contain controls from the toolkit and ...... need help to fix the error. thanx in advance regards, debjit ...
TheMSsForum.com >> MFC >> CListCtrl::EnsureVisible question - The ... Hi everyone, I am looking for a good ActiveX composite control ... Many thanks in advance newsgroupie Tag: CListCtrl::EnsureVisible question Tag: 463611 ...
TRULY Understanding ViewState One thing that could help reduce the viewstate size in this case though is ..... on the page as you move step forward, and hence will be heavy on wire even ...
TEACHING AND EXAMINATION SCHEME FOR THIRD YEAR ENGINEERING (SUGAR ... Material of construction and its mechanical properties, Determination of total No. of ..... Magneto abrasive finishing, Abrasive flow machining, Wire EDM, ...
TRULY Understanding ViewState - Infinities Loop NET's ability to wire up declared attributes to control properties. ..... The trouble is ASP.NET does not provide an easy way to programmatically initialize ...
DotNet Treasure Electricity doesn't move through a wire but through a field around the wire. ..... the IIS metabase and forwarding the request to the right worker process. ...
C++: MSDN Magazine Articles The next version of Visual Studio (code-named “Orcas”) will help C++ developers ..... Prevent the sizing of the column headers in an ATL composite control. ...




Search This Site:










forms ldapauthentication windowsidentity - help using forms authentication to network resource

treeview control - how to get depth of selected node on client side.

looking for great hosting site

forms authentication login page problem

using regular asp.net components

framework 2.0, vs 2005, membership functionality via oracle?

how refresh control dll

content management.

vs 2008 freezes completely - must kill in task manager and restart

vwd -> vs 2005 pulls from vwdwebcache/ dir??

3.0.8 template creation/use

visual studio 2005 function -- exclude from project

mysql thought...

authentication problem

overcoming layout limitations

about dnn 212

dnn 2.12 running extremely slow - why?

how do i setup a user on my webserver that has bare minimum rights to use my web app but nothing else.

save webpartzone width after posting

data sources window

login control and profile initialization

break the text in side a node

problem importing control in asp.net 2.0

controls position problem

free/cheap skin for school

configuration error: access to the path "d:\inetpub\dotnetnuke\web.config" is denied.

asp.net tab

sorting a hybriddictionary?

using asp:login control... how to connect to my own database?

asp.net & classic asp authentication

 
All Times Are GMT