TRULY Understanding Dynamic Controls (Part 2)

Part 1: Dynamic vs. Static
Part 2: Creating Dynamic Controls
Part 3: Adding Dynamic Controls to the Control Tree
Part 4: Because you don't know what to render at design time 

PART 2

Creating Dynamic Controls

Creating a dynamic control isn't just about "newing" one up. There are several different types of dynamic controls. There are also several different ways that dynamic controls can manifest themselves in your page's control tree, sometimes when you don't even realize it. As promised, understanding this will not only increase your understanding of dynamic controls, but of ASP.NET in general.

A Dynamic Control can be a:
  1. Server Control
  2. User Control
  3. Parsed Control

But no matter what kind of control it is (dynamic or otherwise), they all share the same base class: System.Web.UI.Control. All built-in controls, custom server controls, and user controls share this base class. Even the System.Web.UI.Page class derives from it.

This section will focus very specifically on how dynamic controls are created. So the following code samples aren't really complete. Just creating a control dynamically isn't enough to get it participating in the page life cycle because it must be added to the control tree. But I like focusing in one thing at a time. Fully understanding something makes it easier to understand something that builds on it, because you can make assumptions and focus on the new functionality.

 

Dynamically creating Server Controls

TextBox tb = new TextBox();

There's not much to them. All the built-in controls are Server Controls, and so are any controls you create yourself that inherit from Control, WebControl, CompositeControl, etc.

 

Dynamically creating User Controls

First lets create a hypothetical user control:

<MyUserControl.ascx>
<%@ Control Language="C#" AutoEventWireup="true"
CodeFile="MyUserControl.ascx.cs" Inherits="MyUserControl" %>
Your name: <asp:TextBox ID="txtName" runat="server" Text="Enter your name" />

A user control is not unlike a regular ASPX webform in that the ASCX contains markup that declares "static controls", and inherits from a code-behind class, and that code-behind class itself may or may not load dynamic controls.

Assuming this ASCX file were placed into the root of the application (denoted by "~/"), we could dynamically load it like this:

Control c = this.LoadControl("~/MyUserControl.ascx");

You might wonder why there's a difference in loading server controls and user controls. Why can't we create a user control just like a server control? This user control has a code-behind class "MyUserControl", so why can't we do this:

MyUserControl c = new MyUserControl(); // no!

I invented a new code sample CSS style just to illustrate this. Blue means good. Red means bad. You definitely can't create a user control like this.

In Part 1 remember we examined how the page parser converts your markup into a class that inherits from your code-behind class? And remember that the generated code creates the controls, adds them to the control tree, and assigns them to your code-behind's control variables that are declared as protected? User Controls work the same way.

By creating the code-behind class directly you have effectively by-passed this whole process. The result will be an instance of your control with no control tree. Your control definitely won't work, and will probably result in a NullReferenceException the first time your code actually tries to use any of the controls (because, well, they're null).

That is why the "LoadControl" method exists, and that is why the actual ASCX file must stick around. LoadControl retrieves and instantiates the type of the auto-generated code created from your markup.

The path given to LoadControl must be the virtual path to the ASCX file. Physical paths aren't allowed. The virtual path can take on one of three forms. Lets assume the application is running under virtual directory name "Truly" (in other words, http://myserver/truly represents the root of the application). The three forms are:

Control c;
c = this.LoadControl("MyUserControl.ascx"); // relative
c = this.LoadControl("./MyUserControl.ascx"); // relative
c = this.LoadControl("~/MyUserControl.ascx"); // app relative
c = this.LoadControl("/Truly/MyUserControl.ascx"); // root relative

Whatever the case, the virtual path must map inside the current application. No loading user controls that reside in other applications. So a root-relative path better map back into the current application ("Truly"), or you will get an error.

Relative paths are relative to the current page, or the current user control. That is important to know, because if you write a user control that loads another user control dynamically, you should know the difference between these:

Control c;
c = this.LoadControl("AnotherUserControl");
c = this.Page.LoadControl("AnotherUserControl");

The path is relative to the control you call the method on. The first line loads a control that exists in the same directory as the current user control. The second line loads a control that exists in the same directory as the current page, and the two don't have to be the same. Don't assume your user control is going to live in a particular location. It might be moved around. Really you have the same problem with Page.LoadControl, because you don't really know where in the directory structure the page loading your control will be.

