Tuesday, September 04, 2007 4:50 PM InfinitiesLoop

TRULY Understanding Dynamic Controls (Part 4)

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

UPDATE: Click here to download the Sample Code referenced in this article.

I started part 1 of this series so long ago. My original plan was to have all 4 parts done over four weeks. It's been a lot longer than that! Sorry folks!

The new plan is that four parts is not enough! I realized while writing this part that there's just too much to cover!

There are three main reasons I can think of why you would want to create controls dynamically instead of declaratively.

  1. Because the types and/or number of controls to be rendered is not known at design time. The form may be driven by a database or through configuration, or it is dynamically determined based on other data the user has provided.
  2. You know what controls may be rendered but there are a large number of possibilities and/or the controls are expensive to load, so you don't want to load them all.
  3. Because you are creating a custom server control and you don't have a choice.

In this part, we will cover #1 and #2. Part 5, hopefully the last part, will cover #3.

Example: You don't know "what" or "how many" controls should be rendered at design time

First and foremost -- ask yourself whether the problem is that you don't know "what" controls should be rendered, or if it's just "how many" controls should be rendered. If it's a question of how many, you can still solve the problem declaratively thanks to Templating.

This is a real scenario that someone emailed me about. They were attempting to solve the problem with dynamic controls. They were having various problems with their implementation and asked for my help. And of course, it turned out to be much simpler with templating.

The scenario is as follows:

You want users to be able to upload files to your site. They do this from a file manager page, where they can see a listing of the files that already exist. Next to each file is a button to delete the file. At the bottom of the list is a file upload control they can use to upload a new file. Any time a file is deleted or uploaded, the listing is updated.

To display the files, we will add rows dynamically to a table that is declared on the form. When a file is deleted we'll give some feedback via a label.

<asp:Label ID="lblStatus" runat="server" EnableViewState="false" />
<table id="tblFiles" runat="server" cellpadding="4" cellspacing="4" border="1"></table>

Here is the implementation I was given. In red, because it has problems!

public partial class _Default : Page {
    protected override void OnLoad(EventArgs e) {
        string[] files = Directory.GetFiles(Server.MapPath("~/uploaded"));
 
        foreach (string path in files) {
            // strips directory from path
            string fileName = Path.GetFileName(path);
 
            HtmlTableRow row = new HtmlTableRow();
            // file name cell
            HtmlTableCell cell = new HtmlTableCell();
            cell.InnerText = fileName;
            row.Cells.Add(cell);
 
            // delete cell
            LinkButton cmdDelete = new LinkButton();
            cmdDelete.Text = "delete";
            cmdDelete.CommandArgument = fileName;
            cmdDelete.Command += new CommandEventHandler(cmdDelete_Command);
            cell = new HtmlTableCell();
            cell.Controls.Add(cmdDelete);
            row.Cells.Add(cell);
 
            // add row to table
            this.tblFiles.Rows.Add(row);
        }
        base.OnLoad(e);
    }
 
    private void cmdDelete_Command(object sender, CommandEventArgs e) {
        // command argument contains the file name to delete
        string fileName = Path.GetFileName((string)e.CommandArgument);
        string path = Path.Combine(Server.MapPath("~/uploaded"), fileName);
 
        File.Delete(path);
        this.lblStatus.Text = "Deleted " + fileName;
 
        // now remove the table row for the file
        LinkButton cmdDelete = (LinkButton)sender;
        // link button's parent = cell, parent.parent = row
        HtmlTableRow row = (HtmlTableRow)cmdDelete.Parent.Parent;
        this.tblFiles.Rows.Remove(row);
    }
}

It seems pretty straight forward. The first cell shows the file name, the second shows a link we can click on to delete it. When deleting a file, we remove it from the file system and then remove the row from the table. And of course, we let the user know the file was deleted successfully. Let's see how it runs...

File Manager

Cool -- let's delete "InfinitiesLoop.gif", who wants that thing anyway?

Deleted InfinitiesLoop.jpg

So far so good! Now, let's delete "TheFamily.jpg". I was blinking during that picture, and I don't appreciate the bunny ears over my head, sister. Bah...

Deleted TheFamily.jpg

WHAT?! I swear I clicked delete on TheFamily.jpg, but it deleted my TODO list! Nooooo.....! Oh well I guess I can just go home now. Dare I try to delete something else? Who knows what it's going to do next...

So, what went wrong?

