Dynamic User Control, Ajaxify Your Controls
2009-04-03 13:33
399 查看
Introduction
This article describes a control which can host aUserControl. It allows to Ajaxify a user control without any code change. The main benefit is that when refreshing its content, it does not instantiate the full page but only the contained user control.
Most of the ASP.NET features (at least the ones that I tested) are supported: viewstate, controlstate, postback events, validators... Moreover, one of the cool features of this control is allowing
UserControlto do cross-domain Ajax postback.
Background
When I wanted to put some Ajax in my ASP.NET project, I started by looking at the Microsoft Ajax Toolkit. However, one drawback of theUpdatePanelis that when it refreshes its content, it instantiates not only the
UpdatePanelbut also the entire page. It's great in some cases when you have several parts of the page interacting. However when your
UpdatePanelis independent (a menu for example) from the main content and your page is heavy, the side effect is that it takes ages to refresh only a small part of your page.
I could have used some webservice to update my content, but I don't like to output HTML from a C# function and it's not easy to design content this way. Microsoft gave us the user controls which are great to design and code: so I started to think of a kind of an
UpdatePanelwhich will host a
UserControland allow it to be refreshed without reloading the whole page.
The main difficulty was to allow a developer to Ajaxify a
UserControlwithout any code change.
Implementation
Here is a quick overview of how the Control is implemented and how it works:After the initial page request, the client receives a standard HTML page
When the client fires a postback event on a control hosted by the
DynamicUserControl, the event is trapped by some JavaScript code and redirected to the
DUCHandler
On the server side, the
DUCHandlerinstantiates a dummy Page containing only the
DynamicUserControland the custom
UserControl
From the
UserControlpoint of view, it's a standard page postback
The output result is then sent back to the client, and the browser updates only the
DynamicUserControlwhich fired the postback
Note: All other postback events fired from outside the
DynamicUserControlare handled by the Page handler
I will try to explain some of the hacks I used to make it work on the server and client side.
Server Side
The main difficulty in this part was to override the mechanism used to handle the viewstate, the controlstate and all the internal stuff of the Framework. Unfortunately, most of the ASP.NET classes are internal or sealed (or both), which makes it difficult to plugin.The solution (hack) was to use the reflection API to access all these methods and members. The drawback of using reflection is that the code is really dependent on the Framework version and that it could break upon any minor update of it.
The
DUCHandleris really just a wrapper around the Page handler: It creates a page containing only a
DynamicUserControlwith the
UserControlinside. However there are some noticeable hacks:
The
DUCPageWrapper: which overrides the
FindControlmechanism of the
Page. It allows the internal control to have the same ID as in the full page control tree but without all the surrounding controls.
The
DUCScriptManagerWrapper: which overrides the
RegisterArrayDeclarationneeded by controls using script arrays (especially the validators).
The
DUCRedirectModule: which intercepts the redirection response before sending it to the client (i.e. It allows the
DynamicUserControlto handle the
Response.Redirectmethod).
Client Side
This is really the heart of theDynamicUserControl.
The first thing the script does is to override the
__doPostBackfunction. So when the client fires a postback event, the code checks if it's originating from inside a
DynamicUserControl. In this case, the script parses the control to find the form's inputs that it contains. Then it makes a
POSTrequest with all the data to the
DUCHandlerand replaces the control content with the HTML output.
One of problems I had was integrating the script codes and script includes outputted by the
DUCHandlerinside the existing page: simply putting the HTML tags in the
innerHTMLwas not sufficient. I had to create the script includes elements the DOM way (i.e. using
document.createElement) and call
window.evalwith some delay to correctly evaluate the JavaScript blocks in the current page scope. (see
DynamicUserControl._updateContainer()and
DynamicUserControl._scriptExecutor()in the DynamicUserControl.js file).
Also, to allow the control to post data to another sub-domain, I needed to use a proxy iframe. Using the iframe proxy was the solution to bypass browser security both in IE and Firefox. You can see how it works by looking at the
DynamicUserControl._doPostBackXSS()function in the JS file and you can compare it with the standard way of posting data using
XmlHttpRequestin the
DynamicUserControl._doPostBackXHR()function.
Using the Dynamic User Control
In order to use this control, your project must be in ASP.NET 2.0 and must reference the Microsoft Ajax extensions DLL.Here is the configuration to put in your Web.config file:
Collapse
Copy Code
<pages> <controls> ... <add tagPrefix="jp" namespace="DUCExtension" assembly="DUCExtension"/> </controls> </pages> ... <httpHandlers> <add verb="*" path="*.duc" type="DUCExtension.Modules.DUCHandler, DUCExtension" /> </httpHandlers> ... <httpModules> <add name="DUCRedirect" type="DUCExtension.Modules.DUCRedirectModule, DUCExtension"/> </httpModules>
If you want to enable the cross-domain postback, you also need to add the following lines:
Collapse
Copy Code
<appSettings> <add key="DUCDomain" value="mydomain.com"/> </appSettings>
Then, if you use a
UserControlthis way in your page:
Collapse
Copy Code
<%@ Register TagPrefix="uc" TagName="Test" Src="~/UserControl/Test.ascx" %> ... <uc:Test runat="server" ID="ucTest" />
you can replace your code with this:
Collapse
Copy Code
<jp:DynamicUserControlrunat="server" ID="ducTest"
UserControlPath="~/UserControl/Test.ascx"
EnablePostBackRegistration="true" >
</jp:DynamicUserControl>
You may also add a
ProgressTemplatewhich will be shown during the control update:
Collapse
Copy Code
<jp:DynamicUserControlrunat="server" ID="ducTest"
UserControlPath="~/UserControl/Test.ascx"
EnablePostBackRegistration="true" >
<ProgressTemplate>
<div>
<asp:Image runat="server" ImageUrl="~/img/ajax-loader.gif" />
</div>
</ProgressTemplate>
</jp:DynamicUserControl>
At last, here is a quick overview of the properties available:
UserControlPath: Path to the user control to instantiate
UserControl: Get the embedded user control
ProgressTemplate: Content to be shown while updating the control
UrlMap: Prefix to the user control path (used in cross-domain postback)
EnablePostBackRegistration: If set to
true, embedded controls are able to register themselves for PostBack data (i.e.
Page.RegisterRequiresPostBack)
UserControlProperties: Set embedded
UserControlproperties. Here is a sample:
Collapse
Copy Code
<jp:DynamicUserControlrunat="server" ID="ducTest"
UserControlPath="~/UserControl/Test.ascx"
EnablePostBackRegistration="true" >
<UserControlProperties>
<jp:UserControlProperty Name="TestString" Value="Toto"/>
<jp:UserControlProperty Name="TestInt" Value="123"/>
<jp:UserControlProperty Name="TestDouble" Value="45.26"/>
</UserControlProperties>
</jp:DynamicUserControl>
(Assuming
TestString,
TestIntand
TestDoubleare public properties of the embedded
UserControl)
Limitations
I have tested it quite a lot, however I'm sure it isn't bug-free. Apart from the bugs you may find, here are some limitations you should be aware of:Event Validation is disabled for all controls inside the
DynamicUserControland MUST be disabled in some cases at the page level (i.e. by setting
EnableEventValidation="false"in the
Pagedirective)
You can't set properties on the user control in the declarative part of your pages
The
Pagecan depend on the user control embedded inside the DUC, however the user control CANNOT depend on the page it lives in!
The side effect of using an iframe is that it creates an entry in the browser history (only when using the cross-domain feature)
It was only tested under Firefox 2, IE6 SP2 and IE7
Validators Hack does not work with the new Framework 3.5 Beta 2
History
2007 October 8thFirst Release
2007 October 11th
Fixed bug with Framework 3.5 Beta 2 (2.0.50727.1378)
2007 October 30th
Added support for dynamically added stylesheet file (see this thread)
Minor correction of
_scriptExecutor(DynamicUserControl.js)
Added support for ASP.NET theme
2007 October 31
Fixed bug with Kevin's help (see this thread)
Corrected some bugs with Validators (especially with IE)
Added support for any ScriptMode in the ScriptManager
2007 November 1st
Corrected a bug on control state not loaded for dynamically created controls
2007 November 14
Fixed bug when using
Response.Redirect
Added support for Multiline textboxes
Added support for user control properties (Thanks to Kevin)
License
This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.
A list of licenses authors might use can be found here
相关文章推荐
- WinForms Controls > Examples > How to: Add New XtraForm, RibbonForm and XtraUserControl to Your Proj
- ASP.Net AJAX+userControl+js实现仿igoogle效果网站
- ASP.NET AJAX Roadmap--Server Controls (13): ScriptManager Control Overview
- 一种开发模式:ajax + ashx + UserControl
- Csharp:user WebControl Read Adobe PDF Files In Your Web Browser
- How to set bmp for your UserControl in the toolbox
- Teach Your Apps To Play Nicely With Windows Vista User Account Control(转)
- ASP.Net AJAX+userControl+js实现仿igoogle效果网站
- ASP.Net AJAX+userControl+js实现仿igoogle效果网站
- Implementing your own base class for user controls in Silverlight 2
- ASP.NET AJAX Roadmap--Server Controls (7): UpdatePanel Control Overview
- AJAX Control Toolkit: Extender controls may not be registered before PreRender
- Connection for controluser as defined in your configuration failed.
- ASP.NET AJAX Roadmap--Server Controls (9): UpdateProgress Control Overview
- 编译器错误信息: CS0030: 无法将类型“ASP.webusercontrol_treeview_ascx”转换为“System.Web.UI.WebControls.TreeView”
- Connection for controluser as defined in your configuration failed phpmyadmin xampp
- ASP.NET AJAX Roadmap--Server Controls (10): UpdateProgress Control Tutorials
- Csharp:user WebControl Read Adobe PDF Files In Your Web Browser
- ubuntu 下解决phpmyadmin error: “Connection for controluser as defined in your configuration failed”
- Connection for controluser as defined in your configuration failed.