Messing with UpdatePanel to speed up and extend Ajax
I was looking for an answer to the problem of a grid inside an update panel. You see, since the rows and cells of a DataGrid or a GridView are special controls that can't be put inside panels, only in specific parent controls like tables and rows, there is no way to update only a row or a cell of a grid. If the grid is big, it takes a long time to render it entirely, it takes the CPU to 100%, it even blocks the animation of gifs. That results in ugly Ajax.
So, my first thought was: is there a way to update only what has changed? As I was saying in a previous post, a Page is rendered as its HTML string the first time it is loaded and then each Ajax postback makes it render like a list of tokens. The token format is this:
For example 100|updatePanel|UpdatePanel1|<inner HTML of panel of 100 bytes>|
What if I would to insert my own tokens, then? Could I, let's say, change the innerHTML of a control outside of the UpdatePanel? And the answer is YES!
There are 20 token types:
Most are not interesting, but 4 of them are!
type:updatePanel
If you add a token to the rendered page string that has the type updatePanel and the id is the UniqueID or ClientID of a control, the content will replace the innerHTML of that control, even if the control is not in an UpdatePanel.
type:hiddenField
If you add a token to the rendered page string that has the type hiddenField and the id is the UniqueID or ClientID of a control, the content will replace the value html property of that control. You can use it on hidden fields, but also on any type of input or html element that has a value. If the control does not exist, a hidden input will be created with that id and then the value will be set. You could read that value after a normal PostBack, let's say.
type:expando
If you add a token to the rendered page string that has the type expando a script will be executed in Javascript that looks like this:
Example: 5|expando|document.getElementById('TextBox1').style.backgroundColor|'red'|
This will result in the change of the background color of the control with id TextBox1 to red.
type:focus
If you add a token to the rendered page string that has the type focus and the content is a ClientID, then the focus will be set to that control provided that the focus.js script has been loaded. This script is loaded when you use Page.SetFocus. So, in order to set the focus to a control using this method, you must use SetFocus in PageLoad on any control you would like.
Why not use SetFocus, then, and be done with it? Well, because this, as all the methods above work on ANY control in the page, not just the ones in the update panel.
Of course, that doesn't solve my initial problem, of speeding up the Ajax rendering of large grids. That's because, even if I would solve the ViewState issues and the quirks that are bound to appear, I still can't change the innerHTML property of tables or table rows, as it is a readonly property.
So where am I to use this? It's easy: first of all, put a button (and only a button) inside an UpdatePanel. Any click on that button will trigger an Ajax postback, but will send a minimal amount of data. Then, put outside the UpdatePanel a Panel. Now you can override the Render of the page and on every Ajax postback, add whatever HTML you want to that panel. If you want to do it from javascript, put the button in a div with style="display:none" and then trigger the button click whenever you want to cause the postback. I am certain that for large readonly grids, that is a way faster method than the putting the grid inside the updatepanel.
To do this the traditional Atlas way you would have had to declare a web service, and then to set a javascript onclick event on the button, that would have executed a WebService method that returned a string, and manually change the innerHTML of the panel.
So, my first thought was: is there a way to update only what has changed? As I was saying in a previous post, a Page is rendered as its HTML string the first time it is loaded and then each Ajax postback makes it render like a list of tokens. The token format is this:
length|type|id|content|
For example 100|updatePanel|UpdatePanel1|<inner HTML of panel of 100 bytes>|
What if I would to insert my own tokens, then? Could I, let's say, change the innerHTML of a control outside of the UpdatePanel? And the answer is YES!
There are 20 token types:
- updatePanel
- hiddenField
- arrayDeclaration
- scriptBlock
- expando
- onSubmit
- asyncPostBackControlIDs
- postBackControlIDs
- updatePanelIDs
- asyncPostBackTimeout
- childUpdatePanelIDs
- panelsToRefreshIDs
- formAction
- dataItem
- dataItemJson
- scriptDispose
- pageRedirect
- error
- pageTitle
- focus
Most are not interesting, but 4 of them are!
type:updatePanel
If you add a token to the rendered page string that has the type updatePanel and the id is the UniqueID or ClientID of a control, the content will replace the innerHTML of that control, even if the control is not in an UpdatePanel.
type:hiddenField
If you add a token to the rendered page string that has the type hiddenField and the id is the UniqueID or ClientID of a control, the content will replace the value html property of that control. You can use it on hidden fields, but also on any type of input or html element that has a value. If the control does not exist, a hidden input will be created with that id and then the value will be set. You could read that value after a normal PostBack, let's say.
type:expando
If you add a token to the rendered page string that has the type expando a script will be executed in Javascript that looks like this:
id=content
Example: 5|expando|document.getElementById('TextBox1').style.backgroundColor|'red'|
This will result in the change of the background color of the control with id TextBox1 to red.
type:focus
If you add a token to the rendered page string that has the type focus and the content is a ClientID, then the focus will be set to that control provided that the focus.js script has been loaded. This script is loaded when you use Page.SetFocus. So, in order to set the focus to a control using this method, you must use SetFocus in PageLoad on any control you would like.
Why not use SetFocus, then, and be done with it? Well, because this, as all the methods above work on ANY control in the page, not just the ones in the update panel.
Of course, that doesn't solve my initial problem, of speeding up the Ajax rendering of large grids. That's because, even if I would solve the ViewState issues and the quirks that are bound to appear, I still can't change the innerHTML property of tables or table rows, as it is a readonly property.
So where am I to use this? It's easy: first of all, put a button (and only a button) inside an UpdatePanel. Any click on that button will trigger an Ajax postback, but will send a minimal amount of data. Then, put outside the UpdatePanel a Panel. Now you can override the Render of the page and on every Ajax postback, add whatever HTML you want to that panel. If you want to do it from javascript, put the button in a div with style="display:none" and then trigger the button click whenever you want to cause the postback. I am certain that for large readonly grids, that is a way faster method than the putting the grid inside the updatepanel.
To do this the traditional Atlas way you would have had to declare a web service, and then to set a javascript onclick event on the button, that would have executed a WebService method that returned a string, and manually change the innerHTML of the panel.
Comments
As I said, a row cannot be changed by changing its innerHTML. That would have been cool, but it doesn't work. Changing cells one by one doesn't really help either, I believe, due to the amount of work necessary to make it function flawlessly, but since I never tried it, I can't say for sure.
SideriteCouldn't you give each table row (or cell for that matter) a unique ID, and use the expando token technique to document.getElementById(id).innerHtml = xxx?
RonIt did work in the test scenarios, but I haven't used it yet in a real life situation. If you do, don't forget to let me know ;)
SideriteHi there, very interesting approach. I'd like to find out if you made if finally to work. Any problems?
Anonymous