When the page first renders, there are 4 rows with 4 link buttons. We never specified an ID for those link buttons when we created them. That means they get automatically generated IDs. The rows and cells do, too, but only the link button actually requires an ID to be rendered, because it must cause a postback via javascript (since links don't naturally do postbacks). If you view the html source, you will see the link button's href looks like "javascript:__doPostBack('ctl01', '')". In this case, ctl01 is the LinkButton ID.

So let's say the link buttons have IDs ctl01, ctl02, ctl03, and ctl04 (the actual IDs are different because of the controls between them, but you get the idea). We click on ctl02 to delete "InfinitiesLoop.jpg". When the postback is processed, first we recreate the table with all 4 rows again. The link buttons will still have the same IDs they did last time. ctl02 raises its command event, we delete the file, and then remove the row from the table. But the link buttons have already determined what their IDs are going to be -- the rendered table will now have link buttons with these IDs: ctl01, ctl03, ctl04. Hmmm. ctl03 is the link button the represents TheFamily.jpg. It will now appear 2nd in the table.

Now we click to delete TheFamily.jpg (ctl03). First the table gets recreated -- only, this time there is no InfinitiesLoop.jpg in the Upload directory. So we only create 3 link buttons this time -- ctl01, ctl02, ctl03. At this stage, ctl03 is the link button that corresponds to TODOList.txt. See the problem yet?

ASP.NET knows who raised the postback event via its ID. But we've pulled the rug out from under it -- the controls on this request have different IDs than they did on the previous request. There happens to be a control with the ID that was posted, but it isn't the one we intended it to be. ASP.NET finds ctrl03, and we delete TODOList.txt. Oops.

This problem can manifest itself in many different ways. If we had textboxes in this table, you might find that they lose their values after certain postbacks, or that their values seem to shift in position.

What to do about it

It's important that the structure of the control tree be the same after a postback as it was before the postback. We actually were not really violating that rule in this case -- we rendered out 3 rows when InfinitiesLoop.jpg was deleted, and we created 3 rows after the postback. But we cheated -- we created 4 rows and removed one, and then on the next request we just created 3 right off the bat. One way we could solve this is by giving each LinkButton a specific ID that will never change. In this case the simplest way would be to make it's ID equal to the file name. There's no way you can confuse one link button with another then. But it's far from ideal to have to stuff the file name into the control ID. The name might be really long. And if we were accessing multiple directories, we could have duplicate file names, so now we'd have to include path information, too. There may be other controls in each row, such as checkboxes that show the file's attributes like whether it is marked as ReadOnly. We'd have to apply this trick to those controls, too. Yuck. Yuck. Yuck.

Really what you want is to be able to "reset" the ID scheme of all the controls in the table. When we remove a row, all the controls after it should 'shift up' with their IDs, so that they will render with the same ID they will have on the next postback. So, what if when we deleted a row, we completely rebuilt the table from the start? Just clear out all the rows and then rebuild it like we originally did...

protected override void OnLoad(EventArgs e) {
    BuildTable();
    base.OnLoad(e);
}
 
private void cmdDelete_Command(object sender, CommandEventArgs e) {
    // command argument contains the file name to delete
    string fileName = Path.GetFileName((string)e.CommandArgument);
    string path = Path.Combine(Server.MapPath("~/uploaded"), fileName);
 
    File.Delete(path);
    this.lblStatus.Text = "Deleted " + fileName;
 
    // removed a row, throw away the table and rebuild it
    this.tblFiles.Rows.Clear();
    BuildTable();
}
 
private void BuildTable() {
    string[] files = Directory.GetFiles(Server.MapPath("~/uploaded"));
 
    foreach (string path in files) {
        // strips directory from path
        string fileName = Path.GetFileName(path);
 
        HtmlTableRow row = new HtmlTableRow();
        // file name cell
        HtmlTableCell cell = new HtmlTableCell();
        cell.InnerText = fileName;
        row.Cells.Add(cell);
 
        // delete cell
        LinkButton cmdDelete = new LinkButton();
        cmdDelete.Text = "delete";
        cmdDelete.CommandArgument = fileName;
        cmdDelete.Command += new CommandEventHandler(cmdDelete_Command);
        cell = new HtmlTableCell();
        cell.Controls.Add(cmdDelete);
        row.Cells.Add(cell);
 
        // add row to table
        this.tblFiles.Rows.Add(row);
    }
}
 

"A" for effort (affort?), but there's still a problem. Now when we click to delete TheFamily.jpg after deleting InfinitiesLoop.jpg, this is what we get.

Delete TheFamily.jpg again

Nothing happens except the postback. If you examine the control IDs this time, you will see that instead of resetting the IDs by clearing out the table and rebuilding it, the rows simply continued the ID sequence. That's because Controls.Clear() does not reset the automatic ID counter unless the control implements INamingContainer. HtmlTable does not implement INamingContainer.


Aside: What is INamingContainer?

INamingContainer is a marker interface, meaning it has no methods to implement. A control "implements" this interface to let the framework know that it plans on giving it's child controls really specific IDs. It's important to the framework, because if a two instances of the same control are on the same page, and the control gives its child controls some specific ID, there'd end up being multiple controls with the same ID on the page, which is going to cause problems. So when a Control is a naming container, the UniqueID for all controls within it will have the parent's ID as a prefix. This scopes it to that control. So while a child control might have ID "foo", its UniqueID will be "parentID$foo" (where parentID = the ID of the parent). Now even if this control exists twice on the page, everyone will still have a UniqueID.

INamingContainer also has the property that any controls within it that do not have a specific ID will have its ID automatically determined based on a counter that is scoped to it. So if there were two naming containers, foo and bar, they might have child controls with UniqueIDs foo$ctl01 and bar$ctl01. Each naming container gets its own little counter.

Note that the ID "foo$ctl01" does not necessarily imply that ctl01 is a direct child control of foo! All it means is that foo is ctl01's naming container (control.NamingContainer). It's parent might be another control which is not a naming container.


So we can solve this problem once and for all by using a custom HtmlTable control that implements INamingContainer. I won't bother showing that... because there's an even better solution.

Remember this scenario is that you don't know "how many" controls there should be at design time. We do, however, know "what" the controls are. You can do it dynamically, if you manage the control tree correctly. But when you know what the controls will be ahead of time, Templating can step in and take care of all of this for you automatically!

<asp:Label ID="lblStatus" runat="server" EnableViewState="false" />
 
<asp:Repeater ID="rptFiles" runat="server" EnableViewState="false">
    <HeaderTemplate>
        <table cellpadding="4" cellspacing="4" border="1">
    </HeaderTemplate>
    <ItemTemplate>
        <tr>
            <td><%# Path.GetFileName((string)Container.DataItem) %></td>
            <td>
                <asp:LinkButton runat="server" Text="delete"
                    CommandArgument="<%# Path.GetFileName((string)Container.DataItem) %>" />
            </td>
        </tr>
    </ItemTemplate>
    <FooterTemplate>
        </table>
    </FooterTemplate>
</asp:Repeater>

Using a repeater, we declare what each row will look like. This is what I mean by "what" vs "how many". We know what, so we can express "what" via the repeater ItemTemplate. The "how many" will come from databinding the repeater, which will repeat the template once for each item we bind to it.

protected override void OnLoad(EventArgs e) {
    this.rptFiles.ItemCommand += new RepeaterCommandEventHandler(rptFiles_ItemCommand);
    BindRepeater();
    base.OnLoad(e);
}
 
private void rptFiles_ItemCommand(object source, RepeaterCommandEventArgs e) {
    // command argument contains the file name to delete
    string fileName = Path.GetFileName((string)e.CommandArgument);
    string path = Path.Combine(Server.MapPath("~/uploaded"), fileName);
 
    File.Delete(path);
    this.lblStatus.Text = "Deleted " + fileName;
 
    // removed a row, rebind the repeater
    BindRepeater();
}
 
private void BindRepeater() {
    string[] files = Directory.GetFiles(Server.MapPath("~/uploaded"));
    this.rptFiles.DataSource = files;
    this.rptFiles.DataBind();
}

This is what Databound controls are good at. Let them do their job. Doing things dynamically when you don't really need to only complicates things. This design is so much better in so many ways. For one, notice we really have no UI related code in the code-behind (except for the 'status' message). ASP.NET's code-behind model is meant to separate code and UI. Also, notice that there's considerably less code! Why? Because the repeater takes care of the following things for us: (1) it implements the foreach loop. We give it something to enumerate over and call DataBind, it does the rest. (2) it implements the creation of the controls for us. Controls are still being created dynamically at runtime, but we've handed that responsibility to the repeater by describing for it what an item should look like. (3) it implements INamingContainer. Calling DataBind on a databound control throws away its contents and rebuilds it from scratch, just like we had to do. All we need to worry about is maintaining the data and letting the repeater know when the data has changed.

Example: You don't know "what" controls should be rendered at design time, or you want to avoid loading controls you don't need because of performance or because there are too many possibilities.

If you don't know what controls will be rendered in the first place, you have to use dynamic controls, right? Well, even then, it depends. Maybe you don't know what control you will need, but the possibilities are limited. In that scenario, you can avoid dynamic control complexities by simply declaring every possible control with Visible=false, then switch the one you want on by making it visible. Like the repeater example above, this takes the burden of being responsible for the control tree out of your hands. It will also work well if it's possible for the control that is loaded to change during a postback due to a change in state. Since you're loading them all anyway, it doesn't matter.

What about performance?

I've suggested this solution a number of times to readers who sent me their problems. A lot of the time, they were doing it dynamically instead of using the visibility trick because they considered it better for performance. True, loading two controls is more work than loading one, especially when you can know one isn't needed early on. But you're splitting hairs, my friend! Controls, whether they be built in controls, custom server controls, or user controls, are efficient. It doesn't take many resources to instantiate one. It doesn't take many resources to add it to the control tree.

The only time I'd be worried about the performance of a control on the page that doesn't need to exist is if the code in the control is going to do something it doesn't need to do, or if it has a lot of ViewState associated with it. Most of the time, that's not the case. If all the control does is render some html and maybe process some postback data, it isn't worth the effort. If the problem is the amount of viewstate it contains... well, then turn it off, and just enable it for the one control that you do need. If the problem is the operations the control is going to do, like query a database, then that control isn't coded correctly. Control's typically shouldn't go off and do things on their own. They should be told when to do things by the page it lives on. Controls should almost never call databind on themselves, unless they are designed for a really specific purpose where you just want the control to do its own thing. Refactor the control so it has to be told when to do that expensive operation, such as by putting the logic in the DataBind method.

Ok. Let's move on. Say you can't just load them all ahead of time. Maybe the control to load depends on a setting in your database. Maybe there are hundreds of possibilities based on input. The next question to ask is whether the control to load is dependent on state information on the page.

For example, if your database holds the path to a user control which acts as the footer to your layout, that's probably pretty static. It probably doesn't depend on any state information. So all you have to do is load the control and add it to the tree every request. No issues. Just make sure you do it as soon as you can in the page life cycle. OnInit preferably.

If the control(s) to load depend on state data, then that's another story. In this example, we have two radio buttons and a place holder. The dynamic control is loaded into the placeholder, but which control we load depends on which radio button is selected. If you wish, you can also imagine that the control we load is dependent on a setting in web.config. But for this example we'll just use two hard controls.

First we'll define two user controls, UserControl1.ascx and UserControl2.ascx.

<%@ Control Language="C#" %>
<script runat="server">
    private void ButtonClick(object sender, EventArgs args) {
        this.lbl.Text = "Clicked UserControl1";
    }
</script>
UserControl1<hr />
<asp:Label ID="lbl" runat="server" /><br />
<asp:Button ID="cmd" runat="server" Text="Click" OnClick="ButtonClick" />

UserControl2 looks the same except has "UserControl2" instead of "UserControl1". Each has a label and a button. When the button in control 1 is pressed, it updates the label in control 1. When the button in control 2 is pressed, it updates the label in control 2. Keep in mind that each control has its very own button and label. There is no label on the form:

<%@ Page Language="C#" %>
<script runat="server">
    protected override void OnLoad(EventArgs e) {
 
        if (this.opt1.Checked) {
            ph.Controls.Add(LoadControl("~/UserControl1.ascx"));
        }
        else if (this.opt2.Checked) {
            ph.Controls.Add(LoadControl("~/UserControl2.ascx"));
        }
 
        base.OnLoad(e);
    }
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Pick-a-control</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:RadioButton ID="opt1" runat="server" Text="Number 1"
            AutoPostBack="true" GroupName="g" Checked="true" />
        <asp:RadioButton ID="opt2" runat="server" Text="Number 2"
            AutoPostBack="true" GroupName="g" />
        <hr />
        <asp:PlaceHolder ID="ph" runat="server" />
    </div>
    </form>
</body>
</html>

The devil is in the details. Let's load it up. UserControl1 will load by default, so lets just go ahead and make sure it's working by clicking on the button:

Clicked on UserControl1

So far so good. It says "Clicked UserControl!" like we expected. It loaded, we posted back, it reloaded, and it successfully processed the click as if it were a declared control. Beautiful. Let's swap over to UserControl2 now... but we won't click on its button yet...

Switched to UserControl2

What? That's weird. According to this, I've loaded UserControl2, but it's label has the data I put into UserControl1's label! Voodoo!

That's nothing. Now let's have some fun. Change the label control in UserControl2 to a TextBox. Don't even give it the same ID.

<%@ Control Language="C#" %>
<script runat="server">
    private void ButtonClick(object sender, EventArgs args) {
        this.txt1.Text = "Clicked UserControl2";
    }
</script>
UserControl2<hr />
<asp:TextBox ID="txt1" runat="server" /><br />
<asp:Button ID="cmd" runat="server" Text="Click" OnClick="ButtonClick" />

Now what happens when we switch from control 1 to control 2?

Voodoo!!

Ohhh yah... we're hacking now. We've successfully loaded the ViewState for the Label in UserControl1 into the TextBox in UserControl2. They both have a "Text" property, which both happen to use "Text" as the ViewState key to remember the value in. *High five*

I mean this to drive home an important point. The control tree into which viewstate is loaded must basically match the control tree which was used to save that viewstate. Coming from more procedural web frameworks (like ASP), you might tend to think of the life of a control to be over once it renders itself. But really, I like to think of it as if a control's lifecycle straddles the request/response boundary. It does indeed get re-instantiated upon every request, but because ASP.NET manages state data for us, the control created on a postback is intimately connected with its "predecessor", for lack of a better word. This more "logical" lifecycle ends after the control has loaded its ViewState and its postback data from its previous life, if any. After that, but before it begins its next life in PreRender, you can make persistent changes to the control tree with no worries.

So here is the solution to the above problem. Every scenario is different, so this isn't necessarily a real general pattern you should follow to the tee, but it shows how we do things the right way for this scenario. Hopefully you can adapt the solution to your specific needs.

<%@ Page Language="C#" %>
<script runat="server">
    protected override void OnLoad(EventArgs e) {
        if (!Page.IsPostBack) {
            // no viewstate on initial request, load the default control
            ViewState["state"] = opt1.Checked ? 1 : 2;
            LoadUserControl();
        }
        base.OnLoad(e);
    }
 
    protected override void LoadViewState(object savedState) {
        base.LoadViewState(savedState);
        // viewstate loaded, now we know which control to show.
        LoadUserControl();
    }
 
    private void CheckChanged(object sender, EventArgs args) {
        // state has changed. Remove the loaded control and load the new one.
        ViewState["state"] = opt1.Checked ? 1 : 2;
        ph.Controls.Clear();
        LoadUserControl();
    }
 
    private void LoadUserControl() {
        Control c;
        int state = (int)ViewState["state"];
        if (state == 1) {
            c = LoadControl("~/UserControl1.ascx");
        }
        else {
            c = LoadControl("~/UserControl2.ascx");
        }
        // id assigned to avoid shifting IDs when control changed on a postback
        c.ID = "foo";
        ph.Controls.Add(c);
    }
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Pick-a-control</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:RadioButton ID="opt1" runat="server" Text="Number 1"
            AutoPostBack="true" GroupName="g" Checked="true"
            OnCheckedChanged="CheckChanged"  />
        <asp:RadioButton ID="opt2" runat="server" Text="Number 2"
            AutoPostBack="true" GroupName="g"
            OnCheckedChanged="CheckChanged" />
        <hr />
        <asp:PlaceHolder ID="ph" runat="server" />
    </div>
    </form>
</body>
</html>

The idea is to load the control that existed on the previous request by utilizing a ViewState field to remember which control was active. We override LoadViewState, then immediately after calling base.LoadViewState, we can look for the ViewState value to tell us which control existed previously. On the initial request, there is no ViewState and therefore no call to LoadViewState, so we detect this in OnLoad and make sure the default control is loaded at first. Then, we listen for the CheckChanged event on the radio buttons to tell us when the control to be loaded has changed (note: I actually really dislike the CheckChanged event, but that's a different discussion. It works well for this simple scenario).

At the time the CheckChanged event fires, we will have already loaded a user control -- whichever one was active previously. So we have to remove it before adding the new one. That is why we call Controls.Clear() on the placeholder. And finally, we assign the control a specific ID so there's no way we can run into the ID problem mentioned earlier, which would happen when removing the old control and adding the new one in response to the CheckChanged event. There's no way we could accidentally post data from one control into another as you switch from one control to another, because we're loading the correct control prior to the loading of post data. Post data is loaded right after LoadViewState. That is why we don't do the logic from OnLoad -- the user control would miss that phase. Actually, ASP.NET loads post data in two phases, one before OnLoad / after LoadPostData, and one right after OnLoad. The purpose of the 2nd pass after OnLoad is to load postdata for any controls that may have been dynamically added (or created through databinding) during OnLoad. That would actually work just fine for our scenario, but there are consequences to this late loading that are best avoided if possible. Normally, you can rely on post data to have been completely loaded from the OnLoad phase. But for late-comers, that isn't the case. It's best to provide consistent behavior if you can, so LoadViewState is where it's at!

Actually, this very closely emulates what data bound controls do. If you were to examine the code for the Repeater, for example, you would find that from its CreateChildControls method it examines a ViewState field. If it exists, it calls its CreateControlHiearchy method, which rebuilds its control tree based on ViewState. When it is DataBound, it clears the control collection and rebuilds it again, calling the same CreateControlHiearchy method.

THAT'S ALL FOLKS. In the next part we'll cover custom server controls and some of the things to watch out for there.

UPDATE: Click here to download the Sample Code referenced in this article.

One more thing...

Many of you apparently were so anxious to read part 4 of the series, you cleverly deduced that the url to the article must be the same as Part 3, only with a "4". You URL HAXXOR, you!!!!111... You see, I've been working on a draft of this article for a long time now (which as it turns out has been completely redone), and I unknowingly had the article saved in a state that would allow you to access it if you happened to know the address to it!

In all, by the time I realized what was happening, this article received 121 hits to it even before it was published! All you hackers got to read my embarrassingly terrible draft. I thwarted you by renaming the article temporarily. I added "abc" to the end. I was waiting for someone to guess that, too. If you did, there would have been a surprise in it for you. But no winners. Oh well....

Until next time.... part 5 will come much sooner than part 4 did, I promise.

Filed under: , ,

Comments

# ASP.NET Dynamic controls

Sunday, September 02, 2007 6:31 AM by primoz.tech.blog

ASP.NET Dynamic controls

# re: TRULY Understanding Dynamic Controls (Part 4)

Sunday, September 02, 2007 8:42 AM by AndrewSeven

I like the fact that you often come back to alternatives to dynamic controls.

I've answered a lot of questiong on aspmessageboard.com about dynamic controls and most of the time the best answer is to use one of the alternatives.

In a recent project, I needed tables where you could add and remove rows so I extended the gridview so I could extract its data, remove a row from the data, then rebind.

# re: TRULY Understanding Dynamic Controls (Part 4)

Sunday, September 02, 2007 12:22 PM by Joseph A. Habdank

well good article - It is a great pity that you did not publish it earlier (like a month ago). Due to the problem which you described under example 2 I had to disable the ViewState - and I have to go with it now. If I had known that solution before life could have been easier.

Btw. to all very abiscious ASP coders:

forget about using dynamic controls unless there is NO OTHER WAY (and I mean it). ASP dynamic controls show a *lot* of problems if you do not know very well the ASP (inner structure, and very good understaing of databinding process, event flows etc).

e.g. - even if you have to switch between 5 GridViews with their SQLDataSources etc - forget about it. Just have them all statically and change a Visible property accordingly to your needs. I did not do it that way, AND I REGRET IT BADLY now.

# re: TRULY Understanding Dynamic Controls (Part 4)

Sunday, September 02, 2007 10:35 PM by Md Sarker

I am recently having a similar problem with a datalist. No events from any control inside the EditItemTemplate getting fired. I have EmableViewState=false for page. Any solution? Please email info@webcosmo.com

# re: TRULY Understanding Dynamic Controls (Part 4)

Monday, September 03, 2007 4:31 AM by liggett78

I'm really having hard time trying to read all these dark-blue, grey, dark-green etc. words on the black background. You should consider switching at least the background color.

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, September 04, 2007 4:48 PM by Dani

Hi

I've read a lot of your articles and they have been very helpful!

So, here is my issue that i have not been able to resolve. I have custom controls (.ascx) being added dynamically to statically placed placeholders on the page. I need to add the controls dynamically beause their location, which placeholder they belong in, is driven by xml files configured by the some administrator.

I've followed the implemention you have with loading the dynamic controls using the Init() and LoadViewstate. But, for some reason, when a postback occurs, i lose all entered data for Textbox, state of Checkbox, state of Combobox and basically every control who's state can change. If i make the Textbox Readonly=True, then the values remain after each postback. I see you had to update ViewState for the checkboxes in your example above to maintain the state the user selected. I can't do that since my controls are outside the scope of my page. i've tried doing FindControl but with no success.  So, if you have any suggestions for preserving the state of these controls (textbox, checkbox, combo) i would really appreciate it!

Thanks again for your articles!

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, September 04, 2007 5:04 PM by InfinitiesLoop

Dani -- I would have to see some code, or at least get some more details. Exactly when and where are you loading the control? And are you giving it an ID?

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, September 04, 2007 6:32 PM by Dani

Hi,

Thanks for the quick response.

I have a LoadCustomControls() function tha is being called by before base.OnInit(e) and if it's not a postback and also after base.LoadViewState(savedState). Every control has an ID and i am also clearing call controls from each placeholder by calling the PlaceHolder.Controls.Clear().

Also, inside each control i am loading the data on the condition that is not a postback. So if(!IsPostback){LoadControlData();}

I can email you some code examples of exactly what i'm doing if that helps.

Thanks again.

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, September 04, 2007 7:40 PM by InfinitiesLoop

Why do you only load in OnInit when it is not a postback? Does the control that you are loading depend on any state data? You said it depends on a configuration file, so unless there's more to it, the answer is no. And if the answer is no, you should just load it from OnInit every request and forget about LoadViewState.

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, September 05, 2007 10:49 AM by linus

hi, i have a similar problem as in example 2.

however, i have textbox in the ascx for the user to enter. there is a 'save' button in the aspx page. the save button is suppose to capture the textbox entry and save it to the database. how do i capture the entry by the user? i know that you need to cast it and access the expose the property set in the ascx page. but, i want to know how would u implement it.

cheers and thanks

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, September 05, 2007 11:51 AM by InfinitiesLoop

linus -- so if I understand correctly, you just need to get the value in the textbox from the page that is hosting the user control? You could use FindControl to get to the textbox if you know it's ID. But I wouldn't do it that way. I would do as you say -- create a property on the user control that exposes the textbox value. Then on the aspx, cast the user control to the type of the code behind (depending on the project model you are using, you may need to add a @reference directive) and access the property. That method allows the communication you need without hard coding the control ID, or even hard coding the fact that it's a textbox. You are free to change those details in the ascx without breaking the page.

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, September 05, 2007 9:22 PM by linus

ok thanks for the guidance... my next question would be...

if my dynamic ascx is only to be loaded at runtime or should i say on postback, do i have to do the creation of the dynamic content in the init event as adviced in this article, aspnet.4guysfromrolla.com/.../092904-1.aspx. ?

i know that i can't do the creation on the button event as it would have reset the textbox entries in the ascx when i click the 'save' button. hmm what should i do?

cheers and thanks

# re: TRULY Understanding Dynamic Controls (Part 4)

Thursday, September 06, 2007 10:37 AM by CurlyFro

what if you had Labels and label update Buttons in UserControl1 & 2.  would the events get fired and correctly update their associated label?

# re: TRULY Understanding Dynamic Controls (Part 4)

Thursday, September 06, 2007 11:15 AM by InfinitiesLoop

CurlyFro -- The controls will behave just as they normally would.

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, September 11, 2007 8:07 AM by charlotte

Great articles, keep up the good work :)

I just started building a solution with dynamic controls, but stopped immediately after reading your part 1-4 above.

Though, after reading, I am all confused about what to do - I am new to .NET so that might be why I cannot figure it out myself.

I have to create a - dynamic!? - form builder with anywhere from 0 to N groups, each group with 1 to N checkboxes or 2 to N radiobuttons inside. And each grouped list of either checkboxes or radiobuttons can be displayed in 1-5 columns. The number of groups, checkboxes, radiobuttons, and columns vary from form to form. Though the form is not dynamically in the sense, that user input can change it. Whenever the form has been build (by web editors), that's what it looks like.

If not creating dynamic controls to build the groups of checkboxes/radiobuttons, what could I do instead?

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, September 11, 2007 4:30 PM by InfinitiesLoop

charlotte --

Nice scenario. I think you should be able to do it without anything dynamic. Even though your data is dynamic on a couple different dimensions, you always know WHAT controls would be rendered. Here's a rough outline that would get you close...

<asp:Repeater DataSource="<%# GetGroups() %>" ...>

   <ItemTemplate>

        <%# Eval("GroupName") %>

         <asp:CheckBoxList Visible="<%# Eval("IsCheckBoxList") %>" DataSource='<%# Eval("Items") %>' />

         <asp:RadioButtonList Visible="<%# !Eval("IsCheckBoxList") %>" DataSource='<%# Eval("Items") %>' />

   </ItemTemplate>

</asp:Repeater>

The GetGroups method returns a list of group objects, each which contains info about the group as well as a 'Items' property or method that returns a list representing the choices within that group. They also contain a property IsCheckBoxList which returns true if it should be a checkbox list or false if it should be a radio button list (a more UI-agnostic name might be better, like AllowMultiple).

Then you simply build both a CheckBoxList and a RadioButtonList, but their visibility is mutually exclusive based on the boolean value of IsCheckBoxList/AllowMultiple.

That control itself allows you to specify the number of columns the options should be rendered in, which you databind with another reference like RepeatColumns="<%# Eval('Columns') %>". Note that even the datasource of these controls is set declaratively. All you have to do is call DataBind() on the root repeater and everything is set into motion automatically.

Note that it may be that RadioButtonList/CheckBoxList do not render in the way you would require it, but the concept would be the same even if you had to create your own custom control that renders them in TDs or something.

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, September 12, 2007 5:24 AM by charlotte

wow - thanks !!! for your quick reply - I'll go ahead right away - you are a darling :))

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, September 12, 2007 8:13 AM by Nicola

I need to put two controls with the same id in two different panels.

I understood that it would be possible since I gave to the two panels two different id so that, say, the uniqueids of my two controls would be something like:

Panel1$Textbox1 and

Panel2$Textbox1

VS2005 doesn't allow me to do this.

Isn't panel implementing INamingContainer ? Maybe I misunderstood the meaning of INamingContainer ....

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, September 12, 2007 12:30 PM by InfinitiesLoop

Nicola -- Panel does not implement INamingContainer. That would be way too much for the main scenario panel is used for.

INamingContainer is really meant for situations where you know for sure there's going to be duplicate IDs, or its a possibilty and you don't know.

For example, repeater implements INamingContainer, because it is going to be repeating the declared item template, which likely has a control within it with an ID.

UserControls implement INamingContainer, because they might contain a control with a specific ID and someone might put multiple instances of the user control on the same page.

You don't have either of those risks with a panel.

Even if it did implement it, ASP.NET assigns controls to the fields named after the ID. So you can't expect "this.foo" to refer to one of the panels, because how does asp.net know which one it should refer to? For that reason you generally can't have multiple controls with the same ID declared within the same markup on the same page or user control.

Why do you require this? Perhaps there's a better way.

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, September 14, 2007 9:05 AM by Nicola

I don't understand this:

"ASP.NET assigns controls to the fields named after the ID"

But, if I understand the rest, I can create a user control with a panel inside and use it! I will try.

I need to do this because I am involved in this fool project: we have a win32 form designer which stores form definitions in a database.

A client application (again win32) can read this form definitions (along with lot of other data dictionary informations: user profiles, data catalogues and so on) and renders the UI dynamically, actually running a complete application. It is a kind of framework, something similar to Access (we called this app of ours "Ouverture"). Now I am trying to port this logic to asp.net to have a web based client. The form definitions are hierarchical, one form could be placed inside another, and so I can have components with same name, provided that they are in *different containers* (if it wouldn't be so, it would be a problem even for the win32 client).

I sometimes feel frustrated but I keep fighting :-) (and your articles are a great support for me indeed..)

Thanks

Bye

Nicola

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, September 14, 2007 10:31 AM by Nicola

With a user control containing just a panel it works. But perhaps are there any risks / dangers in this approach ? You seem quite careful about user controls.. or not ?

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, September 14, 2007 12:10 PM by InfinitiesLoop

Nicola -- UserControls implement INamingContainer, that's why it works. If all you need is to be able to contain the item with a naming container, then you should implement a simple Container control that implements INamingContainer and use that instead. It would be much simpler. And easy too.. here's the entire class

public class NamingContainer : Control, INamingContainer

{

}

done :)

