您的位置:首页 > 其它

Dynamic User Control, Ajaxify Your Controls

2009-04-03 13:33 399 查看

Introduction

This article describes a control which can host a
UserControl
. 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
UserControl
to 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 the
UpdatePanel
is that when it refreshes its content, it instantiates not only the
UpdatePanel
but also the entire page. It's great in some cases when you have several parts of the page interacting. However when your
UpdatePanel
is 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
UpdatePanel
which will host a
UserControl
and allow it to be refreshed without reloading the whole page.

The main difficulty was to allow a developer to Ajaxify a
UserControl
without 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
DUCHandler
instantiates a dummy Page containing only the
DynamicUserControl
and the custom
UserControl


From the
UserControl
point of view, it's a standard page postback

The output result is then sent back to the client, and the browser updates only the
DynamicUserControl
which fired the postback

Note: All other postback events fired from outside the
DynamicUserControl
are 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
DUCHandler
is really just a wrapper around the Page handler: It creates a page containing only a
DynamicUserControl
with the
UserControl
inside. However there are some noticeable hacks:

The
DUCPageWrapper
: which overrides the
FindControl
mechanism 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
RegisterArrayDeclaration
needed 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
DynamicUserControl
to handle the
Response.Redirect
method).

Client Side

This is really the heart of the
DynamicUserControl
.

The first thing the script does is to override the
__doPostBack
function. 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
POST
request with all the data to the
DUCHandler
and replaces the control content with the HTML output.

One of problems I had was integrating the script codes and script includes outputted by the
DUCHandler
inside the existing page: simply putting the HTML tags in the
innerHTML
was not sufficient. I had to create the script includes elements the DOM way (i.e. using
document.createElement
) and call
window.eval
with 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
XmlHttpRequest
in 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
UserControl
this 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
ProgressTemplate
which 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
UserControl
properties. 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
,
TestInt
and
TestDouble
are 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
DynamicUserControl
and MUST be disabled in some cases at the page level (i.e. by setting
EnableEventValidation="false"
in the
Page
directive)

You can't set properties on the user control in the declarative part of your pages

The
Page
can 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 8th

First 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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