Atlas M2.2 - Dynamic UpdatePanels (finally)

The June Atlas CTP fixes some bugs, and adds support for dynamic UpdatePanels opening up scenarios such as UpdatePanels within data-bound controls or WebParts, and more importantly, custom control scenarios. This post demonstrates the control development usage.

Today we released the June Atlas CTP (internally known as build M2.2). There are a handful of bug fixes, but one feature in particular, makes this anything but a minor release. UpdatePanels can now be added dynamically into the page at runtime! This opens up a number of scenarios including UpdatePanels inside templates of data-bound controls, UpdatePanels in user controls used as WebParts etc.

UpdatePanels within Templates
As you'd imagine, as a page developer, you can now do something like this:

<asp:DataList runat="server">
  <ItemTemplate>
    <asp:UpdatePanel runat="server">
      ...
    </asp:UpdatePanel>
  </ItemTemplate>
</asp:DataList>
Even though the UpdatePanel appears to be statically declared, in reality it is dynamic. The DataList builds up a set of items dynamically and instantiates its template while enumerating its data. In past builds, you'd see an error indicating that it was too late to register an UpdatePanel; this scenario now works fine.

UpdatePanels within Composite Controls
The implications of dynamic UpdatePanels will likely open up many more control developer scenarios. I can now build a composite control that implements partial rendering within itself simply by including an UpdatePanel as part of its controls collection. This makes partial rendering more "automatic" for page developers using a new breed of Atlas-enabled server controls. I have put together a sample demonstrating the model. It is a simple PhotoViewer composite control that contains an Image control, and a couple of Button controls to implement next/prev functionality.

Here is the relevant portion of code. You can download the full sample here.

public class PhotoAlbumViewer : CompositeControl, IPartialRenderingCompositeControl {

    private Image _image;
    private Button _nextButton;
    private Button _prevButton;
    private CompositionUpdatePanel _container;

    private int _index;
    private string[] _images;

    public string[] Images {
        get { ... }
        set { ... }
    }

    protected override void CreateChildControls() {
        _container = new CompositionUpdatePanel(this);
        _container.Mode = UpdatePanelMode.Conditional;
        _container.ID = "Container";
        Controls.Add(_container);

        _image = new Image();
        _nextButton = new Button();
        _prevButton = new Button();

        _nextButton.Text = "Next";
        _nextButton.CommandName = "Next";

        _prevButton.Text = "Previous";
        _prevButton.CommandName = "Prev";

        _container.Content.Controls.Add(_image);
        _container.Content.Controls.Add(_nextButton);
        _container.Content.Controls.Add(_prevButton);
    }

    protected override bool OnBubbleEvent(object source, EventArgs args) {
        bool handled = false;

        CommandEventArgs ce = args as CommandEventArgs;
        if (ce != null) {
            if (ce.CommandName.Equals("Next", StringComparison.Ordinal)) {
                _index++;
                handled = true;
            }
            else if (ce.CommandName.Equals("Prev", StringComparison.Ordinal)) {
                _index--;
                handled = true;
            }
        }

        if (handled) {
            if (_index < 0) {
                _index = _images.Length - 1;
            }
            else if (_index >= _images.Length) {
                _index = 0;
            }
            _container.Update();
        }

        return handled;
    }

    ...

    #region IPartialRenderingCompositeControl Members
    void IPartialRenderingCompositeControl.RenderUpdatePanel(CompositionUpdatePanel updatePanel, HtmlTextWriter writer) {
        if ((_images == null) || (_images.Length == 0)) {
            return;
        }

        int imageCount = _images.Length;
        _image.ImageUrl = _images[_index];

        if (_index == 0) {
            _prevButton.Enabled = false;
        }
        if (_index == (imageCount - 1)) {
            _nextButton.Enabled = false;
        }

        writer.RenderBeginTag(HtmlTextWriterTag.Table);

        writer.RenderBeginTag(HtmlTextWriterTag.Tr);
        writer.RenderBeginTag(HtmlTextWriterTag.Td);
        _image.RenderControl(writer);
        writer.RenderEndTag();  // Td
        writer.RenderEndTag();  // Tr

        writer.RenderBeginTag(HtmlTextWriterTag.Tr);
        writer.RenderBeginTag(HtmlTextWriterTag.Td);
        _prevButton.RenderControl(writer);
        writer.Write("&nbsp;");
        _nextButton.RenderControl(writer);
        writer.RenderEndTag();  // Td
        writer.RenderEndTag();  // Tr

        writer.RenderEndTag();  // Table
    }
    #endregion
}