# re: TRULY Understanding Dynamic Controls (Part 4)

Monday, September 17, 2007 11:41 AM by Joao

Hi.

I'm trying to build a custom server control for creating a datagrid.

And I can't solve the following problem:

I created a custom template where I build these LinkButton controls: Edit, Delete/Update,Cancel to represent all those actions but when I click on a button the ItemCommand Event isn't raised. I only occurs when I press the button a second time.

Any help would be apreciated.

Joao

# re: TRULY Understanding Dynamic Controls (Part 4)

Monday, September 17, 2007 1:25 PM by InfinitiesLoop

Joao -- sounds like exactly what would happen if your IDs were different on postbacks. The first time its ignored because the id changes on the postback, then the second time it works because it was a postback before and after. You'll have to send me some code to tell you exactly why that might be happening. Turn on tracing and watch the control tree as you click the button -- if its id changes, find out why by thinking about how and when it is created.

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, September 18, 2007 5:57 AM by Joao

Hi,

I'm trying to create a custom server control and I can't make it work.

It's a dynamic datagrid, I created several ITemplate classes and one of them has the buttons: Edit,Delete/Update,Cancel. When I click in one of the buttons for the first time it doesn't fire the ItemCommand event, the event only occurs when I click a second time in the same button.

Any idea why?

Joao

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, September 18, 2007 1:27 PM by InfinitiesLoop

Joao -- see above comment

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, September 18, 2007 6:27 PM by julianmj

ok ok, I already know that is not a very good idea use OnLoad, I don't want to commit a ViewState crime!!

How can I dynamically populate a control (DropDown, CheckBoxList)??

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, September 18, 2007 6:49 PM by InfinitiesLoop

julianmj -- databinding?

cbl.DataSource = GetMyData();

cbl.DataBind();

I think you can add to the items collection manually if you'd rather. Whether you do that from OnLoad or elsewhere depends on your scenario. Earliest you can do it, is when you should, and disable viewstate on the thing if you can do it every request.

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, September 19, 2007 9:26 AM by julianmj

Sorry but my first comment does not appears.

I wrote:

I have made some custom controls(Dropdownlist, Checkboxgroup) with new properties, I use this properties to create a query and populate the control dynamically. I do this sending to a method this collection: Form.Controls, then  search for every custom control and do the databind for each one.

Now, after I read your articles I modify the custom control class and add this method:

public class GrupoRadio : RadioButtonList,IControlBind

   {

...

      protected override void OnInit(EventArgs e)

       {

           //IdOrigenDatos is a new propertie

           this.DataSource = GetData(this.IdOrigenDatos);

           this.DataBind();

           base.OnInit(e);

       }

...

}

Is this correct??

Again sorry and thanks !!! for your quick reply.

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, September 19, 2007 1:13 PM by InfinitiesLoop

julianmj -- Is it correct... well is it working or are you having any problems? You might want to disable viewstate on that control since you're binding it every request anyway.

# re: TRULY Understanding Dynamic Controls (Part 4)

Thursday, September 20, 2007 12:27 PM by julianmj

Thanks again. It is working and I already disable ViewState. I'm leaving that way, it worked for me.

I will read again all your articles to find a way to improve my controls...

(sorry for all the typos, I'm still learning English) :)

# re: TRULY Understanding Dynamic Controls (Part 4)

Saturday, September 22, 2007 1:35 PM by Alexander Sher

Hi Dave!

Several month ago in one of the topis on gotdotnet.ru forum we got similar question about 2 dynamically loaded user controls. But my solution was a bit different (adapted to your example):

private bool optChanged;

protected override void OnLoad(EventArgs e) {

   base.OnLoad(e);

   if (IsPostBack) {

       ((IPostBackDataHandler)opt1).RaisePostDataChangedEvent();

       LoadControls(opt1.Checked ^ optChanged);

       ph.Controls.Clear();

   }

   LoadControls(opt1.Checked);

}

private void LoadControls(bool loadFirst) {

   Control ctl;

   if (loadFirst) {

       ctl = LoadControl("~/UserControl1.ascx");

   } else {

       ctl = LoadControl("~/UserControl2.ascx");

   }

   ctl.ID = "foo";

   ph.Controls.Add(ctl);

}

protected void CheckChanged(object sender, EventArgs e) {

   optChanged = true;

}

What can you say about it?

# re: TRULY Understanding Dynamic Controls (Part 4)

Sunday, September 23, 2007 6:23 AM by Alexander Sher

Oops, sorry! My last piece of code will not work ;) Correct variant might look like this:

private bool optChanged;

protected override void OnLoadComplete(EventArgs e) {

   base.OnLoadComplete(e);

   if (IsPostBack) {

       LoadControls(opt1.Checked ^ optChanged);

       ph.Controls.Clear();

   }

   LoadControls(opt1.Checked);

}

private void LoadControls(bool loadFirst) {

  Control ctl;

  if (loadFirst) {

      ctl = LoadControl("~/UserControl1.ascx");

  } else {

      ctl = LoadControl("~/UserControl2.ascx");

  }

  ctl.ID = "foo";

  ph.Controls.Add(ctl);

}

protected void CheckChanged(object sender, EventArgs e) {

  optChanged = true;

}

# re: TRULY Understanding Dynamic Controls (Part 4)

Sunday, September 23, 2007 4:13 PM by InfinitiesLoop

Alexander -- events like CheckChanged occur after OnLoad so I'm not following what the code is doing... it seems like optChanged would always be false. It also seems that on postbacks you're always going to load the control(s) twice even if the active one has not changed.

If you use LoadViewState instead you are able to get the value of opt1 before it loads its postdata, which means it will always be whatever it was on the last request. You load the appropriate control there. The the changed event, if it has indeed changed, is raised. You clear the existing control then load the new control.

# re: TRULY Understanding Dynamic Controls (Part 4)

Monday, September 24, 2007 2:18 AM by Alexander Sher

> events like CheckChanged occur after OnLoad

Yes, that's why in second example I've used OnLoadComplete instead. So it works.

But I don't understand about LoadViewState. As I know, page LoadViewState will be called before opt1.LoadViewState. Surely, I can create new control, inherited from RadioButton, override LoadViewState method and create something like WasChecked property or add event that will occur after LoadViewState and before Page.ProcessPostData. But can I do it without creating new controls?

# re: TRULY Understanding Dynamic Controls (Part 4)

Monday, September 24, 2007 12:38 PM by InfinitiesLoop

>> As I know, page LoadViewState will be called before opt1.LoadViewState

Yes, thats why you call base.LoadViewState from your override first. It's recursive, so by then the checkbox/radiobutton will have loaded its ViewState.

# re: TRULY Understanding Dynamic Controls (Part 4)

Monday, September 24, 2007 7:03 PM by Alexander Sher

> It's recursive, so by then the checkbox/radiobutton will have loaded its ViewState.

Well, please correct me if I'm wrong. LoadViewState is called recursively for all controls that have something in viewstate, but it doesn't produce this recursion. As I understood, recursion is produced by three internal methods - LoadViewStateRecursive, LoadChildViewStateByID and LoadChildViewStateByIndex. And LoadViewState is called before LoadChildViewStateByID or LoadChildViewStateByIndex. That's why I think opt1 viewstate will be loaded after Page.LoadViewState is completed, and LoadViewState override will not help, especially when page viewstate is empty.

You see, I'm trying to reduce ViewState size and avoid adding data that already in there (radiobuttons already store their old values). May be itis better to look on the problem from another angle - disable opt1 and opt2 viewstates and manage changes manually?

# re: TRULY Understanding Dynamic Controls (Part 4)

Monday, September 24, 2007 7:21 PM by InfinitiesLoop

No you're absolutely right about LoadViewState -- child state is loaded after it is completed. My bad. But my lapse of reason had a purpose because I was intending for it to be used as part of the "manual" tracking (when you'd always have a key to load, and it would always be loaded after calling base). Doing that and disabling the opt's ViewState would probably result in smaller state, slightly. Kind of splitting hairs at that point though.

I still think you're doing too much with calling LoadControls() twice even when the state didn't change. Why not add an IF around it...

if (optChanged) {

LoadControls(opt1.Checked);

}

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, September 25, 2007 5:36 AM by Nicola

I understand that, using dynamic controls, I have to rebuild the control tree at each postback.  Would it be possible to save the control tree (once generated the first time) somewhere in some format and restore it on next postbacks ? Doing so may save me some processing..

Thanks bye Nicola

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, September 25, 2007 11:14 AM by Stephen

i'm working on a UserControl that has a customizable number of rows of data.  There can be 0 or there can be infinity or anything in between.  I've got add and remove link buttons.  

The code is in the URL http://pastehere.com/?jfifwt .  I originally did all the stuff without a repeater and dyamically created all the controls on Page_Load, which as you have noted, was a logistical nightmare.  So, as per your suggestion I've recoded with the repeater, trying to bind it to a dataset in the viewstate.  I can add items, and remove items with the add and remove button.  The data in the repeater items persist through postbacks... but any time I click on the add or remove linkbuttons all my data in the text fields of all the items disappear.  I figure it has to do with the fact that my underlying datasource (The dataset in the viewstate) doesn't contain the modified data.  but if it is data bound, why not? and what is the best way to fix this.  The repeater is a much simpler solution than I had before.  but I'm not able to get this to work.

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, September 25, 2007 1:11 PM by InfinitiesLoop

Nicola -- Say you did somehow save the control tree. Where would you put the data? On the client for postback? How would you process that data? Parse it, and rebuild the tree? So, you're going to be processing something anyway. Why not cut out the middle man and just process it the way you normally do? One way or another something has to rebuild the tree -- even if asp.net did it for you, it would be using resources that you can eliminate.

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, September 25, 2007 1:53 PM by Stephen

I got it figured out. The dataset and the repeater was not quite in sync.  So I caught it in the Page_Load and updated the information in the dataset with the information in the Repeater, foreach repeateritem item in Repeater.Controls, then a FindControl("txtboxes") on each of the controls and saved them to the datatable using the index of item.ItemIndex.  If anyone is lost on what I'm saying, a link to my source is in the post above. I had just got done converting a mess of dynamic controls to use the repeater control and was losing my information between postbacks.  The actual code I added is below.  Not beautiful, but it works.  Your article series was GREAT and allowed me to refactor a large unwieldy code  base to something a lot more comfortable to deal with.

       // Fill the ViewState Datasource with information.

       foreach (Control item in Redux1.Controls)

       {

           if (item is RepeaterItem)

           {

               if (((RepeaterItem)item).ItemIndex > -1)

               {

                   // Find if the controls are in this Repeater Item

                   TextBox txtName = (TextBox)(item.FindControl("fldName"));

                   DropDownList ddlMonth = (DropDownList)(item.FindControl("iMonth"));

                   TextBox txtDay = (TextBox)(item.FindControl("fldDay"));

                   TextBox txtYear = (TextBox)(item.FindControl("fldYear"));

                   TextBox txtPerc = (TextBox)(item.FindControl("fldPercent"));

                   // if the controls are found save them to the dataset.

                   if (txtName != null){

((DataSet)ViewState["dsetOwners"]).Tables[0].Rows[((RepeaterItem)item).ItemIndex]["fldName"] = txtName.Text;}

                   if (ddlMonth != null)

                   {

((DataSet)ViewState["dsetOwners"]).Tables[0].Rows[((RepeaterItem)item).ItemIndex]["iMonth"] = ddlMonth.SelectedValue;}

                   if (txtDay != null){ ((DataSet)ViewState["dsetOwners"]).Tables[0].Rows[((RepeaterItem)item).ItemIndex]["fldDay"] = txtDay.Text; }

                   if (txtYear != null)

                   { ((DataSet)ViewState["dsetOwners"]).Tables[0].Rows[((RepeaterItem)item).ItemIndex]["fldYear"] = txtYear.Text;}

                   if (txtPerc != null)

                   { ((DataSet)ViewState["dsetOwners"]).Tables[0].Rows[((RepeaterItem)item).ItemIndex]["fldPercent"] = txtPerc.Text; }

               }

           }

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, September 25, 2007 2:18 PM by InfinitiesLoop

Stephen --

I love that you converted to repeater and it's working well. It's definitely the way to go...

As for your problem... well, when you are rebinding the repeater you have to think of it as if you are starting over. Anything not in the data you are binding is going to be thrown away.

But -- you probably have a way to get the data out of the repeater, correct? Like a save button that goes into each item and updates the corresponding row? You can use that exact same logic to aid you here. It's the reverse of setting the DataSource on the repeater -- you just need a method that gives you the DataSource back again from the state of the repeater. You do that, then you add or remove the row and rebind that. When it finally comes time to save, you use the same method to get back the dataset from the repeater and do what you want with it.

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, September 25, 2007 2:22 PM by InfinitiesLoop

Stephen -- nevermind you read my mind!

One thing that concerns me with the code is that you're saving the DataSet in ViewState. You shouldn't need to do that -- you can always rebuild the DataSet from scratch using the data in the repeater. By storing the DataSet you're really storing the data twice -- once in the dataset, once in the repeater.

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, September 25, 2007 3:00 PM by Alexander Sher

> I still think you're doing too much with calling LoadControls() twice even when the state didn't change. Why not add an IF around it...

Yes, you absolutely right and I'm really ashamed of this mistake.

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, September 25, 2007 4:16 PM by Nicola

Well I was thinking that, at the end, the result of all this process is "simply" some html/javascript. My idea was: why don't you save the html result somewhere (in a database ?) and then re-send it "as it is" from the second request on ? (just trying to understand better..)

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, September 25, 2007 5:07 PM by InfinitiesLoop

Nicola -- the result of rendering controls is just html/javascript, but thats not all they are good for. They react to postbacks, and change their state. They may render different data each time.

If you're rendering the same thing all the time and want to optimize for that, take a look at OutputCaching.

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, October 03, 2007 2:37 PM by Arif

How would you solve the issue of dynamic Controls being loaded inside different containers than the last time the page posted back. For example if you look at Web 2.0 startpages like iGoogle or PageFlakes you will notice that you can move around widgets entirerly using clientscript (without PostBack) & a callback to the server saves their new location. However, with the default ASP.Net ViewState management, the ViewState will never be able to get applied to the control because now the control was dynamically added to a different container than in the previous postback. Sure, you can re-add the control back to the same container first - so that ViewState can get applied - and then move it but that seems like such a hack and awfully slow. Would you recommend building custom ViewState in the Load/SaveViewState methods of the page, similar to the DynamicPlaceHolder?

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, October 03, 2007 2:43 PM by InfinitiesLoop

Arif -- the structure of the control tree does not have to dictate the structure of the rendered html. In this case I'd recommend a container control that knows how to selectively render its child 'modules' into the right locations, without physically moving them in the control tree. See my post on "Rendering ASP.NET Controls out of place" for an example of doing something like that. This particular solution can be more specific though without the use of that specialized Renderer control.

Another solution is to just position the 'modules' with css. It wouldnt matter what order they were in the html, the style would just put them in the right location. You'd have to have some client side positioning logic anyway if you're going to support moving them client side.

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, October 03, 2007 5:39 PM by g

hi, thanks for your article.  i'm currently chewing on some of the things you said about viewstate.  

my situation: i make a webservice call, from which i asynchronously receive a rendered dropdown, which gets placed on the page that makes the call.  the dropdown is a customized control, which has overriden events (eg. prerender) that conditionally add rows to the dropdown.  i'm instantiating the custom control in the webservice to bind it and render the html for the return.  what i'm noticing is that the prerender event of the control does not fire.  why would this be?

the thing that's different here from your explanations is that the control is not being added to the page control tree, as it's not exactly participating in the page life cycle.  

i've been looking for some resource to speak to this area but haven't found any yet.  your thoughts would be appreciated.

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, October 03, 2007 8:18 PM by InfinitiesLoop

g --

Events like Init and PreRender are driven by asp.net itself, not each individual control. That's why PreRender doesn't occur... no one told it to. Controls weren't designed to be 'hosted' independently so I don't think there's a great workaround for you there. You certainly wouldn't be able to participate in postback events with that control (which you may not care about anyway).

But... if what I feel about the control is right, then you dont need to do what you're doing from PreRender anyway. If you dont care about viewstate and postbacks then it wouldnt make any difference if you just did your dynamic item additions from Render instead of PreRender. Just keep in mind that since the control isnt in a control tree, it isn't going to have a fully unique ID either. Render this thing twice on the same page, and you've got two dropdowns with the same "name".

# re: TRULY Understanding Dynamic Controls (Part 4)

Thursday, October 04, 2007 2:31 PM by g

dave,

thanks for the prompt response!  your comments help fit some of the pieces together.  do you think the fact that a control doesn't go through its events is a design oversight?

in the interim i've started looking at script callbacks as well, which may be a good middle ground to settle in (vis-a-vis webservice calls).   we'll see.

# re: TRULY Understanding Dynamic Controls (Part 4)

Thursday, October 04, 2007 2:34 PM by InfinitiesLoop

g -- no problem. No I don't think its a design oversight. It's a limitation of the design I suppose. Controls going through lifecycle events without being in a page just doesn't make sense in asp.net because of how viewstate and postback data are processed, and because of INamingContainer.

Do you know about UpdatePanel? Allows you to make partial updates to the page without opting out of the page lifecycle.

# re: TRULY Understanding Dynamic Controls (Part 4)

Thursday, October 04, 2007 2:59 PM by g

a prompt response again - thanks :).  re: your comments on controls going through events - i'll chew on that.  what are the implications of inamingcontainer?