It's usually best to keep user controls in one location, or at least segregated into multiple "silos" that logically separate them. That way you can always use a relative path like in the first line, yet you still have the freedom to move the controls around, just as long as you move them together. Pages that reference the controls will just have to know where they are, at least relatively.

You could also consider making the path to it configurable (via a property), use a web.config setting (less favorable), or use an app-relative path (ie, "~/UserControls/MyUserControl.ascx") and make the location known and unchangeable. 

 

Dynamically creating Parsed Controls

The ParseControl method is even more dynamic. It takes a string of markup and converts it into a control:

Control c;
c = this.ParseControl("Enter your name: <asp:TextBox id='txtFirstName' runat='server'/>");

The string can contain virtually anything you can put on a page (disclaimer: I say virtually, because while I don't know of a limitation, there may be one. If anyone reading this knows of a limitation let me know). All of the controls contained within will be put together into a single control, where it's control tree contains the parsed controls.

Parsed controls are interesting, but use them very carefully. Parsed controls carry a performance burden, because the string must be re-parsed every time. Normally the parsed result of pages or user controls is cached.

Here's a dramatic demonstration of the performance hit. Here we create the same user control over and over again (1 million times):

start = DateTime.Now;
for(int i = 0; i < 1000000; i++) {
    c = this.LoadControl("UserControls/MyUserControl.ascx");
}
end = DateTime.Now;
Response.Write((end - start).TotalSeconds);

And here we parse the content of the user control as a string over and over again:

start = DateTime.Now;
for(int i = 0; i < 1000000; i++) {
    c = this.ParseControl(
"Enter your name: <asp:TextBox id='txtFirstName' runat='server'/>");
}
end = DateTime.Now;
Response.Write((end - start).TotalSeconds);

And here are the results:

ParseControl took 253 seconds, loading the user control only took 19 seconds. That's a huge difference. Running ParseControl 1 million times in 253 seconds is still not a major performance issue (you're likely to have much more significant bottle necks in your application), but your mileage may vary, and you can't deny the performance results. Use it very sparingly. And definitely don't parse a string that came from a potentially malicious user! You would be enabling them to create any control that is available in the application, and they could use that to attack your site.

Any virtual paths you use inside the string follow the same rules as in the user control example. Relative paths are relative to the location of the control (or page) which you call ParseControl on. So there's that same subtle but important difference between the UserControl.ParseControl method and the Page.ParseControl method.

 

Dynamic Controls and Templates

There's another sneaky way that dynamic controls can be created. You have likely used templates without even realizing it. This topic is critical because if you understand how templates are used in databound controls like the Repeater, DataGrid, GridView, etc, then you will better recognize times where you may be approaching a problem with dynamic controls when you really don't have to. Later on there will be a section all about those situations.

Templates decrease the need for you to create dynamic controls manually because they let you declaratively define a "template" by which controls will be created dynamically for you. The Repeater control for example lets you define an ItemTemplate and optionally an AlternatingItemTemplate. Within the template you declaratively define controls:

<asp:Repeater ID="rpt1" runat="server">
    <ItemTemplate>
        Your name: <asp:TextBox ID="txtName" runat="server" Text="Enter your name" />
    </ItemTemplate>
</asp:Repeater>

The template contains a TextBox server control. Do you think the page that this repeater sits on has this TextBox, just like in our simple UserControl above? Do you think you can access this TextBox like this?

protected override void OnLoad(EventArgs e) {
    this.txtName.Text = "foo"; // no!
    base.OnLoad(e);
}

No no no. Remember -- red means bad. The TextBox you declared does not exist on the page, at all. There is no "build control" method generated for it, and it will not be assigned to any protected class variables you define (as described in Part 1).

Think about it for a minute. It doesn't even make sense for the TextBox to be accessible from the page like a regular statically declared control. The Repeater is going to be creating this TextBox once for every DataItem that is data bound to it. So there will be many of these on the page -- anywhere between 0 and N of them in fact. So if there were a page-level reference to it, which one would it point to? It doesn't make any sense.