So what's happening here? I have a control deriving from CompositeControl, and creates its contents by overriding CreateChildControls. The overall rendering is produced through a combination of child controls and layout scaffolding around the controls rendered directly as markup into the writer. The control renders an image and updates the image each time the buttons are clicked by handling bubbled events. Pretty standard stuff so far if you've written server controls.

What's new is the use of the CompositionUpdatePanel control, a derived UpdatePanel control that makes it simple to implement composition by providing a default ContentTemplate which is a required property right now as well as enabling the scenario where some of the content is rendered directly without using child controls. This control is part of a prototype I have put together and included with the sample - I am hoping to get this type of functionality and scenario fully supported in Atlas eventually. Essentially the composite control implements the IPartialRenderingCompositeControl interface (also introduced in the sample), and passes it into the contained UpdatePanel control. This interface allows the UpdatePanel to call back into the outer composite control to render the children and any interspersed markup when it comes time to render the UpdatePanel.

In this particular sample all of the child controls have been added into a single UpdatePanel. A more complex control might have multiple UpdatePanels within, as well as some content outside of UpdatePanels. There are a couple of things to keep in mind as you go down this path. Your control's Render method will not be called in a partial rendering request; only the UpdatePanel's Render method will be called. Therefore, some logic might need refactoring, in terms of how you build your child control hierarchy, or how you might need to move some rendering logic into the RenderUpdatePanel implementation. Secondly, you're more than likely to set your UpdatePanel's Mode property to Conditional, and determine precisely when it should be updated by calling the Update method. In the sample, the UpdatePanel is updated only when the user clicks on one of the buttons, rather than on every request.

A look at the "evolution" of UpdatePanels
So what was the big deal with dynamic UpdatePanels? The answer lies with the Triggers feature. One use of a trigger is to specify which UpdatePanels and when they should be updated as the ScriptManager puts together a response for partial rendering. See my previous post for more information on triggers. For example, a ControlEventTrigger marks an UpdatePanel as needing an update if and when a specified control raises the specified event. This makes things interesting. The UpdatePanel needs to exist in the control tree and register for listening to the event before the event is raised. The event is raised during the post-back processing phase of the page lifecycle. Hence adding UpdatePanels during Load, or PreRender, or PreRenderComplete (that is when some DataBound controls instantiate their templates) is simply too late.

We've relaxed that restriction, but this in turn results in a constraint on how you set up triggers for dynamic UpdatePanels. The control that raises the event to which the ControlEventTrigger points to needs to be in the same naming container as the UpdatePanel. In other words it cannot be in a sibling or parent naming container. This ensures the UpdatePanel can successfully register and handle events, because within a naming container controls are created at the same time (at least that is the case in properly designed composite controls). The reason for this is some subtle aspects of the page life cycle and post-back processing architecture. When the event source control needs to raise an event as a result of some post-back data, the page attempts to find it in the control tree, and in the course of doing so, controls are created on-demand to fill naming containers. With the constraint on triggers that I just mentioned, it is guaranteed that both the event source and the UpdatePanel will have be created before the event is raised.

This constraint actually works reasonably well. For example, a WebPart typically will contain the control that causes post-back as well as the content that needs to be embedded in the UpdatePanel. The same goes with the GridView or DataList scenario. The control that needs to cause an update within a particular row in a data-bound control is quite likely logically placed in the same row. In the case of composite controls, I don't think Triggers will even be that useful, further implying this constraint isn't a big deal. The composite control using the UpdatePanel is responsible for mapping its own semantics into appropriate calls to Update, just like I did in my sample.


[ Tags: | | | ]
Posted on Friday, 6/30/2006 @ 10:45 AM | #ASP.NET


Comments

37 comments have been posted.