i do know about updatepanel.  i'm currently exploring my options for asynchronous page renderings - i'd like to know how the plumbing works.  using the updatepanel means subscribing to the ajax.asp framework, which means a couple of things that make me pause: 1) going through the whole page lifecycle on asynch postbacks; 2) leaving too much of the functionality to the black box (though maybe wonderful) of ajax.asp and resting on my laurels.

# re: TRULY Understanding Dynamic Controls (Part 4)

Thursday, October 11, 2007 2:36 PM by Josh

Thank you for such a great article.  It has taught me a lot around dynamic controls.  I have fought most of these issue on a  project where we used dynamic user controls.  I am playing around using templates to replace some of our very complex dynamic control code and I have a question.  I am using the following code to add my controls to the page:

<asp:Repeater ID="rptFees" runat="server" EnableViewState="False" DataSourceID="feeDS">

<ItemTemplate>

<gv:Fees ID="fee" runat="server" Amount=<%# DataBinder.Eval(Container.DataItem, "Fee_Amount") %>

FeeID=<%# DataBinder.Eval(Container.DataItem, "Fee_ID") %>

PaidBy=<%# DataBinder.Eval(Container.DataItem, "Fee_PaidBy") %>

Type=<%# DataBinder.Eval(Container.DataItem, "Fee_Type") %> />

</ItemTemplate>

</asp:Repeater>

This works great and the right number of gv:Fee user controls get added.  The problem comes in with post back events.  This user control has a button that when clicked needs to call a function back on the server.  

When I run the page, the controls get added but when I click the button, the server side code is never called.  Is there something extra you have to do to get the repeater to allow for this?

Thanks,

Josh

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, October 16, 2007 4:21 AM by Nicola

Is it possible to "reverse-engineering" a control collection to a .aspx page ? I have a function that dynamically builds at runtime a page. I am thinking about changing it in an offline process to statically build pages to be compiled, instead of build them on the fly. Would it be possible ? I looked at HtmlWriter class but it seems suited for pure html, what if you need to write an .aspx file ?

Thanks bye nicola

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, October 16, 2007 2:10 PM by InfinitiesLoop

Nicola -- I'm afraid nothing built-in is gonna help you here. But you can use an xml writer or xml document. Determining all the attributes and converting the values to strings won't be a trivial task though :) Don't forget about dynamically writing the @register directives.

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, October 17, 2007 12:23 PM by Rizzy

Hi, I read (skimmed) all the 4 parts.  Lots of good knowledge, thanks.  I have a problem where I'm using dynamic controls, and I don't know whether I can use a repeater like you showed in this part.

I have a page which allows the user to look up products.  To begin with, that page has 10 rows, which have a textbox for entering item num and a textbox for entering qty.

When the user runs out of the rows, they can click the "More" button to give them more rows.

My Draw() method adds rows and cells dynamically, to a statically declared table, and adds a textbox to each one of the cells dynamically.  For my textboxes, I do declare IDs, based on the numrow, and I keep count of current number of rows, so when I need to read in the user input values, I can do that.

Finally when the user is done, they click "Validate" which validates the items and quantities against a database by first reading in all the values and adding them to an object and calling object.validate().  The object then contains the validation info, basically text description for an item if it was valid, and a valid status, and error message if it was invalid, and an invalid status.

Based on this info, I get an enumerator into that object's list and while drawing my textboxes dynamically, I input the values from the list into the textboxes, and highlight the rows appropriately (red for invalid, green for valid).

I have to call my Draw() method from Pre_render event handler, which is a problem when I want to do validation using validators. I'd love to call it from Pre_Init, but that doesn't work because firstly, I don't know how many rows I have till I get to the button More's event handlers, and secondly I don't have the validated item list from the database till I get to the Validate's event handler.  So as far as I know it, it's impossible for me to get the validators to work.

Any ideas on how to solve this?

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, October 17, 2007 1:26 PM by Nicola

I understand, thanks. Another doubt: what about thread synchronization ? If I am dynamically building controls into a web page do I need to stick around some "lock(this)" statements ?

Or does the framework handle this process automatically ?

Thanks again! Bye Nicola

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, October 17, 2007 1:41 PM by InfinitiesLoop

Nicola -- there's only one thread that processes the page. No worries.

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, October 19, 2007 3:19 AM by Nicola

Really ? it is quite different from what I was used to with isapi development.  What about static class or Application / Session properties ?

I guess my question is a little off topic... Perhaps may you point me to a good resource about this topic ? thanks again again. Bye Nicola

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, October 19, 2007 1:07 PM by InfinitiesLoop

Nicola -- yes really. There's only one thread processing the request. The page, the instances of the controls, the entire control tree and all its state -- these are all unique to each request which is served by only one thread, so you're safe to go willy nilly. There are many requests occuring for different users, of course, so accessing static members or shared instance members still needs to worry about thread sync. Session doesn't, because unless you put it into read only mode, only one request from the user can access session at a time (it is internally syncrhonized already).

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, October 23, 2007 9:16 AM by Jenny

Great article!  I've been doing .Net for a few years but this helps me understand some of the behaviors I've seen in past projects.  

I like your idea of using a Repeater in cases where you know "what" you're going to put on a page, but not "how many" and I'm trying to use this in a test project.  I have a user control (RxWebObject.ascx) which contains an image button and a gridview for now.  It's associated with a business object which can have "child objects".  You can think of it as a hierarchy of objects.  When the user clicks on the user control, I want to have it display its children as user controls (so they can be clicked on too).  

For performance reasons (and because I don't know how the user might want to navigate through the children) I don't want to read all the data and display the entire structure at page load time.

Of course, VisualStudio.Net doesn't allow me to use circular references, so I can't put a reference to RxWebObject.ascx into the ItemTemplate of my Repeater in RxWebObject.ascx.

I AM able to use LoadControl in the codebehind of RxWebObject  to create my child RxWebObjects and then dynamically add them to a placeholder inside the user control, but then I run into plenty of problems that you've discussed in your article...they disappear, aren't clickable, etc.  Also, the business object associated with the control becomes "nothing" again on postback.

Do you have any suggestions for how I might handle this situation?

Thank you!

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, October 24, 2007 5:39 AM by Stev

I have a survey system which creates questions of one of six different types within a databound repeater.  Each question type is a user control, which is created dynamically using the ItemDataBound event.  All of this looks great.  I have the system navigating back and forth through the sets of questions and they display well.  The one problem which I cannot seem to solve is how to save the user's answer for each question, which could be a selection in a radiobuttonlist, a checkboxlist or even simply free text in a textbox.  When I iterate through the control collection in the repeater I can't see the user control that was created dynamically, even though I've made sure it has a unqiue ID.  Do you have any suggestions?

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, October 24, 2007 2:21 PM by InfinitiesLoop

Stev -- the fact you are creating the controls from ItemDataBound worries me a bit :) That's because unless you are databinding the repeater every request, the control isn't going to exist on a postback. ItemDataBound only occurs when you DataBind the repeater.

What you'd need to do is store in ViewState information about which controls were created during databinding, then from the ItemCreated event you lookup that value and add the appropriate control. That way they can continue to exist even if no databinding occurs on a postback.

One test you must always put your controls through is this -- put a button that does a postback on the page. Don't have it do anything, it just posts back. You should be able to click that button at any stage in your page progression and everything should remain the same. If things disappear, or revert their state, you have a problem.

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, October 24, 2007 2:26 PM by InfinitiesLoop

Jenny -- Just create a control that does nothing except load the user control dynamically.

<abc:MyPlaceHolder  runat="Server" />

Where MyPlaceHolder overrides OnInit and loads the user control into its control collection, nothing more. It's just a level of indirection so you can still approach the case where you have a user control nested within itself.

# re: TRULY Understanding Dynamic Controls (Part 4)

Monday, October 29, 2007 11:25 AM by rodchar

Thanks for the articles and especially for sticking around for all the comments so far (please don't stop now :)

I have a question if i may.

i think i fall in the category of your 2nd example:

"Example: You don't know "what" controls should be rendered at design time, or you want to avoid loading controls you don't need because of performance or because there are too many possibilities."

But now i don't know if i need to use dynamic controls or templates or ??.

Here's my dilemma: I have about 10 tables in my sql 2005 db. they all are related to one master table which is the employee master table. And there's a possibility more tables will be added later.

what i'm trying to do is be proactive and build a general routine kinda like the formview (and maybe i need to use the formview programmatically i don't know yet.)

thanks,

rodchar

# re: TRULY Understanding Dynamic Controls (Part 4)

Monday, October 29, 2007 2:14 PM by InfinitiesLoop

rodchar -- whether you use the FormView or not depends on whether you want some of the features it provides. Either way, you'd be building controls into a hiearchy that meets the needs of the table in question. For each, you'd be hooking into its DataBinding event, and from the handler you'd be assigning to the control the data from the bound data. You just need to ensure that you create these controls every request, such as by saving the selected table (its name or id, NOT the table itself!) into viewstate then from OnLoad or OnLoadViewState you look for that entry. It wouldn't be that different from the example in this article.

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, October 31, 2007 9:34 AM by Digamber Rana

Hi InfinitiesLoop,

Firstly thank you for such a great article, i have read lot of article in these issue but this was the best. You have given very thorow explanation of the topics.

But still i am confused while creating one of the logic for my project, could you please tell me the reason, i am sending you a small demo code, where i am providing a simple functionality to provide the language known option for user according to their choice, for which i have provided a add more button, when that clicks and new textbox appers in the form, i managed to restore its view state but when i again click the add more button, the text box is added two times, i know i could add the invisible text boxes, but my problem is little bigger where i have to add a usercontrol with lot of control in it, it is just a simple demo, which if you could given me explanation i will be thankful.

public partial class DemoCommonControl : System.Web.UI.Page

{

   private bool IsButtonClicked = false;

   protected void Page_Load(object sender, EventArgs e)

   {

   }

   protected override void OnInit(EventArgs e)

   {

       base.OnInit(e);

   }

   protected override object SaveViewState()

   {

       return base.SaveViewState();

   }

   protected override void LoadViewState(object savedState)

   {

       base.LoadViewState(savedState);

       CreateTextBox();

   }

   protected void Button1_Click(object sender, EventArgs e)

   {

       if (ViewState["counter"] == null)

           ViewState["counter"] = 0;

       else

           ViewState["counter"] = Convert.ToInt32(ViewState["counter"]) + 1;

       IsButtonClicked = true;

       CreateTextBox();

   }

   private void CreateTextBox()

   {

       int x = Convert.ToInt32(ViewState["counter"]);

       TextBox txtName;

       if (x == 0)

       {

           for (int ctr = 0; ctr <= x; ctr++)

           {

               txtName = new TextBox();

               PlaceHolder1.Controls.Add(txtName);

           }

       }

       else

       {

           for (int ctr = 0; ctr <= x - 1; ctr++)

           {

               txtName = new TextBox();

               PlaceHolder1.Controls.Add(txtName);

           }

       }

   }

}

Thanks,

Digamber

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, October 31, 2007 1:47 PM by InfinitiesLoop

Digamber  -- your CreateTextBox method is going to create textboxes based on your ViewState counter each time it is called. But you are calling it twice -- once from LoadViewState and once from Button Clicked. Say the counter is 2. You call CreateTextBox. Then the button click event is raised and you change the counter to 3. Then you call CreateTextBox again, which creates the same number of textboxes as before plus 1 more.

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, October 31, 2007 1:52 PM by Fredrik

Hi, and thanks for a great series of articles.

What if the control you load in LoadUserControl depends on ControlState (maybe in addition to ViewState)?

LoadControlState occurs before LoadViewState - doesn't that mean that your controls need to have been created before LoadControlState? And adding controls from within LoadControlState is not possible - you get a "Collection was modified after the enumerator was instantiated".

Thanks!

/Fredrik

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, October 31, 2007 2:03 PM by InfinitiesLoop

Fredrik -- I'd try to stick to thinking of Control State as ViewState. Its really the same thing, just segregated out. The fact the event occurs whenever doesn't matter, controls added later still go through the essential event sequence.

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, October 31, 2007 6:17 PM by Fredrik

But if I add controls after LoadControlState, will these controls have their control state loaded? I thought that if the control is not in the hierarchy at the time when LoadControlState occurs, the control state would be lost.

If I add controls in LoadViewState (occuring after LoadControlState), how can these controls ever receive their control state?

Or am I missing something here?

Thanks!

/Fredrik

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, October 31, 2007 6:21 PM by InfinitiesLoop

Fredrik -- when a control is added to the tree, it plays 'catch up' with the event sequence. If you add a control dynamically from Load, for example, which is after LoadViewState, it will still load its viewstate.

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, November 02, 2007 10:53 AM by Rodney

Hello InfinitiesLoop (Newman, if your a Seinfeld fan),

Regarding the following quote:

"At the time the CheckChanged event fires, we will have already loaded a user control -- whichever one was active previously. So we have to remove it before adding the new one. That is why we call Controls.Clear() on the placeholder."

I have adapted your CheckChanged example and depending on which radio button is selected i retrieve a DataTable from the database and manually display the fields in an html table.

my question is when CheckChanged is fired, say it was opt1 and now it's opt2, will opt1's data routine run as well, then clear the controls, then run opt2's data routine?

thanks,