When you declare a regular static controls, you are telling the framework "here is a control I'd like you to create and add to the page's control tree." When you define a template, you are saying "here is a control tree that I would like added to the page's control tree when the template is used." In the case of a repeater, the template is used once for every data item. But Templates don't have to be used that way (for example, the System.Web.UI.WebControls.Login control's LayoutTemplate property is a Template that isn't repeated). It's up to the control's implementation how the template is utilized.

Instead, the markup within the Template is parsed into an object that implements the System.Web.UI.ITemplate interface. Let's take a look at what code is generated for the static repeater in the example above:

private global::System.Web.UI.WebControls.Repeater @__BuildControlrpt1() {
    global::System.Web.UI.WebControls.Repeater @__ctrl;
 
    #line 12 "C:\projects\Truly\MyPage.aspx"
    @__ctrl = new global::System.Web.UI.WebControls.Repeater();
 
    #line default
    #line hidden
    this.rpt1 = @__ctrl;
 
    #line 12 "C:\projects\Truly\MyPage.aspx"
    @__ctrl.ItemTemplate = new System.Web.UI.CompiledTemplateBuilder(
        new System.Web.UI.BuildTemplateMethod(this.@__BuildControl__control4));
 
    #line default
    #line hidden
 
    #line 12 "C:\projects\Truly\MyPage.aspx"
    @__ctrl.ID = "rpt1";
 
    #line default
    #line hidden
    return @__ctrl;
}

Pay particular attention to the middle of this method. The repeater control has a property named ItemTemplate (which happens to accept objects of type ITemplate). The parser's generated code is creating a new CompiledTemplateBuilder and passing in a BuildTemplateMethod Delegate to it's constructor. The delegate is pointed at a method defined on this page, which happens to be this "build control" method:

private void @__BuildControl__control4(System.Web.UI.Control @__ctrl) {
    System.Web.UI.IParserAccessor @__parser = 
          ((System.Web.UI.IParserAccessor)(@__ctrl));
 
    #line 12 "C:\projects\Truly\MyPage.aspx"
    @__parser.AddParsedSubObject(new System.Web.UI.LiteralControl("\r\n Your name: "));
 
    #line default
    #line hidden
    global::System.Web.UI.WebControls.TextBox @__ctrl1;
 
    #line 12 "C:\projects\Truly\MyPage.aspx"
    @__ctrl1 = this.@__BuildControl__control5();
 
    #line default
    #line hidden
 
    #line 12 "C:\projects\Truly\MyPage.aspx"
    @__parser.AddParsedSubObject(@__ctrl1);
 
    #line default
    #line hidden
 
    #line 12 "C:\projects\Truly\MyPage.aspx"
    @__parser.AddParsedSubObject(new System.Web.UI.LiteralControl("    \r\n   "));
 
    #line default
    #line hidden
}

This "build control" auto-generated method is responsible for building the control tree that we declared within the repeater's ItemTemplate. As you can see, it creates a literal control, then calls the TextBox's build control method to create it, and then creates another literal control (the literal controls represent the non-server-control markup before and after the TextBox).

But this method isn't actually called from anywhere. It's simply the target of a Delegate, which was passed to the CompiledTemplateBuilder. So we haven't figured out yet exactly how the controls get into the control tree.

That's because it's up to the control containing the template to do something with the template. The page parser has done it's job. Now it's up to the repeater to do something about it. And of course, we already know the repeater "uses" it once for each data item. To see exactly how it uses it, lets look at the ITemplate interface:

public interface ITemplate {
    void InstantiateIn(Control container);
}

That's the entire interface. Just one method. The idea is that you have this control tree template, and when you need to instantiate it -- when you need to create an actual control tree based on the template (not unlike the relationship between a Class and an Instance of a Class) -- you call InstantiateIn(). The control tree is then created and added to the container you give it.

So we can summarize Repeater's use of templates as the following:

  1. Go to the next data item.
  2. Determine the appropriate item template for this data item (one of: ItemTemplate, AlternatingItemTemplate, HeaderTemplate, FooterTemplate).
  3. Create a container control (repeater uses a RepeaterItem).
  4. Call the selected template's InstantiateIn method, passing the container.
  5. Add the container to the repeater's control collection.
  6. Goto step 1 if there's any data items left.