Liming

Posted on 6/30/2006 @ 11:53 AM
Excellent. This just lift some major restrictions I had in terms of using Atlas effectively in a few applications! Can't wait to try it out. Thanks for the detailed explanation.

Nikhil Kothari

Posted on 6/30/2006 @ 12:50 PM
Btw, I should mention, this is an early prototype, and shows what you can do with the bits released today. One of my goals with this, beyond getting this information out at this stage, was also to experiment with ideas around what we can/should do in the future as we continue to evolve. I have some ideas through which it will be even simpler to write such controls, but it will require some refactoring of the ScriptManager/UpdatePanel implementations, so I'll save that for a later date.

In the meantime, if you have some feedback, or interesting ideas you'd like to discuss, please post them.

Dhanraj

Posted on 6/30/2006 @ 1:17 PM
Excellent work. I'm using a tab control and a gridview control inside it. surrounding the gridview control with updatepanel. In the code behind i have a function which populates the gridview on page load. I have couple of buttons on top of gridview, which adds records to the table which the gridview displays. Is there a way we can refresh just the Gridview when the records are added?. I have a client side javascript function which makes an asynchronous call to the server function to add the records when the buttons are clicked. For testing purpose i've tied the update panel trigger to the timer control which refreshes after 10 seconds, but that does'nt seem to be working.

Can Erten

Posted on 6/30/2006 @ 2:15 PM
Great to see update panel in different page life cycle. This will open up great oppurtunities for us. I really wonder and want to try multiple nested dynamic update panels and their behaviors.. IPartialRenderingCompositeControl interface will also help to not to misuse of the updatepanel in the entire page.
Thanks for the update.

Ed

Posted on 6/30/2006 @ 2:28 PM
That's a nice new feature. How is the safari support for this?
Thanks for the update

Laurent Kempé

Posted on 6/30/2006 @ 3:30 PM
Great update!!! Login is working fine and the new updatepanle solved some of my previous issues.
One question, how would you handle connected webparts?

Amit Goswami

Posted on 7/1/2006 @ 6:21 AM
Great, looking forward to trying this out in a MultiVew control which has been giving me problems lately.

Loren

Posted on 7/1/2006 @ 9:15 AM
I see that login controls now work inside update panels, but do upload controls work in update panels yet?

Lchrennew

Posted on 7/2/2006 @ 2:05 AM
Greate! I love Atlas!

Matt

Posted on 7/2/2006 @ 12:26 PM
I'm not sure if it's just me, but it appears that the update progress now only "fires" the first time on a page where previously it fired everytime there was an async call.

Dirc

Posted on 7/3/2006 @ 6:32 AM
Any specific instructions as to how to upgrade form the previous version? Is a simple un-install/ install? Or can we simply run the install and the previous version will be upgraded? (Documentation only mentions a first time install)

Also, is it totally backwards compatible?

Dirc

Posted on 7/3/2006 @ 6:34 AM
Matt, as for the event to be only fired the first time, I have been having this problem intermittently in the previous version, so I'd hesitate before blaming this version!

Nikhil Kothari

Posted on 7/3/2006 @ 8:48 AM
Laurent: On the topic of connected webparts, the Webpart should call Update() on its contained UpdatePanel, when it needs to be re-rendered, for example, as a result of some data being changed as a result of connections.

Loren: File upload scenarios cannot be implemented as async postbacks with partial rendering - there is no way for client script to read a file and send the data programmatically - so you must use a regular postback there.

Dirc: I'd recommend uninstalling/re-installing. Of course you'll want to copy the new dll into your existing application's bin folders manually.

If you are reporting bugs or strange issues, or need support with an issue, please post them on the atlas forums for better response, as well as better tracking. Thanks.

Matt

Posted on 7/3/2006 @ 10:37 AM
Dirc: I see what you mean. Today it's working fine.

Mettlus

Posted on 7/3/2006 @ 12:01 PM
Hello Nikhil, Can you tell me how can I accomplish this?
I have a simple page with a Grid View on it which binds to a Select statement on a SQL Db. The table gets new rows every second, how can I refresh the page and show user latest data without doing a client refresh etc....for now I use the meta tag to refresh every 5 seconds which causes new data to load.
Tell me the ATLAS way!!!!