rodchar

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, November 02, 2007 1:58 PM by InfinitiesLoop

Helly Jerry -- err, Rodney,

Yes.

Does that answer your question? :)

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, November 02, 2007 2:19 PM by Rodney

Do you think this is ok to do? to go out to database on each postback twice?

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, November 02, 2007 2:30 PM by InfinitiesLoop

It's only twice on postbacks in which the radio button selection has changed.

I think that's better than storing data in viewstate and rebuilding the controls based on that. If you're really concerned about perf/db contention then you can cache the data in asp.net cache pretty easily, and even use a SqlCacheDependency to make it automatically invalidate when the database changes. It doesn't get much better than that.

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, November 02, 2007 2:35 PM by Rodney

Great!! Thank you for your article and follow-ups. This has been educational.

Rod.

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, November 06, 2007 9:11 PM by Rob

Thanks for the articles! I thought after much goggling I might find the answer here. I'm much wiser now, but I am still searching for a solution to a problem.

I can send/post code if needed, but first let me just say that there appears to be a few people like me out there with this problem (and without a resolution -- and the code involved is rather lengthy): I am creating dynamic user controls, each containing one particular type of input field (dropdownlist, radio list, text field, checkboxes, etc), all driven by field meta data stored in a database.

I can successfully process all field types I'm using, except for a dropdownlist -- I cannot fetch the SelectedValue from the dropdownlist (within a user control) after the user clicks save.  

I have worked hard at understanding the points at which I should be creating and repopulating the control. This article and another led me to start overriding LoadViewState and recreating and repopulating the list after a call to base.LoadViewState. It simply doesn't seem to work... The frameworks via Viewstate just doesn't seem to be able to set the selectvalue of the dropdownlist; it's empty, even though via a trace I can see the the value was posted back correctly.

I am fairly certain that I am creating my controls in the same order, etc., etc., etc.  Any thoughts are appreciated!

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, November 07, 2007 8:18 AM by Rob

Never mind the above; I believe I have it...  I just listened to what I was saying and decided that I might not be recreating the option list in time, and sure enough...

Looking forward to part 5!

# re: TRULY Understanding Dynamic Controls (Part 4)

Thursday, November 08, 2007 2:03 PM by Rodney

Hi again InfinitiesLoop and all,

The following is a snippet from your Part4SampleCode:

  protected override void OnLoad(EventArgs e) {

       if (!Page.IsPostBack) {

           // no viewstate on initial request, load the default control

           ViewState["state"] = opt1.Checked ? 1 : 2;

           LoadUserControl();

       }

       base.OnLoad(e);

   }

   protected override void LoadViewState(object savedState) {

       base.LoadViewState(savedState);

         LoadUserControl();

   }

   private void CheckChanged(object sender, EventArgs args) {

       ViewState["state"] = opt1.Checked ? 1 : 2;

       ph.Controls.Clear();

       LoadUserControl();

   }

public property DataTable dt;

   public void LoadUserControl() {

        int state = (int)ViewState["state"];

       if (state == 1) {

           dt = LoadDataTable1();

       }

       else {

           dt = LoadDataTable2();        }

   }

I'm trying to encapsulate this snippet inside another WebUserControl if possible. I ran into a wall with my implementation. I'm using a DataTable in the LoadUserControl (which i made public) and i would like to expose the datatable so that this new UserControl will accept a DataTable and use it from there. However, on the postback the datatable becomes null.

Any ideas? do you have a better way or suggestions? do you think this would be an unusual implementation?

thanks,

rodchar

# re: TRULY Understanding Dynamic Controls (Part 4)

Thursday, November 08, 2007 2:18 PM by InfinitiesLoop

Rod -- I'm not exactly sure what is going on based on your code snippet. The DataTable is assigned in LoadUserControl. When is it null and how?

# re: TRULY Understanding Dynamic Controls (Part 4)

Thursday, November 08, 2007 2:36 PM by Rodney

I'm sorry for the confusion. What I'd like to do is extract the LoadDataTable methods from the new UserControl (from your LoadUserControl procedure)  and let the code-behind do the LoadDataTable methods and just pass in the datatable.

so, when i try this any postback will cause the datatable to be null.

hope this is a little clearer.

rod.

# re: TRULY Understanding Dynamic Controls (Part 4)

Thursday, November 08, 2007 2:43 PM by InfinitiesLoop

Rod -- so I think what you are saying is that you'd just have a DataTable property, which is assigned by the page using this user control. And your problem is that it is null on a postback.

It's going to be null unless you assign a value to it. Remember posts represent an entirely new life for the page. Everything is reinstantiated from scratch. So the property won't have a value unless you assign it.

# re: TRULY Understanding Dynamic Controls (Part 4)

Thursday, November 08, 2007 3:07 PM by Rodney

In the override LoadViewState the DataTable property is null on postback. now, i'm assigning a value to the DataTable everytime inside the code-behind page_load and i notice when i debug it and cause a postback from the usercontrol the code-behind page_load doesn't even run. am i doing the assigning of the datatable in the wrong place?

# re: TRULY Understanding Dynamic Controls (Part 4)

Thursday, November 08, 2007 4:59 PM by InfinitiesLoop

Rod -- LoadViewState comes before Load. You'll have to either do it sooner like Init.

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, November 09, 2007 2:27 PM by Rodney

So instead of using viewstate["state"] i'm going to have to use session? because when i checked in OnInit viewstate wasn't available, which i guess makes sense since LoadViewState hasn't occurred yet. am i thinking right? and is this ok to do this way?

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, November 09, 2007 2:49 PM by InfinitiesLoop

Rod -- why would you use session? Apparently I really don't understand what you're trying to do. Why don't you send me some actual code privately?

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, November 09, 2007 9:53 PM by Rodney

Hi InfinitiesLoop,

Thanks once again for taking time out to follow-up on all our comments. This has TRULY been helpful and I appreciate it very much. Also, thank you for the long and short ways to handle my specific issues, I now feel I have better direction.

Rod.

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, November 14, 2007 11:42 AM by Rodney

Hey all,

So if i'm using dynamic controls and i'm hitting a database on every postback could i turn off viewstate for the individual controls that are being dynamically generated? could i disable it for the entire page except for the viewstate mentioned in the article's context?

thanks,

rodchar

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, November 14, 2007 7:45 PM by InfinitiesLoop

Rodney -- in general, yes. Sometimes even if you are providing data on every request you need viewstate enabled because of other features of the control, but you have to take that on a case by case basis, and even then there's usually a way to work around it.

# re: TRULY Understanding Dynamic Controls (Part 4)

Monday, November 19, 2007 5:54 PM by shauntck

Rodney, thanks for taking the time to help people. It is greatly appreciated by many of us!

I'm trying to do something simliar but a little different and can't seem to get it working right.

I've got a gridview that has a template field with a place holder in it. In the rowdatabound event I'm finding the placeholder control and adding a dynamic control to it based on a switch conditional.  Generates the control fine.

In the updating row event, I'm trying to store the user inputed data to the database. When I use findcontrol for the placeholder, it finds it. However, the dynamically added controls don't exist. How can I fix it? I'm fine with using a statically creating it (if it will work) and setting visible = false. However, i'm trying to save the results in a database, so I'm wondering if i'm going to have problems binding multiple controls to a single field? Here's some code in how i'm doing it now... perhaps you could give me some guidance in how to approach this better. Thank you for your help... it is much appreciated!