In addition to this logic, the Repeater has an optional SeparatorTemplate, which it calls InstantiateIn() on between each data item.

Remember the "build control" method that the Delegate pointed to? Calling InstantiateIn() on the template is going to call that method. So the method is executed once for each data item, and thus that txtName TextBox we declared in the ItemTemplate markup is going to be created multiple times, in a dynamic manner.

So as you can see, Templates are powerful. And it also demonstrates that the framework itself is very much involved in the creation of dynamic controls. You can also create templates programmatically instead of statically by implementing the ITemplate interface yourself, but that's for a different blog entry.

All this and I still haven't gotten to the heart of the matter. The next part will examine how and when dynamic controls are added to the control tree. By "when" I mean when during the page lifecycle. That is so important because "when" can affect how the dynamic control participates in the lifecycle, and depending on the type of control and what features of it you are relying on, doing it at the wrong time will break it...

41 Comments

  • Nice job, Dave. You enlighten my horizont in developing controls. Why this kind of articles isnt written in e-books? Hardly waiting for part 3 :)

  • Wow, great article. Hurry up with the next one :-)

  • Good stuff. I am anxiously waiting for Part 3! Thanks.

  • when can we expect part 3?

  • I&#39;m anxiously awaiting part 3. &nbsp;I&#39;ve never had any difficulty dynamically creating controls, it&#39;s re-creating them on post-back that gives me fits.

  • Right... sorry its been so long since part 2 everyone. I have been quite busy working Atlas of all things.

    I haven't forgotten about it... Part 3 is half written, I'll finish it up asap, probably sometime next week.

  • i generate labels and textboxes in a place holder on basis of data in sql.but on every post back i have to recall same method to maitain number of textboxes and values of labels.can u explain me how to retrive the value of textboxes from viewstate-statebag.cause the qurey retives large amount of data including names of labels.it would be helpfull to utilize viewstate values.

  • utsav --- you dont need to retrieve the values out of StateBag yourself. To remember how many textboxes there are, just store the number in viewstate:

    this.ViewState["count"] = count;

    Then on postbacks you can recreate that many textboxes.

    As for the values in the labels, they too can maintain their state automatically. Just set their text property after you add them to the control collection (not before).

    So you're going to have two different "modes" of creating the controls -- one when you have data, where it sets the text properties of the labels and controls, and stores the number of them in viewstate -- and another where you simply recreate them controls based on the count and nothing more.

    I just described in a nutshell how DataGrid works...

  • thk u for help will try it out today itself

  • looking forward to more such good indepth articles which are understood properly.

  • it is so good! thanks you very munch,

  • Thanks that is a gret article. My problem seems to be escaping all of &nbsp;articles that i have read. I have a User Control that is created, configured and populated based on databased values. I am adding the controls to the forms collection
    &nbsp; &nbsp; &nbsp; &nbsp;NewQuestionCTL newMultiChoice = (NewQuestionCTL)LoadControl(&quot;NewQuestionCTL.ascx&quot;);
    &nbsp; &nbsp; &nbsp; &nbsp;newMultiChoice.setQuestion(type, text, number);
    &nbsp; &nbsp; &nbsp; &nbsp;newMultiChoice.ID = &quot;newQuestion&quot; + number.ToString();
    &nbsp; &nbsp; &nbsp; &nbsp;form1.Controls.Add(newMultiChoice);
    They look great but I cannot find the controls to read the values from after the postback, despite the fact that I redrawing the controls on postback. How do I access the values of of the 10-20 controls to update the database. &nbsp;

  • Jim -- since you are creating them once every request already anyway, why don't you just hang on to them with a dictionary? You can key the dictionary by the question number.

    Dictionary index = new ... ();

    // ... load control as usual
    // store it in the index
    index[questionNumber] = userControl;

    // Then later on when you need them,

    userControl = index[questionNumber];

    Or you can enumerate them

    foeach(KeyValuePair pair in index) {
    pair.Key; // the question number
    pair.Value; // the user control
    }

  • You can use this.Page.LoadControl.

    Its not stupid... thats one of the whole points of the code behind model :) You don't necessarily need to load something dynamically to accomplish it though.

  • Hi,

    Thx for your answer:

    "You can use this.Page.LoadControl."

    that was my initial thought too and it works fine on page but not during design-time in VS.NET (designer).

    I have complex templated custom control which I want to "fill" through designer.

    Joshua

  • Sounds like you want to provide a template then. Too broad of a subject to cover in a comment box... but you want the same functionality that say, a repeater has:



    user content goes here



    The content of the item template is used by the repeater in whatever means it deems necessary. Templates aren't just for repeating databound controls. Login control, for example, uses a template to allow you to customize the layout of the login form. If someone wants a user control loaded into the template, they can simply declare a user control within the template.

    Here's a quickstart on templated controls:
    http://samples.gotdotnet.com/quickstart/aspplus/doc/webctrlauthoring.aspx#templated

  • You can load a "look" from a file with either LoadControl or LoadTemplate, but I don't know of a way to do this at design time. I'm curious why you want to do it at design time -- are just trying to customize the design time look of your control, and you want to seperate the UI logic into a seperate file for ease of maintenance? You could load the file from disk directly, then as long as it only contains html, you could write a designer that uses it to display it as rendered html. Sorry I don't have any specific advice on how to go that route, designers aren't my strong point :)

    Still I'm curious exactly what you are trying to accomplish... typically a control's design time matches its runtime except at worst, dummy UI to represent things that aren't known until runtime. If you're loading special html just for design time, then it doesnt sound like its going to match runtime very well.

  • Hi,
    I had some code working in .Net 1.0 regarding dynamic controls, now since moving it to .NET 1.1 it just doesnt seem to work, or i am doing something stupid ? &nbsp;Are there any differences between frameworks with regards to dynamic controls.
    Is it possible to have a dynamic control created, to change its text value (for example) using javascript client-code and pick up that new value at the server side when its submitted ?

  • Neil -- there aren't any real differences that I know about, but there could have been some optimizations made or something that could have an affect on your scenario. Hard to say without more details.

    Yes... but if the control isn't a form control (checkbox, textbox, etc) then it won't post its value natually. For example a label. You can change its value on the client easily, but for that value to be communicate to the server it must be copied into a hidden form field. A lot of custom controls out there use that trick. If its a textbox the value will already be submitted and picked up by the control.

  • Is it possible to build controls that support nested Templates?

    I want to support:















    I created ReadViewTemplate which implements ITemplate (see below for the code), and my control of type CRUD exposes a ReadViewTemplate as a property. However when I try to run the code I get the error:

    Cannot implicitly convert type 'System.Web.UI.CompiledTemplateBuilder' to ReadViewTemplate'

    How *should* I approach this?

    public class ReadViewTemplate :ITemplate
    {
    private ITemplate _validationTemplate;
    private ITemplate _headerTemplate;
    private ITemplate _footerTemplate;
    private ITemplate _bodyTemplate;


    [Browsable(true)]
    [DefaultValue(null)]
    [PersistenceMode(PersistenceMode.InnerProperty)]
    [TemplateContainer(typeof(ReadView))]
    [TemplateInstance(TemplateInstance.Single)]
    public ITemplate HeaderTemplate
    {
    get { return _headerTemplate; }
    set { _headerTemplate = value; }
    }

    [Browsable(true)]
    [DefaultValue(null)]
    [PersistenceMode(PersistenceMode.InnerProperty)]
    [TemplateContainer(typeof(ReadView))]
    [TemplateInstance(TemplateInstance.Single)]
    public ITemplate ValidationTemplate
    {
    get { return _validationTemplate; }
    set { _validationTemplate = value; }
    }

    [Browsable(true)]
    [DefaultValue(null)]
    [PersistenceMode(PersistenceMode.InnerProperty)]
    [TemplateContainer(typeof(ReadView))]
    [TemplateInstance(TemplateInstance.Single)]
    public ITemplate BodyTemplate
    {
    get { return _bodyTemplate; }
    set { _bodyTemplate = value; }
    }


    [Browsable(true)]
    [DefaultValue(null)]
    [PersistenceMode(PersistenceMode.InnerProperty)]
    [TemplateContainer(typeof(ReadView))]
    [TemplateInstance(TemplateInstance.Single)]
    public ITemplate FooterTemplate
    {
    get { return _footerTemplate; }
    set { _footerTemplate = value; }
    }


    #region ITemplate Members
    public void InstantiateIn(Control container)
    {

    if (container is ReadView)
    {
    if (this._bodyTemplate!=null) this._bodyTemplate.InstantiateIn((ReadView)container);
    if (this._validationTemplate != null) this._validationTemplate.InstantiateIn((ReadView)container);
    if (this._headerTemplate != null) this._headerTemplate.InstantiateIn((ReadView)container);
    if (this._footerTemplate != null) this._footerTemplate.InstantiateIn((ReadView)container);
    }
    else
    {
    throw new Exception("This template can only instantiate its controls within a ReadView control");
    }
    }

    #endregion
    }


  • Rick, I'm afraid templates were simply not designed to be nested. The page parser always created a particular type to implement the interface based on markup.

    I'm not sure it makes total sense at least in this scenario anyway... the fact that ReadViewTemplate has a nested template implies that it matters where the user puts the nested templates. For example:


    content content content
    ...
    content content content
    ...
    content content content


    It doesn't seem like it is your intent for the user to be able to put content between the head and body templates. And so, the correct solution is for the ReadViewTemplate property not to be a template in the first place. You could create a custom type that has two properties: HeadTemplate and BodyTemplate. Then return via a get-only property an instance of that type from your control. Then you can accomplish this nested syntax, and the ReadViewTemplate is not a template at all, and thus the above markup would correctly generate an error.

    You could also keep it simplier by just breaking them into different properties...

    ...
    ...
    ...
    ...

  • Wow, when I highlight the whole page with my mouse I can almost read it. Good content though.

  • Thanks a lot for great article. I hope you'll be able to give us some advice.
    We have to allow designers working in VS IDE and modify properties of the ViewForm.ItemTemplate content. At runtime, I want to be able reorder content based on the database records and potentially add more content. The database has definition for every label text and source for corresponding value text.






    <asp:Label ID="Row1Value" CssClass="formw" runat="server" Text=''>



    <asp:Label ID="Row2Value" CssClass="formw" runat="server" Text=''>

    ....




    Sorry if I have not made myself clear... Aspx will have ItemTemplate defined. At runtime, we will read database and need to reorder divs placing them in appropriate sequence based on db definition and, in addition, we need to add more divs for 'custom' records which have not been included in ItemTemplate at design time.

  • Irene - You should create a custom control. All it would do is override Render, and then render its child control in the appropriate order (by calling control.RenderControl on them).

    The idea is you want to customize the order they are rendered and nothing more -- it could cause you problems if you were doing something like shuffling the order of the controls in the tree.

    The custom control also gives you the ability to enforce proper usage. If the only top level divs should be within the control then you can throw an exception if someone adds something else, or you could even have another type of control they use instead, which would allow you to have an API for customizing how the rendering occurs (if you need such customization).




    ....


    ....



    In this case the "type" property is just something I made up -- its something the OrderedContainer control (which is badly named) could look at to make decisions.

    Hope that helps.

  • Hi there, Sorry to bother you again. I have just a quick follow-up to the previous post…

    Regarding item 3, I’ve ended up not updating Text property based on Source but calling SubscribeToDataBinding() when Source != "" && !Text.Contains("Eval")

    private void SubscribeToDataBinding(Item control)
    {
    if (control.Source != "")
    {
    control.DataBinding += delegate(object sender, EventArgs e)
    {
    Item target = sender as Item;
    FormView Container = target.BindingContainer as FormView;
    target.Text = Convert.ToString(DataBinder.Eval(Container.DataItem, control.Source, "{0}"), System.Globalization.CultureInfo.CurrentCulture);
    };
    }
    }

    4. I’ve derived my OrderedContainer control from Panel. Is it the best base class for this purpose? Other than being a parent for Item controls it has to have OrderedContainerDesigner : PanelContainerDesigner.

    Thanks a lot for your help.
    -irene

  • Irene -- What you derive from should be based on what you want to render. If you want the container to render a div, panel works. If you dont need it to render anything I'd use Control. Just depends on if you really need that surrounding the content or if you need to style it.

  • Sorry I missed your previous comment...

    I'm not sure why you are dynamically creating the Items? I thought your usage was that the items would be declared within the OrderedContainer, and all OrderedContainer did was render them in a different order.

  • No harm in subscribing to an event more than once -- events are designed for just that. If you just want to avoid overwriting an existing design-time databinding expression then check that Text is not empty from within your binding handler. Its still possible that it was databound though (to an empty value). If that concerned you, you could have some placeholder text in it by default, then only set it if its still that value.

    As for designers... they aren't my strength, I'll admit that :) If you want your control resizable in the designer then that implies you have a width/height property on your control? Inherit from webcontrol and make sure your designer returns true for AllowResize (which I think is the default anyway...).

  • Thanks again for your help.
    Now I’m trying to manage database driven items in a container but I need it to be GridView columns collection. I thought if I create my TemplateField I would be able to generate Columns from the database definition. I guess I am confused about the way of defining and using custom TemplateFields.

    class ColumnDetail : TemplateField
    {
    private DocDetail _docDetail ;
    ColumnHeader _header;
    ColumnItem _item;

    public ColumnDetail()
    {
    _docDetail = new DocDetail();
    _header = new ColumnHeader(_docDetail.LabelField);
    _item = new ColumnItem(_docDetail);
    }

    public override ITemplate HeaderTemplate
    {
    get { return _header;}
    }
    ………..
    }
    public class ColumnHeader : ITemplate
    {
    BoundLabel _label;
    public ColumnHeader(BoundLabel label)
    {
    _label = label;
    }

    public void InstantiateIn(Control container)
    {
    container.Controls.Add(_label);
    }
    }
    public class ColumnItem : ITemplate
    {
    DocDetail _docDetail;
    public ColumnItem(DocDetail docDetail)
    {
    _docDetail = docDetail;
    }

    public void InstantiateIn(Control container)
    {
    container.Controls.Add(_docDetail);
    if (!string.IsNullOrEmpty(_docDetail.Source))
    {
    _docDetail.DataBinding += delegate(object sender, EventArgs e)
    {
    IDocDetail target = sender as IDocDetail;
    IDataItemContainer Container = (target as Control).BindingContainer as IDataItemContainer;
    target.Text = Convert.ToString(DataBinder.Eval(Container.DataItem, _docDetail.Source, "{0}"), System.Globalization.CultureInfo.CurrentCulture);
    };
    }
    }

    I thought I could use my ColumnDetail but I am getting compliler error…




    …..
    How should I approach it? Thanks again.

    - Irene

  • Irene. -- not sure what your compile errors are. You don't need to inherit from TemplateField. TemplateField is called such because its meant to get the UI from markup. Since you seem to want to just render specific UI driven by the db, just inherit from DataControlFileld.

  • Thanks a lot for immediate response :-)
    I am not quite sure what is happening. &nbsp;The problem with compilation was due to my silliness not having ColumnDetail declared as public class. &nbsp;Now I can place it within the columns but it does not act as Template. &nbsp;It does not display anything. It does not hit the break inside InstantiateIn().
    I do want to display columns collection in design as well. I am not sure if it effects whether to inherit from DataControlFileld or TemplateField.
    Sorry, as you can see I am rather novice in this area and really appreciate you help very much!

  • Irene -- pretty sure you should inherit DataControlField or possibly BoundField. TemplateField is meant to get UI declaratively, and you want to create it dynamically based on data, without any input from the markup.

    This article may help get you started with creating a custom field:
    http://msdn.microsoft.com/msdnmag/issues/06/01/CuttingEdge/

  • Hello

    I need your help. I can not retrieve value from dynamic TextBox and DropDown List Control. I was create dynamic control at the Page_Load() event and the number of control count and control type (TextBox or DropDownList) is accorading to the DB value.

    At first I was used DataList control with the dataset.datatable. My DataList Item Template has 1 PlaceHolder and after I created my dynamic control, add to that Place Holder.

    And then when the user click Save Button, I have to save the data from dynamic control value to the database. So I was using the FindControl() Method for every DataList item template. I find for PlaceHolder control first and then i find again my dynamic control(TextBox or DropDownList) from that PlaceHolder. But I got a result that is No Control from that PlaceHolder.

    I trace my Control Tree View, I can see my Dynamic Control under the PlaceHolder Control. But I can not find my dynamic control from that PlaceHolder when my Debuging.

    Pls help me ASAP. I need all of your help!
    Thanks

  • nyimalay -- You'll have to provide me some code so I can understand what is wrong. Sounds like you probably have a significant amount of code with database lookups and all that. Try to narrow it down as compact as possible while still reproducing the problem.

  • This article is truly a great asset. I've searched all over for information that is sound, succinct, and easy to understand. You have achieved all this. Thanks for the enlightenment.

  • Ashok -- maybe this will help you.

    http://msdn.microsoft.com/en-us/library/w70c655a.aspx

    or perhaps this

    http://www.codeproject.com/aspnet/aspnetusercontrol2.asp

  • can we display data without using databound controls using datatable.

  • Divya -- sure, you could either manually build up the html in the code behind with a foreach or you can embed that foreach directly into the page using the old asp style code blocks.

    You might also be interested in the ASP.NET MVC framework currently under development.

    http://weblogs.asp.net/scottgu/archive/2007/11/13/asp-net-mvc-framework-part-1.aspx

  • Pablo -- have you tried setting the custom templates on the repeater from Init instead of Load? It doesnt appear there's any reason to delay it. Not sure if thats the problem but I'd do that anyway. Also, I'd hook the databinding event before adding the panel to the control collection. I dont think it matters in this case but as a practice I try to do everything I can to the control before adding it, unless there's a good reason to do it afterward. Let me know if either of those helps...

  • Hi, hope you still monitor the part, but I'm totally lost here, and that is also because I don't understand all of it complety.

    In my case I made a small example which in total uses 4 nested UC's and each UC has a textbox, button and checkbox. The UC's are in a ItemTemplate of the repeater which is nested. Now on every level I the button adds a new control to the list using:

    protected void Button2_Click(object sender, EventArgs e)
    {
    ArrayList list = new ArrayList();
    int index = 0;

    foreach (RepeaterItem rptItem in rptFirst.Items)
    {
    WebUserControl2 WebUserControl = rptItem.FindControl("Item") as WebUserControl2;

    if (WebUserControl != null)
    {
    WebUserControl2 ctrl = WebUserControl as WebUserControl2;
    list.Add(ctrl);
    index++;
    }
    }

    index++;
    WebUserControl2 _ctrl = LoadControl("WebUserControl2.ascx") as WebUserControl2;
    _ctrl.myText = "NEW ITEM";
    list.Add(_ctrl);

    Repeater1.DataSource = list;
    Repeater1.DataBind();

    }

    Basically every uc has a procedure like this. Then when I also have, on every UC the following procedure:

    protected void Repeater1_ItemCreated(object sender, RepeaterItemEventArgs e)
    {
    if (!e.Item.ItemType.Equals(ListItemType.Separator))
    {
    //Postback then e.item.DataItem is null
    if (e.Item.DataItem != null)
    {
    WebUserControl2 WebUserControl = e.Item.FindControl("Item") as WebUserControl2;

    WebUserControl.myText = ((WebUserControl2)e.Item.DataItem).myText;
    WebUserControl.myCheckBox = ((WebUserControl2)e.Item.DataItem).myCheckBox;
    }
    }
    }

    As you can see I actually bind a (Arry)list of UC to the repeater.

    I want to setup this small example without binding to a Db, that is why I use the ArrayList.

    No When I add a control it appears fine. On this control I can add 1..N Child UC and on the child control I can add 1..N UC (three levels deep). This all works fine, untill I press Add usercontrol button on the highest level. Then I still have the first UC I added and also the second new UC, but all the Child controls ( and their child controls) of the first added control are gone.

    All the controls have the procedures I have included above.

    How can I maintain the state of the child controls which are already there when I add a control on the highest level.

    I was told the problem is I recreate the controls in ItemCreated event, so it is actually a new control which, of course has no childcontrols. If that is true, what would be the solution for this?

    TIA Stephan

  • Really a awesome article on dynamic controls.

    Can we have a similar article on creating custom controls, best way to declare property and events handling with in controls?

    I saw developers facing these issues frequently while writing custom controls. Any good article on this will be helpful.

Comments have been disabled for this content.