Thanks

Nikhil Kothari

Posted on 7/3/2006 @ 6:58 PM
Mettlus - Check out the Timer server control that is part of Atlas - it has a Tick event which you can use as a trigger for your UpdatePanel. Keep in mind - you probably don't want to refresh the page every second though - that would quite possibly an abuse of Ajax...

Clueless John

Posted on 7/3/2006 @ 7:20 PM
Hello Nikhil,
I am trying to read your latest post about Dynamic UpdatePanels. I am sure it was lots of work to program them, debug, and release. Unfortunatly I cannot find any documentation as to what Dynamic UpdatePanels are. Is there a pointer or something you could put so people can tell what you are talking about?
I also downloaded the "full sample", but found it came with no docs, notes, readme or anything. I tried to make it it's own "vs2005 website" but it would not "build".
Is there a way in the future you could include some notes with your samples as how to install/run your samples?
I don't mean to sound like an ass, but I tried googling for an answer, but found only links to your site. I tried wikipedia, but nothing.
I appreciate the work you provide, but I cannot make heads or tails of it.

Thanks again,
John

Monica

Posted on 7/3/2006 @ 10:48 PM
I have a simple page with a formview on it, it binds to a Select statement on a SQL DB. I bind the formview with an objectdatasource. I would like to use the atlas updatepanel for June CTP, which I downloaded yesterday. I try to use the atlas:updatepanel in the formviews edititemtemplate, which I know didn't work in tha April CTP, tought it will work for the June CTP. I get the error message: "The UpdatePanel 'up_order' was not present when the page's InitComplete event was raised. This is usually caused when an UpdatePanel is placed inside a template".
Am I wrong is it still not possible to use the updatepanel inside of a formview?
Thanks

David Taylor

Posted on 7/4/2006 @ 2:43 AM
Talking of the Timer server control....It is a pitty this has still not been better integrated.

Anyone coming from a server control mindset, would expect to be able to call .Enabled = false; or .Inverval = (int) within a async callback and have the timer actually updated. Sadly it seems the underlying xml-script is poking through here and thus the server control does not work correctly. Even if you place the timer within an update panel and change these properties....it does not work. I tried this with an earlier release and noticed the result was even worse that new timers appears to be added and fired in parallel.

This situation clearly needs to be fixed....Or else the timer control should be removed from Atlas and you should get 3rd parties to write their own.

It is an obvious scenario to have the timer fire once per 10-15 seconds until something completes asynchronously, and then to disable the timer via the async update. I could think of many other scenarios as well.

I dont think you fully grasped my earlier post all those months ago asking for this to be fixed...But it would be great if the team could fix this and make the timer behave as ASP.NET page developers would expect.

Thanks...and great work so far.

David Taylor

Nikhil Kothari

Posted on 7/4/2006 @ 9:17 AM
John, yes docs are terse, when they exist - agree.
First what are dynamic panels - they are described over the course of this post in an indirect manner. Dynamic UpdatePanels are those UpdatePanels that are added to the control tree during the page lifecycle, i.e. after the page has been constructed from its initial markup. This includes controls you add via Controls.Add in events handlers, UpdatePanels instantiated because they are contained within templates or the contents of other controls. Hope that helps.
Second, on the sample, I am pretty sure you shouldn't get a build error - Make sure you are running against the June Atlas CTP. Create a website, enable atlas in it, i.e. follow the steps in the Atlas docs to add Microsoft.Web.Atlas.dll to the bin directory etc. and add the code into the App_Code directory, and the page into the website, and then browse to the page. You should be set. For this particular sample, you'll need 3 images - can be anything really - just named a.jpg, b.jpg, c.jpg to visually see something.

Monica - not sure - it ought to work. Could you post your questions along with a small meaningful repro on the atlas forums on the asp.net forums?