protected void EditableGrid_RowDataBound(object sender, GridViewRowEventArgs e)

   {

      if( e.Row.RowType == DataControlRowType.DataRow ) { // we are binding a "data" row, as opposed to a header or footer or empty row.

           GridView gv = sender as GridView;            

           // parse the data column, using a regex

           string answer_type = DataBinder.Eval( e.Row.DataItem, "answer_type" ).ToString();            

PlaceHolder p = e.Row.FindControl( "answer_placeholder" ) as PlaceHolder;            

if( p != null ) {      // ...and we found the panel to hold auction links

               switch(answer_type) {

                   case "r":                      

                       break;

                   default:

                       //build radio buttons and mark them according to answer in db.

                       RadioButtonList rbl = new RadioButtonList() ;

                       rbl.ID = "rbl";

                       rbl.RepeatDirection = RepeatDirection.Horizontal;

                       string answer_int = DataBinder.Eval(e.Row.DataItem, "answer_int").ToString();

                       rbl.Items.Add(new ListItem("Yes", "1"));

                       rbl.Items.Add(new ListItem("No", "0"));

                       if (answer_int != "")

                       {

                           rbl.Items.FindByValue(answer_int.Trim()).Selected = true;

                       }

                       p.Controls.Add(rbl);

                       break;

               }  

}

}

protected void EditableGrid_RowUpdating(object sender, GridViewUpdateEventArgs e)

   {        

       GridView gv = sender as GridView;

       if (gv.Rows[e.RowIndex].RowType == DataControlRowType.DataRow)

       {

           PlaceHolder p = gv.Rows[e.RowIndex].FindControl("answer_placeholder") as PlaceHolder;            

           RadioButtonList rbl = p.FindControl("rbl") as RadioButtonList;

           if (rbl != null)  //HERE - RETURNS NULL!!!

           {

               if (rbl.SelectedIndex > -1)

               {

                   e.NewValues["answer_int"] = rbl.Items[rbl.SelectedIndex].Value.ToString();

               }

           }

       }

   }

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, November 21, 2007 4:09 PM by BriaN

Hi there,

Excellent article - very informative.

I've been waiting for part 5 to ask this, but I'm out of time.  I'm doing everything possible to make things difficult and complicated in an application (not without reason, of course, but still...)

I am loading several custom controls into user control  that is dynamically loaded into an update panel.  I got it working just fine like that, then I dropped the whole kit 'n kaboodle into another user control that calls DataBind() from the PreRender for it's own reasons.  Suddenly ONE of the controls (which renders 3 dropdown lists for selecting hour/minute/second) decides that the SelectedValue is illegal 'cause it's not in the (hour) list.  Other dropdowns built on the fly have been processed already, and more are still to follow.

The debugger catches here:

       public override void DataBind() {

           this.EnsureChildControls();// the dropdowns are added by a subclass in an overidden CreateChildControls method

           base.DataBind(); // error is thrown here - the base class is Panel at this point.

           setInputFromProperty();// takes the generic Text property and sets the rendered conrol accordingly -- e.g. a DateTime.ToString() gets converted into SelectedValues for Hour/Minute/Second dropdowns

       }

the top of the stack trace is:

  at System.Web.UI.WebControls.ListControl.PerformDataBinding(IEnumerable dataSource)

  at System.Web.UI.WebControls.ListControl.OnDataBinding(EventArgs e)

  at System.Web.UI.WebControls.ListControl.PerformSelect()

The debugger shows good values for all the dropdown lists and all the SelectedValue properties even after it has halted for the exception.  These were set by the DataBind() method in the user control container that's part of this subsystem.  The entire subsystem worked in two user controls that didn't DataBind() in the PreRender.

I'm completely mystified.  Before I swamp you with massive code, or put a lot of work into recreating the problem in a simplified version I could post here, is there something you could tell me that might help me find a solution?  I have considered simply not supporting DataBind() in the PreRender - i.e. ignoring the problem and altering the several controls that need this subsystem so they don't do that anymore - but that seems extremely sloppy, and like I might be sweeping under the rug a real problem I should be addressing.

Is DataBind() in the PreRender a dumb thing to do in the first place?

If you can't get back to me before I need to have this fixed, I'll post to tell you what happened.

Thanks,

BriaN

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, November 21, 2007 6:27 PM by BriaN

Hmm...

I fixed it, but I have no clue how.  Maybe I just had a bad binary someplace...  If I find out what went wrong I'll post, but it goes into the unsolved mystery pile for now.

Thanks for this blog, anyway, it rocks!

BriaN

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, November 21, 2007 7:20 PM by InfinitiesLoop

BriaN -- glad you fixed it, no help from me! I don't know what was wrong without really getting into it, but perhaps these tidbits of info will help you.

1. Databinding from PreRender is fine -- in fact thats when binding occurs for controls hooked up declaratively to a datasource control.

2. DataBind is recursive -- it databinds the control it is called on and all of its child controls.

3. DropDownList will throw that error if you set the SelectedValue before binding, and then bind to it a list that doesn't contain that value. Binding without setting the datasource first would fall under this too.

4. PreRender is top-down... so when you bind from PreRender from parent, it is before PreRender in child.

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, November 23, 2007 5:48 PM by BriaN

Thanks for replying so fast, and thanks again for the great article.

I haven't fixed it, I had commented out the DataBind() statement in the parent - but just as was going to test it an unrelated database problem happened and I simply forgot I had made the change...

If I don't call base.DataBind() in the base class of my custom control (which extends panel), I don't get the error, but then I can't set the SelectedValue of the dropdown properly because it's passed in a databinding expression in the markup (as the Text property), which is kind of the whole point of the custom control - hiding the internal differences between different kinds of input controls, heavy with client-side code, behind a uniform markup syntax.

I can't figure this out, and I need to find the answer in under 48 hours or punt and just disallow databinding in the prerender.  Is there a chance of you being able to help me in that time frame?

Thanks again,

BriaN

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, November 23, 2007 6:55 PM by BriaN

PHEW!!!

I found it, it had nothing directly to do with any of this, but was instead caused by expecting a Items.Add(String.Format("{0:00}",i) to add an item with text and value both of just a regular 2 character string with 2 digit numbers - instead, in the PreRender, the formatting stuff was still in the values, so the SelectedValue I set before of "00" was invalid.

The funky thing is, if I only ran DataBind once, I could treat them as "00", "01", etc. to no ill effect.  It was only going back to DataBind() without a round-trip to the browser that broke it... very, very odd.

I now add the items this way instead:

Items.Add(new ListItem(String.Format("{0:00}", i),i.ToString()))

and it seems to be working everywhere.

Can't decide whether to feel smart for finding it or dumb for doing it in the first place now...

I really appreciate having had this public forum to force me to be more rigorous in defining my problem, it helped a lot.

Thanks, and I can't wait for part 5,

BriaN

# re: TRULY Understanding Dynamic Controls (Part 4)

Sunday, November 25, 2007 12:12 PM by Alexander Sher

Hi Dave!

Few days ago I've found a custom control, where child contols were created and added to Controls collection in constructor. Certainly, that isn't good because Controls property and AddedControl method are virtual. But - is that the only reason why we must not add child controls in constructor?

# re: TRULY Understanding Dynamic Controls (Part 4)

Sunday, November 25, 2007 7:43 PM by InfinitiesLoop

Alexander -- I'd at least create them from CreateChildControls and call EnsureChildControls from the constructor. There's no technical reason why you should avoid doing it that way though, other than the virtual wierdness you are referring to.

# re: TRULY Understanding Dynamic Controls (Part 4)

Thursday, November 29, 2007 5:30 AM by Neil

Hi

Just stumbled upon your blog yesterday, looking for information on dynamically creating controls. Some very excellent posts!

I realised that what I was doing was better done with a repeater and a template. So far so good.

I'm creating DropDownLists dynamically based on the user's choice in a first DDL. This DDL is static and the selected index determines the DataSource which determines the number of dynamically created DDLs.

The problem occurs because DropDownList1_SelectedIndexChanged runs after OnInit/OnLoad. If I understand it correctly, the new selected index of the first DDL is not set until DropDownList1_SelectedIndexChanged is invoked, which means my DataSource for the repeater is not setup until after OnInit has run.

Is the only solution to check in OnInit whether the DataSource is ready, skip setting up the DDLs and then call the method to set up the DDLs from DropDownList1_SelectedIndexChanged?

Cheers and thanks for a great article series.

Neil

# re: TRULY Understanding Dynamic Controls (Part 4)

Thursday, November 29, 2007 1:02 PM by InfinitiesLoop

Neil -- the selected index should be available in OnLoad (before the event).

Try this -- always create the child DDLs from OnLoad, based on the SelectedIndex of the first DDL (forget about SelectedIndexChanged, you don't need it). The child DDLs are within a repeater, which has ViewState disabled.

This means binding the repeater every request and therefore getting your data every request. As long as your data is relatively cheap to retrieve that should be better than using ViewState.

But if you'd rather ViewState then just binding the repeater from SelectedIndexChanged should do the trick. You didn't really say whether it was working, just that you seem not to like the order of things?

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, November 30, 2007 5:12 AM by Neil

Thanks for your reply. I see your points and it's working now. I realize I was doing a bit of unnecessary extra work.

My only concern now is that the dynamically created DDLs will be too difficult to setup when they are created in the Repeater. I have to run through each DDL and setup their DataSources. The DataSources also depend on the Selected Index of the first DDL. I'm guessing this will have to be setup the hard way on every postback anyways. So I don't know "how many" controls and I only sort of know "what" controls. I know it's DDLs, but not what to DataBind them to. I'm in doubt whether I should still use the Repeater...

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, November 30, 2007 12:14 PM by InfinitiesLoop

I still think you should use a repeater. You know you can set the DataSources of the child DDLs declaratively?

<asp:Repeater>

<ItemTemplate>

   <asp:DropDownList DataSource="<%# GetDataSource(Container.DataItem) %>" />

GetDataSource would be a method you define in the code-behind (of at least 'protected' protection). The parameter will be the data item for the repeater at the time. If you can manage to squeeze it all in the <%# %> expression then you wouldn't even need the extra function.

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, December 05, 2007 5:55 PM by Sla

"Until next time.... part 5 will come much sooner than part 4 did, I promise." (c)

So where it is? ;)

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, December 05, 2007 9:40 PM by InfinitiesLoop

It's coming it's coming! I still have time to live up to that promise, I think. I've been working on something else lately,

www.infinity88.com/.../sky.html

# re: TRULY Understanding Dynamic Controls (Part 4)

Monday, December 17, 2007 12:05 AM by Bob

Excellent articles!  I have been significanty struggling trying to get a RadioButtonList to work within a GridView.  I downloaded your example and unfortunately that does not help me much.  Here is my situation:

I am building a online survey and looking to populate a GridView with a list of questions.  In the ItemTemplate, I am trying to pull in the list of Choices/Responses into a RadioButtonList.  I need to obviously dynamically create these because all this is driven by a DB backend and the total list of questions as well as each set of Choices/Responses can vary.  So questions typically have 2 to x number of Choices/Responses.  I have paging setup on the GridView (set to 10 for now) to pull in the questions.  I want to scroll through the GridView and extract the set of Choices/Responses from a separate DB table and display to the user.  Obviously viewstate is an issue here as well.  I have tried to follow your article(s) on how to accomplish and have been unsuccessful.  

Can you please provide an example (hopefully in VB) that addresses a RadioButtonList embedded in a GridView please?

Much appreciate that!!

Thanks

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, January 25, 2008 12:07 PM by mariner

The problem (wrong file is deleted) can also be avoided by simply deleting the file in event handler and reloading the page:

Response.Redirect(Request.Path);

Of course no message for the user can be displayed (at least without effort).

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, January 25, 2008 2:32 PM by Anna

I just wanted to say thank you. Not only is the article excellent, but your responses to the questions throughout the series clear up the issues even more.

Again. thanks!

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, January 29, 2008 3:44 PM by Rodney

Hey Dave,

I've tried to use your Part4SampleCode as a template for my page and I'm having a small problem with the way I'm trying to do things:

Posted Code Here:

www.wilcob.com/.../showpaste.aspx

I'm probably missing some concepts here so please bear with me. I am trying to create a dynamic gridview. Try to imagine 2 tabs on a page. I click the second tab the correct GridView loads fine. However, when I click the Select button for  row nothing happens but when I select it again the row is then selected.

any ideas?

thanks,

rod.

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, January 29, 2008 4:45 PM by InfinitiesLoop

Rodney -- try giving the GridView a specific ID, preventing it from assuming an automatically generated ID a 2nd time when the tab changes.

Also -- you don't have to do everything so dynamic. Much of what you have in that dynamic code is always the same. Consider declaring the grid and everything about it that is always the same. Then create everything else dynamicaly, like your fields, etc. Might side-step the whole issue.... :)

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, January 29, 2008 8:18 PM by Rodney

Thanks Dave as usual.

rod.

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, January 30, 2008 10:11 AM by Rodney

Dave,

Could you see this semi-Dynamic GridView working as a UserControl like in the Part4SampleCode?

In the UserControl Page_Load I would:

1. Access any db table based on a condition

2. Build the template columns from db fields

3. Databind

could this be a possible workflow?

thanks,

rod.

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, January 30, 2008 1:05 PM by InfinitiesLoop

Rodney -- sure, in general creating a user control isn't any different than creating a page, if you're making it a self-contained system that doesn't require any data or method calls from the page its hosted on. If you do need to give the control data from the page, such as your tabindex or something, then make it a property on the control and allow the page to call databind when it wants the data reconstructed.

uc1.SomeInformation = info;

uc1.DataBind();

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, January 30, 2008 5:22 PM by Rodney

Dave,

Ok, I declared the gridview and the Select is working for the GridView. When i select either RadioButtons it loads a GridView based on a different datasource. i can select a row press edit and it goes into edit mode fine.

my problem is what if i wanted to changed the selected index value of the GridView back to -1. How can i do that from the host? should i make my gridview object a public proberty inside my user control1 ?

Could you please take a look at my UserControl1.ascx and see if it looks ok so far. Again this host is exactly the same as Default4.aspx in your Part4SampleCode:

www.wilcob.com/.../showpaste.aspx

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, January 30, 2008 6:03 PM by InfinitiesLoop

Yeah you could make it a property, or you could have a method like SetSelectedIndex or ClearSelection, either way.

Keep in mind since you are rebinding the grid each time you may be wasting viewstate. Normally I'd just say you should disable viewstate on the gridview, but I don't think its selected and edit indexes work without it... not sure... try it. If not then you should only perform the databinding and template construction when the view changes, so you are at least utilizing the viewstate you are storing.

# re: TRULY Understanding Dynamic Controls (Part 4)

Thursday, January 31, 2008 3:12 PM by Rodney

Dave,

Surprise...I have a question...

www.wilcob.com/.../showpaste.aspx

I posted my current UserControl1 and was wondering how difficult would it be to add an inline Insert to my dynamic gridview?

would the new line have to occupy the footer row? how do you dynamically add a template column to a footer row?

thanks,

rodchar

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, February 05, 2008 8:57 PM by watson

Hi Dave,

Scenario: Multi-tab UI with different info/modules presented on each tab. Tabs are dynamically generated depending on user action (e.g., selecting menu command which exists outside of any tab or clicking on an edit link in a list from a generated tab). My initial approach was to create a user control to encapsulate each info/module. On user action, I create a tab, then Page.LoadControl the associated user control to embed it within the associated pageview of the tab. The problem is that the loaded control disappears upon postback. I can re-load the user controls on postback but I have to maintain the state of the user controls. For example, think of a data entry module that is loaded onto one of the tabs. User enters information but keeps the tab "open". User then selects a menu command which posts back and launches a new module in a new tab (which is set as the currently active tab). User switches back to the previous data entry tab. This tab should maintain any info that was previously entered by the user. Any thoughts?

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, February 06, 2008 1:50 AM by InfinitiesLoop

watson -- as long as you are loading the 'module' and putting it in the control tree, it doesnt matter whether it is visible or not, it will maintain its state perfectly. So as long as a tab is active in the sense that the user may not be done with what is on it, you should be loading the control that belongs to it. Its just that the 'active' tab is the only one whose module is actually visible.

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, February 08, 2008 9:15 AM by Rodney

Dave,

I Truly apologize for the bombardment of questions several days ago. I do appreciate your replies and value them.

The last question i figured out that i'm just going to add a blank record to the db and the call it back up in edit. good enuff for now. i'll come up with something.

Could you please spot check something for me. When i go into edit mode on the gridview there's a custom control i built as a date picker. i've greatly simplified the control for debugging purposes. The problem i'm having is i can't toggle the calendar on and off with my button. i'm not sure why it's not persisting as it should. i understand if i've reached my limit of posts.

Given:

Default4.aspx of Part4SampleCode (untouched)

UserControl1.ascx

www.wilcob.com/.../showpaste.aspx

CustomControl

www.wilcob.com/.../showpaste.aspx

thanks,

rodchar

p.s. sorry :)

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, February 12, 2008 2:00 PM by InfinitiesLoop

Rodney -- I assume ViewState isn't turned off? When you say it won't persist what do you mean exactly... does it do anything or is clicking the button just causing a postback with no observable effect?

Don't apologize I do enjoy helping people... I only regret that I'm not more responsive. I will eventually answer most inquiries, even if its weeks later.

# re: TRULY Understanding Dynamic Controls (Part 4)

Saturday, February 16, 2008 12:07 PM by Rodney

Thanks Dave for your patience and efforts...

When I click on the button to show the calendar that part will work. However, it's when i click the button again to hide the calendar is what's wrong. The calendar doesn't disappear from that point on.

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, February 20, 2008 10:31 AM by Andrej

Great post(s). Long time ago passed from Part 4...when we can expect Part 5 which covers server controls?

# re: TRULY Understanding Dynamic Controls (Part 4)

Monday, February 25, 2008 11:58 PM by Andy

Dave,

Thanks for your excellent posts on the Viewstate. Keep up the good work. I would love to see more indepth articles like this from you and your team. Thanks again.

I have a few basic things I am still not clear on. What has been most confusing to me about asp.net in general is the order of events in the lifecycle. I understand from reading your articles that when a control gets added to the control tree, it automatically plays catchup to the point right before the current event in the lifecycle (pretty cool stuff IMO). I am still confused on overriding methods in general.

1) There is the virtual method Render(HtmlTextWriter writer) which can be overriden by classes that inherit the Control class. When we do that, are we required to call the base render method (is there stuff that the parent method is doing that is required in the render process)? If so where do we place our custom code? This question does not only apply to only the Render method but to any virtual method.

protected override void Render(HtmlTextWriter writer)

{

   //Custom code here?

   base.Render(writer);//Is this required?

   //or here?

}

2) We have the Init event and the OnInit method. Should we be handling the Init event or overriding the Onint method? Why?

public _Default() {

  this.Init += new EventHandler(_Default_Init);

}

void _Default_Init(object sender, EventArgs e)

{

  //Cusom Code

}

I guess the answer is overriding the Oninit method. If so, should we include our custom code before or after calling the base OnInit?

protected override void OnInit(EventArgs e)

{

  //Custom code here?

  base.OnInit(e); //Required for base to inform other subscribers.

  //or here?

}

3) Controls have properties. It is recommended that anytime you get/set a property, you should call EnsureChildControls to guarantee that the control tree is created. EnsureChildControls internally calls CreateChildControls. Since properties can be set/get anytime during the lifecycle, does this mean I may or may not have ViewState available in CreateChildControls? Example: I set a property in the Init stage.

4) When overriding CreateChildControls(), am I always required to call Controls.Clear() first? Is this because a post back could change something causing the control tree to be rebuilt? Should I always call base.CreateChildControls()?

5) Controls are initialized during the Init stage. This loads the default properties of the controls. How does it do this (I failed to find a method where this is done using Reflector)?  

I would really really appreciate it if you could answer these questions.

Thanks in advance!

Andy

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, February 26, 2008 9:52 PM by tay

Thank you for your excellent post about viewstate. I have questions below.

I have textbox inside the repeater and how can i maintain the data key in inside the textbox by users after postback?

Thank you again.

# re: TRULY Understanding Dynamic Controls (Part 4)

Monday, March 03, 2008 10:30 AM by Stephan

Thanks for writing these articles and all the answers you provide us with, still I  

got don't get my example to work. ;)

I have a repeater containing a User Control. To keep it simple this user controls only contains a textbox. I populate the repeater using a Arraylist containing 5 items (Text 1 to Text 5). The user control exposes a public property called myTest. In the ItemCreated event of the repeater I create an instance of the UC and set the public property. This all works fine. But, and I think you already know what I'm going to ask, when I click a button on the page to post-back. The textboxes are all rendered again, but empty. To me it seems the viewstate of the UC is not maintained. So the repeater keeps track of the number of controls rendered, but the controls itself don't have the data anymore. That is one question. I also want to add a this UC dynamically when I add one of the buttons on the page. But it is possible that data already in the repeater, has changed. How to accomplice adding a new item and keep the changed values of the other items. In the PDF document I read that you suggest first retrieve the changed values put them in a new datasource then rebind again. (Item 4585429 by Rob).

I hope  you understand what I want to achieve, any help is appreciate.

Thx

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, March 07, 2008 6:11 PM by InfinitiesLoop

Stephan --

ItemCreated is called on every postback, even when you aren't databinding again. So on a postback you'll end up creating the UC and then setting its text to something not from the data, since the data isn't available anymore.

What you want to do is either just put the UC in the ItemTemplate statically, or break it up and use the ItemDataBound event. You create the UC from ItemCreated, but you only assign its public property from the ItemDataBound event (alternatively you could hook into the DataBinding event from ItemCreated, and set it from the handler).

As for your second question... yes I recommend you retrieve all the data and rebind!

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, March 07, 2008 6:15 PM by InfinitiesLoop

Andy --

These are all excellent questions. So excellent in fact I plan on a blog entry to answer some of them, as soon as I can...

To give you a quick answer to #5... you can't find it, because that's not where it happens. The framework builds methods on the fly when it compiles the markup, and they are what assign the property values, which is even before OnInit.

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, March 14, 2008 4:14 PM by Michael Holberton

Hi Dave,

I am using a repeater to allow people to enter their names etc. for reservations.

The repeater currently uses a simple array to control the number of rows built by the repeater and the size of the array is supplied by the User when specifying the number of people requiring ticket reservations.

The entered data will be stored in the database when a User clicks on a button to finalise the reservation process.

Now, if someone wants to change the number of people or delete or add a row before clicking on the final button, how do I save previously entered data and bind it when the repeater is rebuilt during postback?

Should I base the Array on a Structure and store the Array in a SessionState variable? Can a Data Table or Dataset object be created in memory and stored in a SessionState variable and bound to a Repeater?

What's the most efficient way to achieve this?

Thanks,

Michael Holberton

Hospedaje Los Jardines & Sacred Valley Mountain Bike Tours

Cusco Database Development and Cycling Services E.I.R.L.

http://www.machawasi.com/

http://machawasi.blogspot.com/

databaseservices.blogspot.com

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, March 14, 2008 5:31 PM by InfinitiesLoop

Michael -- I suggest that when one of these operations are needed (adding, deleting, etc) and you want to save data already entered by the user, you first 'save' the data -- but not the database. You probably have a method already that saves the data to the database by basically copying it directly from the form to the database. Rather than that, refactor it into two parts. The 1st part creates an array of data items representing all the data (using a custom type or whatever), the 2nd part saves it to the database FROM that array of custom types. This provides a clean separation of UI and business logic. And it also gives you for free a way to save the existing data TEMPORARILY. So when the user wants to add/delete, etc, you first save it into the array, using the 1st part of the two methods I described, then you manipulate the array, then re-bind it to the repeater. No need to save anything in session.

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, March 14, 2008 9:39 PM by Michael Holberton

Thanks for the reply Dave!

I'll try using an array based on a Structure as I am thinking that should help to self document the code instead of having to review the code each time to determine which field is associated with which array item.

Michael

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, April 09, 2008 8:01 AM by ezrider

I sent a message to you just the other day, but I'll make this one public.

I really only want to do a simple thing:)

I have and update panel with a placeholder inside. I Have a button outside the UdPanel that acts as an AsyncPBack Trigger.

I have designed a UserControl (a <tr>) which contains a few DDLs and Text boxes. It also contains a button called "Remove".

The idea is that clicking "add" adds rows  dynamically and obviously, Remove, removes the row from the PlaceHolder.  

Adding works to an extent but seems to muck up the Control hierarcy. Removing also does this. Data appears in the wrong controls etc. A control exists on the page when it shouldn't etc.

Added to this, I wanted to have OnSelectedIndexChanged events working on the DDLs in the UserControl.  

Its quite annoying. I have done this with Javascript but I wanted to use server controls to simplify the posting of data plus I wanted partial postbacks to make it quicker.

Anyone have any ideas?

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, April 09, 2008 1:38 PM by InfinitiesLoop

ezrider -- they key is to make sure the controls are added each request. For example if you click add, and then do an async postback via some other button, the control should remain there. It won't be if you aren't adding it, despite the add button not being clicked the 2nd time. But that's not the only thing you would need to worry about. You are probably getting 'messed up' data/hiearchy because you're getting a shifting-id problem, as explained in this article. If you have 3 rows and you remove the 2nd one, the 3rd row is going to have its IDs shift. You can solve that using the techniques I outlined. The simplest way for you may be to just make removed rows invisible rather than actually remove them.

Since you are adding the same control each time, it would be much cleaner if you just used a repeater:

<asp:repeater runat="server" id="rpt1">

  <itemtemplate>

     <abc:usercontrol runat="server" />

  </itemtemplate>

</asp:repeater>

You would databind to this repeater a collection of data items that could contain data required by the user controls (or just a blank array if you only care about the number of them). The tricky part is that you must rebind the repeater whenever you add or remove an item, so you must be able to re-create the collection from the repeater's items, in order to preserve the data the user has already put into it so far.

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, April 11, 2008 6:06 AM by ezrider

Hey Thanks mate!

My "add row" functions were working fine. It was really only the shifting Ids problem with the Remove that was giving me a head ache.    

I'm not sure how I would solve that problem despite the solutions in your article( I had problems with NamingContainer and fixed id names).

I had written a custom object that has the same structure as the control, allowing the values in each row(filter clauses) to be stored in the DB and then recreated when a query was loaded for editing.

Since I already have a Collection of objects maintaining the row values, the repeater approach would be best. It certainly looks cleaner!

I really appreciate your helping me. Great Blag too.

# TRULY Understanding Dynamic Controls by Example

Wednesday, April 23, 2008 3:54 AM by Infinities Loop

Many of the comments I've received in the various dynamic controls entries I've written have been questions

# re: TRULY Understanding Dynamic Controls (Part 4)

Monday, May 05, 2008 5:54 AM by Fire

hi.

I was just reading through ur articles. It all good, but I think I need the #5 for my problem.

I'm trying to create dynamic "multi row edit" gridview with template. Why do I want it ? cause I would use the gridview as main source for lookup data for user.

well, of course most of it I knew what columns that I would need, but I would need the same grid view to look up different data (which of course had lots of different columns on size and format).

And i was thinking to even take it further to make some of the columns editable (and it is multirow in a sense).

I have successfully created it to be loadable, and editable.

what I need now, I wanted to get the data after the user edit it, while the controls would always gone after the postback.

what should I do to retrieve the data from the dynamically made columns on gridview ?

# re: TRULY Understanding Dynamic Controls (Part 4)

Monday, May 05, 2008 1:25 PM by InfinitiesLoop

Fire -- columns are a StateManagedCollection. That means if you dynamically add or remove a column, it will persist that way on its own without you having to redo that operation on a postback. So I'm not really following what your problem is. You say you want to get the data the user entered, but the controls aren't recreated? They should be created by the grid view automatically based on the columns and their templates. Perhaps you are rebinding it or something?

# re: TRULY Understanding Dynamic Controls (Part 4)

Monday, May 05, 2008 9:44 PM by Fire

I have solved it already.

So, it seems it was just my misunderstanding of asp.net state.

After reading ur viewstate article, and discussing with my friend, I have solved the problem.

So, dynamically created column template should always be recreated on gridview_init event (and the cool thing is their value persisted cause of the magic viewstate from the .net framework).

after, that, then I can access the data following the postback event because the controls had been recreated from the init event.

so, things are good for now.. Still perfect-ing my custom gridview though..

yeah, I know that u and some people here said (DONT USE CUSTOM CONTROL)...... :P

but this custom gridview would helped me alot on my projects cause I would definitely need it.

thx anyway for the great article :)

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, May 09, 2008 5:18 AM by Fire

hi again infinities.

I have another problems that I wanted to consult with u.

so, I tried to make a custom textbox to be used for my custom dynamic template gridview.

here's some code from the textbox :

   Public Enum TextBoxTypeEnum

       Text

       Money

   End Enum

   Public Property TextBoxType() As TextBoxTypeEnum

       Get

           Return ViewState("TextBoxType")

       End Get

       Set(ByVal Value As TextBoxTypeEnum)

           ViewState("TextBoxType") = Value

           GetDefaultFormatText()

       End Set

   End Property

   Public Property FormatText() As String

       Get

           Return ViewState("FormatText")

       End Get

       Set(ByVal Value As String)

           ViewState("FormatText") = Value

       End Set

   End Property

   Protected Overrides Sub OnLoad(ByVal e As EventArgs)

       Page.ClientScript.RegisterClientScriptResource(Me.GetType(), "AVPControls.AvpTextBox.js")

       If ViewState("TextBoxType") = TextBoxTypeEnum.Money Then

           Me.Text = String.Format("{0:" & FormatText & "}", CDbl(Me.Text))

       End If

       MyBase.OnLoad(e)

   End Sub

   Private Sub GetDefaultFormatText()

       If ViewState("TextBoxType") = TextBoxTypeEnum.Money Then

           If FormatText = "" Then FormatText = "#,##0.00"

           Me.Text = String.Format("{0:" & FormatText & "}", CDbl(Me.Text))

       ElseIf ViewState("TextBoxType") = TextBoxTypeEnum.Text Then

           FormatText = ""

       End If

   End Sub