David - You can still write another Timer control with this one existing. The timer cannot be placed in an UpdatePanel - known issue, agree its an issue, but this is of lower priority. The higher priority issue is the ability to enable it, change frequency and have it update as you mention. The purpose of the timer as it exists today is to place it outside the update panel, and cause a periodic refresh using async postbacks instead of using the meta tag to update the entire page (hence its expected you'll have one on the page, and it can be outside update panels). It is not to do what you describe, i.e fire while an async update is going on. To fix all these issues you need various client-side events while an async postback is going on, which aren't there yet, and also some mechanisms to update controls without UpdatePanels, but we are working on for a future release.

Mettlus

Posted on 7/5/2006 @ 5:22 AM
Yes Nikhil, Thanks did it, but the problem is the whole page refreshes before i see my grid refresh. Heres my code
<script runat="server">
protected void RefreshGrid(object sender, EventArgs args)
{
GridView1.DataBind();
}
</script>

<atlas:TimerControl Enabled="true" ID="AtlasTimer" Interval="60000" OnTick="RefreshGrid" runat="server" />
<Triggers><atlas:ControlEventTrigger ControlID="AtlasTimer" EventName="Tick" /></Triggers>

Mettlus

Posted on 7/5/2006 @ 1:42 PM
Hello Nikhil, My GridView refresh using atlas is having serious trouble in OPERA and Firefox browsers!!!!
In OPERA v9.0 build 8501 the grid view doesn't refreshes, whereas in Firefox the browser throws an application error dialog box and quits.
Wz up with all this? I am using the Atlas June CTP, btw it works well on IE, also please refer 2 my code to point out any fallacies....I made it work using a callback function which I tie to the TimerControl

Coleman

Posted on 7/5/2006 @ 2:56 PM
Nikhil - Nice work. I have a question/suggestion(s) regarding the Progress Panel. Currently, it is my understanding that you are only allowed one per page. Is it in the works to allow either multiple ProgressPanels that would allow say an image/gif being displayed over individual UpdatePanel areas. For example, over a grid(s) whilst it is updating. I suggested to the folks at Wintellct that this would be a great feature. Furthermore, rather than having multiple ProgressPanels, there could be one, but allow wiring the UpdatePanels to the ProgressPanel (like with triggers or properties) so that it would display over U.P. when performing a callback.

Thanks in advance.

Regards
Coleman

vikram

Posted on 7/7/2006 @ 5:14 AM
Hi
I am using Atlas control toolkit and found this big problem with drag panel.

I was trying to use the drag Panel in one of my page which also contains some drop down lists. When I Drag the drag panel on the dropdownlist (I am using IE) I see the dropdownlist above the drag panel.
This behaviour is only taking place in IE and not in firefox. I do not know the real reason of this problem but this is making life difficult for me.

Do any one know how to solve this problem.

http://vikram.qsh.eu/pages/blogdetail.aspx?blog_id=23

Thanks
Vikram

Eric Golpe

Posted on 7/7/2006 @ 6:41 AM
This is awesome. Great work of the eventing and makes this more pleasurable than they already were in Atlas previously. I am hoping this new appraoch will lend itself better to Master page scenarios using nested user controls on the page.

Great work! A 'must-have' download!

-Best Regards,
Eric

Gopinath Varadharajan

Posted on 7/8/2006 @ 7:31 AM
Nikhil,

I'm trying to update the window.status after updatepanel postback is done. If i use this inside master page (regular aspx works) the
javascript doesn't fire ?
Any suggestions ..

Thx,

Gopi

קולנוע

Posted on 7/10/2006 @ 9:45 AM
Great work ! loved it !

Rose

Posted on 7/11/2006 @ 7:56 AM
Hello Nikhil,

I tried downloading this sample and trying it out. I am new at Atlas btw. But it won't run, this line: Controls.Add(_container); seems to be the problem and generates this error "InvalidOperationException was unhandled by user code. Do you know what that is? Do you have any suggestions on the best way to start working with Atlas? I am evaluating software and my first project is to build a photo album.

Thanks,
Rose.

Hossein Riazi

Posted on 7/11/2006 @ 1:07 PM
Hi Nikhill, It seems that with June CTP, I can not call any web-service if I set EnableScriptComponents=false. It calls the OnError with null parameter.
Please help since setting it to true cause some other problem for my application.
I posted the detail here: http://forums.asp.net/thread/1335487.aspx
I apprciate your help.
Thanks,
Hossein Riazi

Bobstar

Posted on 7/11/2006 @ 3:57 PM
I cant the sample provided to work properly. The compiler tells me 2 errors:

Error 1 Unknown server tag 'nStuff:PhotoAlbumViewer'. C:\Documents and Settings\Bobstar\My Documents\Visual Studio 2005\WebSites\CompositeControl\CompositeControl.aspx 13
and
Error 2 Element 'PhotoAlbumViewer' is not a known element. This can occur if there is a compilation error in the Web site. C:\Documents and Settings\Bobstar\My Documents\Visual Studio 2005\WebSites\CompositeControl\CompositeControl.aspx 13 21 C:\...\CompositeControl\

What's wrong?

Kind regards

Michel

Posted on 7/13/2006 @ 5:39 AM
Hi Nikhil, i am using an asp table control on a webpage and a button control, on its click event i specify the number of rows and columns to the table, i also trying to add a new button control to each of the table cell on click event itself, and it did well, now i want click event of newly created button for that i declare event of new button after declaring its instance, and define its handler in the class. As event should be declare in either OnInit or OnLoad method, but i want it should declare on first button's click event, since any control does not raise event in post back for that i place all controls inside update panel, making EnablePartialRendering property of ScriptManager true and trigger all at first button's click. But it doesn't work at all, so could you please help how to do it?

Thanks
Michel

Rebecca

Posted on 7/13/2006 @ 8:31 AM
I'm not getting anywhere on the forums, and you do have experience with this per your sample, so perhaps you can help? http://forums.asp.net/thread/1339659.aspx It's just this darn calendar!

Mahi

Posted on 7/14/2006 @ 1:54 AM
Hi Nikhil, Great Work.
I got a question. Earlier i have used AJAX in providing context sensitive help for a web page. All that it does was, when i click on a text, it calls a Javascript function which inturn calls the Server method. I am here providing the code..
.....
<td class="label" onclick="LoadHelp('CompanyReference');">Company Reference:</td>
......
<script language="javascript">
function LoadHelp(FieldName)
{
HomePage.GetHelp(FieldName,LoadHelp_CallBack);
}

function LoadHelp_CallBack(response)
{
var helpText = response.value;
// some little more code here....
}
</script>

unfortunately, i could not find any way in Atlas to implement the above feature...
As of my understanding, the update panel gets refreshed only if any of the server controls causes a post back like button etc..
but the onclick method on TD tag, or Asp:Label do not cause post back....so could not use the update panel....
Also i do not want to use the timer as it should call the server method only on the click event of the text or some label...
I thought , calling the update method on Update panel explicitly would help me in some way, but could not find any documentation or useful links on the same...


If you had already answered a similar question some where ... please provide me some pointers....

Thanks,
Mahi

uday bhaskar

Posted on 7/20/2006 @ 3:55 AM
hi i am facing problem in back button i am unable maintain state of the previos page which uses atlas.
please help me.

Atlas Newbie

Posted on 7/21/2006 @ 9:54 PM
Hi,

Please can you give me the examples for creating an update panel with its content template and triggers in VB.NET.

I currently have a page where I dynamically load the lables and corresponding multi dependency combo boxes from DB. In a non atlas scenario this is all working fine. Now when I want to include the Atlas code I will need to set update panels for the generated combo boxes, meaning I will also need to do that dynamically. As I am new to this, it would be a great help if you could provide some code sample of dynamic code time update panel creation using VB.NET.

Thanks in Advance.

ashish

Posted on 7/21/2006 @ 11:09 PM
hey can you provide an vb.net example?

Masoud Shokri

Posted on 7/22/2006 @ 3:58 PM
Two things, First of all I guess it was better if the text color for name and email textboxes wasnt white, because so many people, like me, use google stuff and that tool changes the background of those two textboxes to yellow. Second I tried to work with cascading drop down list in Atlas Get Started examples, I changed the xml file, in the App_Data folder, but nothing changed in the UI, and I tried to change the code but It doesnt work, Is it a bug? Or something?
The discussion on this post has been closed. Please use my contact form to provide comments.