The idea is just that if I choose type money from the textbox then it would convert my text into numeric format (#,##0.00) in this example

now the problems :

1. the getdefaultformattext made my textbox error because it seems that it cannot format my text on that function. So, I decided to play with it a bit and changed it to :

   Private Sub GetDefaultFormatText()

       If ViewState("TextBoxType") = TextBoxTypeEnum.Money Then

           If FormatText = "" Then FormatText = "#,##0.00"

           Me.Text = "123"

       ElseIf ViewState("TextBoxType") = TextBoxTypeEnum.Text Then

           FormatText = ""

       End If

   End Sub

now, my textbox rendered successfully, and when I tried to change the type into money, it then change the formattext property and my textbox text property. The problem is that the text property DID CHANGE but only on properties window, and it DIDN'T CHANGE the text on my textbox text in design view (on the design visible form).

2. I gave up trying the above problem (if I just deleted that text changer on getdefaultformattext function, the textbox did run successfully and change the text on runtime, so it's not really big deal, though I really want to know how to change the text on design time too). Now, the second problem is when I tried to use my custom textbox on my dynamic template gridview. Here, my numeric format wouldn't work. So, I thought maybe the onload didn't get called. So, I tried to add msgbox on onload method just to know the truth :

   Protected Overrides Sub OnLoad(ByVal e As EventArgs)

       Page.ClientScript.RegisterClientScriptResource(Me.GetType(), "AVPControls.AvpTextBox.js")

       If ViewState("TextBoxType") = TextBoxTypeEnum.Money Then

           Me.Text = String.Format("{0:" & FormatText & "}", CDbl(Me.Text))

   msgbox (me.text)

       End If

       MyBase.OnLoad(e)

   End Sub

the msgbox return the RIGHT TEXT (that is the numeric formatted text) successfully. But the text on my textbox didn't change at all. So it seems te problem from number 1 occured again here. Could u suggest me how to fix it ?

thx

regards,

Fire.

# re: TRULY Understanding Dynamic Controls (Part 4)

Monday, May 12, 2008 5:12 AM by Fire

hi infinities,

no answer for the 1st question yet ?

as for the 2nd question, sorry, again, it's part of my fault of my (still) misunderstanding of viewstate ordering process.

thx

regards,

Fire.

# re: TRULY Understanding Dynamic Controls (Part 4)

Monday, May 12, 2008 12:59 PM by InfinitiesLoop

Fire -- the control is kind of confusing! The format text is set in so many different places. If you format the text once, then submit again, you're going to end up trying to format it twice. I'm not sure if the Parsing will work or not. Ideally you'd just output the formatted text from Render or AddAttributesToRender instead of actually CHANGING the text value. I wish I could help more than that at the moment but I gotta go! We might be having a baby today...

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, May 14, 2008 6:14 AM by dani

Hi,

Your articles are so in-depth and have really pulled through for me, great work.

Could I get some pointers on using OnLoad and OnInit, I've read your onload-vs-page-load-vs-load-event article.

My Server Control consists of a GridView which is built in the OnInit() method.  However the databinding for the gridview is called from the OnLoad() method which in turn calls a method RunQuery();

RunQuery() returns a DataTable which is used by a method named BindData() to bind the gridview but also create a Table using the DataTable column names for the headings and a TextBox control for each gridview column.

As RunQuery() will need to be called multiple times, it will return different sets of data, I'm clearing the Table and recreating the Table Header and Rows.  However this occurs on every postback due to the RunQuery method being called from OnLoad()

Sorry if this seems a bit incoherent, I've probably confused my self ;-)

Just read your comment about having a baby, all the best and take care.

Regards,

# re: TRULY Understanding Dynamic Controls (Part 4)

Monday, July 07, 2008 10:16 AM by Adam Rogas

I have a rather odd problem

I am loading an external (skin template) and applying it to my server control when I dynamicly load it onto my page.

( it's a plugin based site editor, I can't use static controls )

The wierd thing that is happening is that all of the controls will not persist thier state unless they are visible on the page, meaning if I have them in a multiview in my template file, only the visible controls persist thier values, from post back to post back event.

       protected override void OnInit(EventArgs e)

       {

           this.EnsureChildControls();

           base.OnInit(e);

       }

       protected override void CreateChildControls()

       {

           if (!_skinLoaded)

           {

               _skinLoaded = LoadDefaultThemedControl();

           }

           if (_skinLoaded)

               AttachChildControls();

        }

       protected virtual bool LoadDefaultThemedControl()

       {

           if (this.Page != null && this.DefaultSkinFileExists)

           {

               Control defaultSkin = this.LoadControl(this.DefaultSkinPath);

               defaultSkin.ID = "_";

               this.Controls.Add(defaultSkin);

               return true;

           }

           return false;

       }

In my server control class I am finding the controls and wireing them up prior to the host controls onInit so the order should not be screwing me up, additionally the controls do work when visible ???

I am not really sure, but I am sure it is something stupid I am doing :)

Any help would be greatly appreciated.

# re: TRULY Understanding Dynamic Controls (Part 4)

Monday, July 07, 2008 11:02 AM by Adam Rogas

You know after 5 hours of looking 5 minutes after I submited my question I figured out that I failed to specify the ID of my host control.  :-(

Well the good news is it works just like it should and I thought it would, the bad news is I wasted 5 hours :)

Oh well I am just glad I found it.

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, July 08, 2008 4:38 PM by SeanB

I've read these articles and you seem like the best person to ask for this.

I've got a web page that holds a user control, which is really a factory that might load up one of several different user controls inside it. The web page doesn't really know what kind of control is getting loaded, and it doesn't care. It just passes the data in to the factory/container control and trusts that guy to load up the right control for the content provided.

This all works great, and I've used it in multiple projects. But I've always just had the child controls hold literals. They never held any interactive controls like a textbox. And now, I need them to do that, and I've discovered something that doesn't work the way I expected.

I'll post the code below, but basically, here's what's happening:

The default page has a textbox, a button, and a factory control. You enter text into the textbox and hit the button.

What should happen is that the page reloads. Then the page takes the text from the textbox and hands it to the factory control. The factory control loads the child control, and passes that text to the child control to display. The child control then displays that text twice - once in a textbox and once in a literal.

If you set a breakpoint in the child control, you can even see that the textbox.text shows the correct value. BUT, when the page loads, only the literal will actually display the value. The textbox itself is blank. Why is it doing that?

Here's the code:

------------------------------------------------------------------------------------------------------------

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Dirtbox._Default" %>

<%@ Register src="~/FactoryControl.ascx" TagName="edit" TagPrefix="admin" %>

<html>

<body>

   <form id="form1" runat="server">

       <asp:TextBox ID="sourceText" runat="server"/>

       <asp:Button id="myButton" runat="server" Text="Update User Control"/>

       <admin:edit runat="server" id="containerControl"/><br />  

   </form>

</body>

</html>

------------------------------------------------------------------------------------------------------------

using System;

namespace Dirtbox

{

   public partial class _Default : System.Web.UI.Page

   {

       protected void Page_Load(object sender, EventArgs e)

       {

           containerControl.PutStuffIntoUserControl(sourceText.Text);

       }

   }

}

------------------------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------------------------

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="FactoryControl.ascx.cs" Inherits="Dirtbox.FactoryControl" %>

<asp:Panel ID="myPanel" runat="server"/>

------------------------------------------------------------------------------------------------------------

namespace Dirtbox

{

   public partial class FactoryControl : System.Web.UI.UserControl

   {

       public void PutStuffIntoUserControl(string useThis)

       {

           MyUserControl subControl = (MyUserControl)LoadControl("~/MyUserControl.ascx");

           subControl.myText = useThis;

           subControl.PopulateInteriorControl();

           myPanel.Controls.Add(subControl);

       }

   }

}

------------------------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------------------------

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="MyUserControl.ascx.cs" Inherits="Dirtbox.MyUserControl" EnableViewState="false" %>

<asp:TextBox ID="textWithinControl" runat="server"/><br />

<asp:Literal ID="literalWithinControl" runat="server"/>

------------------------------------------------------------------------------------------------------------

namespace Dirtbox

{

   public partial class MyUserControl : System.Web.UI.UserControl

   {

       public string myText;

       public void PopulateInteriorControl()

       {

           textWithinControl.Text = myText;

           literalWithinControl.Text = myText;

       }

   }

}

------------------------------------------------------------------------------------------------------------

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, July 08, 2008 8:39 PM by Ioana

Thank you for all the time you put into this, helping us out with things that we can't really find in any book. You should write one, by the way:). Looking forward to your next articles.

And congratulations for the baby!

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, July 09, 2008 2:33 AM by InfinitiesLoop

SeanB --

Here is why I think. When the page loads for the first time, you are loading the child control with empty text. On the postback, you load it with 'newtext' or whatever you typed in. You assign this value to the textbox.text property. The textbox did not exist in the control tree before page load, since that is when it is being dynamically loaded. Normally textboxes load their text before page load. It missed out. But the framework knows that many pages will be loading controls dynamically from page load and that they might have postback data to load -- so, there is a 2nd phase in which postback controls load postback data, AFTER load. The textbox then loads the postback data, which is what the textbox had after the 1st time the page loaded -- BLANK! So the value is then reset back to blank.

To avoid this you must either give each distinct possible user control a different id (which is stable and remains the same if the text in the textbox didnt change, but is different for each possibility), which results in each textbox getting a different unique id, or you must do this loading in a two step process: first, you always load the control you had last time, by utilizing a viewstate field to remember what the textbox value was; second, you respond to new data by handling the buttons click event, at which point you remove the old child control and insert the new one, this ensures the first one gets the last request's postback data, not the new one.

I suggest you take a look at my dynamic controls by example post. It doesnt directly solve your particular problem, but has some very similar things in it.

So the 'real' solution uses a technique that relies on viewstate, where you are always sure to loa

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, July 11, 2008 11:49 AM by Jon Marozick

Great series! It's answered alot of my questions about controls.

One thing I've discovered in my recent testing is that if you add a control dynamiclly to a part of the control tree (in OnInit) that is further down the line, it doesn't play catch-up. Which, if you think about it, makes sense since as InitRecursive() works through the tree it will eventually get to that newly added control.

The example I was working on had two content place holders on a master page. The first content place holder loaded several user controls onto the second content place holder control. I was expecting the dynamically loaded controls to init right away but they didn't.

Sorry if this is a restatement of something in this series but I don't think it was there.

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, July 11, 2008 3:33 PM by InfinitiesLoop

Jon -- yes, if you manage to add a control to one that hasn't init'd yet, the added control won't suddenly init either, but will eventually when the normal init phase reaches it. The 'catch up' feature only comes into play if the parent you are adding the control to has already been through a phase.

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, July 15, 2008 12:55 AM by Cristian Vintila

This is a great series. Helped me understand.

I have a requirement where i create controls on the fly based on database values. The controls are not a set type, there can be textboxes, checkboxlists etc. and i don't know how many there will be either.

I want to load the controls based on a db condition. Then, when i click on a button, i want to save the control values and then load a next set of controls depending on a db condition, kind of like a wizard. Right now i was loading them using a user control in the Page_Init of the control.

Seems to work but it's not working very well. When i click the button, to submit the control values, it re-renders the previous controls, saves the values and then loads the next set using the Controls.Add. However, if i go to view source for the page, it still shows the previous controls in the html view.

Do you have some good suggestions on how to make it work the best way. I'm not sure how to implement a repeater solution, though i'd like to if possible with this kind of scenario.

Thanks for your help and suggestions.

Cristian

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, July 15, 2008 1:09 AM by InfinitiesLoop

Cristian -- my article on understanding dynamic controls by example might help. It sounds like you have the right idea. You just need to remove the old control when moving on to the next one, with Controls.Remove or Controls.Clear. You also then should give the control you are loading an ID based on something short and concise in the db, such that each possible control will have a different ID but one that is always the same. This avoids the 'shifting id' problem I've talked about.

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, July 15, 2008 11:26 AM by Cristian Vintila

Hi, thanks for the quick reply. There seems to be an issue when using this type of functionality with Ajax. Turning off ajax makes everything work as expected but with ajax, my labels(Literals thanks to another one of your article saved me loads of time on css) don't render upon an async postback. The other controls that have id's such as textboxes render properly....well except validator controls which also don't seem to work. I tried giving them unique id's such as lblMyTitle + new Random().Next(100)  but didn't help. Any ideas.

In my page, i am removing the user control where the questions are created using the Controls.Clear -- controls is mapped back to a placeholder control. After Clear i'm calling a LoadControl to load back that user control which should now have the new set of controls.

Cristian

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, July 16, 2008 1:01 PM by InfinitiesLoop

Cristian -- I'd have to see code or a minimized version of  the code. Simply turning on ajax shouldnt cause problems like that if everything is done correctly. By 'turn off' I assume you mean setting EnablePartialRendering="false"? could you provide more details?

# re: TRULY Understanding Dynamic Controls (Part 4)

Thursday, July 17, 2008 9:00 AM by Jip

Hey man, these articles are very nice, however due to my very limited experience with .NET and developing webbased applications I cannot directly find a solution to the problem I'm having. Maybe you can help me a bit?

I posted the issue over here (code included):

forums.microsoft.com/.../ShowPost.aspx

It's kind of a story but it's not too difficult... You seem like someone who could push me in the right direction! It would be great if you could just take a look and share your first thoughts on the matter...

# re: TRULY Understanding Dynamic Controls (Part 4)

Monday, August 04, 2008 11:53 AM by rodchar

Hi Dave,

Man, you are still getting hits and I'm not surprised. Hey, I just wanted to run something by you and get your take on it.

I'm beginning to understand when you said that a lot of the times when people want dynamic controls the solution doesn't really require the degree of dynamic control one thinks (if that makes any sense).

anyways, i'm taking advantage of the ITemplate interface by creating my own class and implementing the interface. and my idea is that i load an xml file to determine the presentation of the fields. what do you think about that idea?

for example, as the dynamic template is being created, there's this xml file pre-loaded keyed by fieldname. And when it's that fields turn to go and look up the instructions in the xml file like whether it's a textbox in edit mode, if it needs a format string {0:d} for dates, if it needs javascript appended? stuff like that.

thanks,

rodchar

# re: TRULY Understanding Dynamic Controls (Part 4)

Monday, August 04, 2008 1:54 PM by InfinitiesLoop

rodchar -- sure, could be useful as a generic form generation control. But be careful that you aren't actually reinventing asp.net markup with your xml. In other words, why create custom XML, and a custom parser, when the output is simply a control tree -- why not let the xml be actual asp.net markup, and your 'parser' is simply LoadControl()? Remember you can load an ascx file and use it as an ITemplate with LoadTemplate.

# re: TRULY Understanding Dynamic Controls (Part 4)

Thursday, August 14, 2008 8:04 PM by CrazyTasty

dude!  that was good read.  

i've been struggling with loading a custom web control through an XML config file.

i've tried a ton of stuff, but just can't stuff to work right.  you clued me in on a few important points that i will attempt.

i'll let you know how it goes...if you're interested that is...i'm sure you get slammed with more questions than you care to talk about.

at any rate....

THANKS!!!!

-

cT

# re: TRULY Understanding Dynamic Controls (Part 4)

Friday, August 15, 2008 6:38 PM by some developer

thanks a lot

# re: TRULY Understanding Dynamic Controls (Part 4)

Monday, August 18, 2008 6:45 PM by CrazyTasty

Can I pick your brain for a minute?

I have a number of the same usercontrol(which consists of a checkbox and an image) that I need to load at runtime. I declare a default control(of the same usercontrol type) in the .aspx page initially because the default always needs to be there.

By default, the default control is checked and once all the other controls are loaded, if the user clicks one or more of the loaded user controls, the default control should uncheck, etc.....on the same note, if none of the dynamically loaded controls are checked, the default should check, etc...

I almost have this working but I can't quite get the functionality I've described above.  I suspect I have the event handlers wired up wrong or not implemented the correct way.

The reason I feel I have to do it like this, is that if we declare all of the control types in the .aspx page, we have to republish the site code...yuck! So, I have an XML file that describes the control and I load them dynamically....that way we can add or remove from the XML file as we please.

At the moment, I load all of the controls(including the default control) in a panel/placeholder.

Can you possibly shed some light for me and/or is there a better way to approach this?

Thanks a lot...I appreciate it!

-

cT

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, August 20, 2008 2:24 PM by InfinitiesLoop

CrazyTasty --- hilarious name, by the way. Its making me hungry every time I see one of your comments!

Anyway -- sounds like you're on the right track. I dont know exactly what is wrong without more detail. The way I imagine doing it is having a helper method like SetCheckedImage(Control) where the passed in control is the one that should be checked. The method would first enumerate all the loaded controls and set their checkboxes to false, and then finally set the passed-in control to true. If the passed in control was null then it sets the default control to checked.

Then, in the user control, whatever it is that causes a postback (checkbox autopostback?), calls that method passing itself when the checkbox changes to true, and null when it changes to false.

Theres probably a faster way to do it -- this will mean enumerating all the controls whenever anything happens, but if you only have a dozen or so of them I doubt its worth trying to optimize too much.

# re: TRULY Understanding Dynamic Controls (Part 4)

Thursday, August 21, 2008 12:40 AM by CrazyTasty

Thanks man. I will try a few things tomorrow when I go in and let you know.

-

cT

p.s. you remember the commercial product that had the tagline, Crazy Tasty!!! ...don't you?   :-)

# re: TRULY Understanding Dynamic Controls (Part 4)

Thursday, August 21, 2008 1:01 AM by InfinitiesLoop

Bloody vikings!

# re: TRULY Understanding Dynamic Controls (Part 4)

Thursday, August 28, 2008 6:20 AM by zee

Thanks for this article.Its an excellent article on Dynamic Controls.

But i have some trouble in working with dynamic controls.Here is the situation I have one dynamic Web user control named WebUserControl1.ascx.Its type name is same (i.e WebUserControl1) in the code behind file and its public , but when i want to access this type in default.aspx page then compiler gives error : "Type or namespace WebUserControl1 could not be found.are you missing an assembly reference?"

Why i am not getting this public type in default.aspx while both user control and default page are on the same level in web site and both have no namespace(means no namespace is specified for both).

Once i tried to access this type using ASP.webusercontrol_ascx , at one time this worked but at other time it does not work.What can be the problem with this .Remember i am working using VS.NET 2008

# re: TRULY Understanding Dynamic Controls (Part 4)

Monday, September 01, 2008 11:31 AM by Stephan

He Zee,

I had this problem, but when I put the

<%@ Register Src="~/WebUserControl3.ascx" TagName="Item" TagPrefix="uc1" %>

at the top of my page, everything works fine. If you put it at the top you can use it in the code behind as a typed control and access it's properties. Well maybe you should cast it first.

# re: TRULY Understanding Dynamic Controls (Part 4)

Monday, September 01, 2008 2:06 PM by Cristian Vintila

Hi,

Thanks again for the update. Sorry i've been away for a while and i just saw your message.

I have attached some my code so you can see what i'm talking about.

By taking ajax off, i meant literally removing the update panels and all that.

Here's a smaller version of the code i'm using.

////////////////////////////////////////////////////////////////////////////

protected void Page_Load(object sender, EventArgs e)

       {

((HtmlGenericControl)Master.FindControl("SiteMasterBodyTag")).Attributes.Add("onunload", "javascript:__doPostBack('PostBackLeavingPage', '');");

BindRepeater();

//set validation group of the currently loaded section

lnkContinue.ValidationGroup = "Section" + loadSectionID + "Validation";

LoadQSControl(loadSectionID);

}

//this is not working that great

protected void Page_SaveStateComplete(object sender, EventArgs e)

       {

           if (IsPostBack)

           {

               object eventTarget = Request["__EVENTTARGET"];

               object eventArgument = Request["__EVENTARGUMENT"];

               if (eventTarget != null && eventTarget.ToString().Trim().Equals("PostBackLeavingPage"))

               {

                   SectionQuestions sq = (SectionQuestions)QuestionsPlaceHolder.Controls[0];

                   //sq.saveAnswers(false);

               }

           }

       }

private void LoadQSControl(byte sectionID)

       {

           lnkContinue.ValidationGroup = "Section" + sectionID + "Validation";

           SectionQuestions control = (SectionQuestions)LoadControl("/profile/controls/SectionQuestions.ascx");

           control.ID = "SQ" + sectionID;

           control.sectionID = sectionID;

           QuestionsPlaceHolder.Controls.Clear();

           QuestionsPlaceHolder.Controls.Add(control);

       }

protected void listSections_ItemCommand(object source, DataListCommandEventArgs e)

       {

           if (e.CommandName.Equals("GoToSection"))

           {

               byte sectionID = Convert.ToByte(e.CommandArgument);

               lnkContinue.ValidationGroup = "Section" + sectionID + "Validation";

               LoadQSControl(sectionID);

           }

       }

////////////////////////////////IN THE CONTROL ITSELF///////////////////////////////////

protected void Page_Init(object sender, EventArgs e)

       {

           if (sectionID.Equals(0)) return;

           RenderControls();

       }

//RenderControls simply does a Controls.Add of the various controls such as checkbox, textbox etc.

//cutoff version of the saveMethod.

public void saveAnswers(bool continueToNextSection)

       {

ArrayList relatedQuestions = new ArrayList();

           if (Controls.Count != 0)

           {

               for (int i = 0; i < Controls.Count; i++)

               {

                   if (Controls[i].ID != null)

                   {

                       if (Controls[i].ID.Contains("AnsForQID"))

                       {

                           short questionID = Convert.ToInt16(Controls[i].ID.Replace("AnsForQID", ""));

                           UserAnswersCollection uaColl = new UserAnswersCollection();

                           if (uac.Count != 0)

                           {

                               UserAnswersCollection.RemovePreviousAnswers(accountID, questionID);

                           }

                           string typeName = Controls[i].GetType().Name;

                           switch (typeName)

                           {

                               case ("RadioButtonList"):

                                   RadioButtonList radList = Controls[i] as RadioButtonList;

                                   AddUAEntityToColl(uaColl, radList.SelectedValue, accountID, questionID,false);

                                   break;

                               case ("DropDownList"):

                                   DropDownList ddList = Controls[i] as DropDownList;

                                   AddUAEntityToColl(uaColl, ddList.SelectedValue, accountID, questionID,false);

                                   break;

}

}

}

}

}

}

//////////////////////////////////////////////////////////////////

# re: TRULY Understanding Dynamic Controls (Part 4)

Monday, February 09, 2009 9:56 AM by Mike

I am not very experienced with .net.

I want to build a form where the meta data in the datbase dictates whether to display a checkbox, radio button, textfield, text area or select box?  The  exact labels/questions and the number of each type of elements is determined based on the specific survey or question set.

Does this have to be done using dynamic controls?  Can this be done using repeaters/templates as described above?

Mike

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, February 24, 2009 10:56 AM by Ted

Great article!  In the case of sharepoint, specifically an editor for a web part, is it possible to load a .ascx control within that control?  There is no page file; only .cs files.  

Consider the following:  I have a web part that has a variable number of properties that depend on a couple of other properties that are alway present.  

Very similar to  the property grid in visual studio.  In visual studio when you focus on an item, the property grid binds to that object and dynamically populates the grid based on the items properties.  Some of these properties are complex and require the use of type converters.

In sharepoint when you develop a web part and the properties of the web part are not simple types (string, int, etc.)  you often times need to create an editor.  The editor has a method named CreateChildControls.  This is where you typically and dynamically would add your controls using Control.Add(myControl).  I would like to convert my dynamic model to a templated model that a .ascx control would provide.  By seperating the GUI and backend code it would provide me with a model that is easier to maintain, the ability to use databinding, and better unit testing.  What is your recommeded approach?

~Ted

# re: TRULY Understanding Dynamic Controls (Part 4)

Monday, April 20, 2009 2:07 AM by Andrew

Great article very helpful.

I'm looking to for a way to solve the following problem without using dynamic controls but just can not figure it out.

I have a form for editing multiple XML node groups at once, each node group will be edited using a customised user control. My problem is that the frequency and type of node groups can vary, there can be 1 to n and of any type of node group.

# re: TRULY Understanding Dynamic Controls (Part 4)

Thursday, April 23, 2009 2:39 AM by prabhu

Hi...HOw to create multiple rows and columns of textboxes and enter the values in the database....

# re: TRULY Understanding Dynamic Controls (Part 4)

Tuesday, April 28, 2009 2:38 PM by whatispunk

Good stuff!! Eagerly awaiting part 5...

# re: TRULY Understanding Dynamic Controls (Part 4)

Saturday, May 02, 2009 6:44 PM by chris

Wonderful post!!!

Had had this challenge for a while and had read thru the 4 parts of ur blessed article. When I saw the dates I was full of prayers that you would still be online that was before I got to the end of the list... :)

My challenge is simple. I have an e-testing app that automatically loads any specified number of questions from the database (this could range from 50 to 300 questions). I constructed a usercontrol containing all the required fields. this control is then "hosted" on the view controls of the multiview control.

So if the quiz master selects 68 questions to be loaded the app should load 68 questions on 68 dynamically created controls on the panel control added to the view control which is in turn added to the multiview control. This has been a nightmare. Any suggestions for alternatives are welcome.

Waiting eargerly for your reply

Thanks

chris

jcc_nnannah@yahoo.co.uk

# re: TRULY Understanding Dynamic Controls (Part 4)

Thursday, August 06, 2009 11:43 PM by vinneyk

Hello. Really great post. Helped me learn a little more about how much I don't know :)  I understand that this post has been up for a few years and that you may have moved on, but just incase you are still responding to posts, I think I have a question that is different from any of the previous posts. So, here it goes:

I am working on a simple messenging service for a web system. I currently have the message controls in an update panel on the master page. This allows me to call a master.DisplayMessage("title", "body") method to show dynamic messages based on the specific situation. This works great, however, I would like to extend this control to be able to prompt users and allow the code to react as appropriate.

For example, (the system is for registering students for classes) an administrator could be registering a student for a class for which they do not meet the prerequisite requirements. In this case, I would like to display the message prompting the user "Would you like to override these requirements?" and react their response.

So, based on the advice of the 4th article, I decided to create the necessary buttons on the master page and show/hide them as necessary. I also created event handlers for each button on the code file for the master page. Using this method, before I call DisplayMessage(...), I set master.OnYesButtonClicked += yada_yada (to play on the earlier Seinfield comment). However, as I am sure you can guess, when the page reloads, the OnYesButtonClicked event handler is null.

Can you please suggest what you think might be a better way to handle this? I feel like I am an in an infinant loop here.

Thanks,

Vinney

# re: TRULY Understanding Dynamic Controls (Part 4)

Thursday, September 10, 2009 4:09 PM by Nick

Very interesting article, providing an insight into the asp.net workings. I from my part, however, follow a simple approach that seems to work fine with dynamic controls. I simply don't access their values programmatically but through the Request.Params collection. If necessary, a recreate the controls by passing the Request.Params value to the routine creating them, instead of for example the original db value.

Regards Nick  

# re: TRULY Understanding Dynamic Controls (Part 4)

Wednesday, December 23, 2009 2:04 AM by Raja

A very enlightning to a newbie for Dynamic controls like me...

Leave a Comment

(required) 
(required) 
(optional)
(required)