14 September 2008 AJAX, ASP.NET, ASP.NET AJAX, Dynamic, HowTo Robert Muehsig

Die Idee (und einige Codezeilen) für dieses HowTo kommt von diesem Blogeintrag der ein tolles Beispiel zeigt. Da ich mir meistens Sachen besser merken kann, wenn ich diese selber mal prototypisch umsetze, schreibe ich auf dieser Basis das HowTo.

Worum geht es?
Stellen wir uns vor, wir hätten viele UserControls geschrieben, welche Daten und Controls unterschiedlicher Art anzeigen können.
Wenn man nun allerdings eine schöne "Web 2.0" AJAX Anwendung bauen will, schickt man im einfachsten Fall JSON Daten hin und her und muss sich das Control über Javascript zusammenbauen. Das ganze ist natürlich nicht sehr wartbar (manches wird über ASP.NET gemacht, manches vielleicht über statisch eingebundene Controls und andere Sachen über Javascript) - schön wäre doch, wenn man die Controls dynamisch auf die Seite holen könnte. Und genau darum geht es heute...

Aufbau:

image

Wir haben ein sehr simples "SampleUserControl":

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="SampleUserControl.ascx.cs" Inherits="DynamicASPControls.SampleUserControl" %>
<div style="background-color: red; height: 200px; top: 200px">
    Fertig geladen \o/
</div>

Der ScriptService:

Für die Kommunikation zwischen Client und Server (und die AJAX Funktionalität) nutzen wir ASP.NET AJAX. Wie das geht und was man damit machen kann, habe ich hier bereits beschrieben.

Im ScriptService (der auch das Attribut ScriptService trägt) erstellen wir uns dynamisch den HTML Code von einem beliebigen Usercontrol:

public class ScriptService : System.Web.Services.WebService
    {
        [WebMethod(EnableSession = true)]
        public string GetControlHtml(string controlLocation)
        {
            Page page = new Page();
            UserControl userControl = (UserControl)page.LoadControl(controlLocation);
            userControl.EnableViewState = false;
            HtmlForm form = new HtmlForm();
            form.Controls.Add(userControl);
            page.Controls.Add(form);
            StringWriter textWriter = new StringWriter();
            HttpContext.Current.Server.Execute(page, textWriter, false);
            return CleanHtml(textWriter.ToString());
        }

        /// <summary>
        /// Removes Form tags
        /// </summary>
        private string CleanHtml(string html)
        {
            return Regex.Replace(html, @"<[/]?(form)[^>]*?>", "", RegexOptions.IgnoreCase);
        }
    }

Der "GetControlHtml" Methode wird die "Location" von dem Control mitgeiteilt und jetzt bauen wir uns dynamisch eine "Page" zusammen und fügen eine "HtmlForm" dazu und hängen dort das geladene Control dran.

Über "HttpContext.Current.Server.Execute" können wir nun unseren fertigen HTML Code in ein String verwandeln.

Die "CleanHtml" Methode entfernt die Form Tags am Anfang wieder - sodass wir möglichst nur noch das original Control-HTML übrig haben.

Größenbeschränkung aufheben:

Da man ja relativ viele Sachen in einem Control machen kann und das daraus resultierende HTML recht groß werden kann, müssen wir dies in der Web.Config erst freischalten:

  <system.web.extensions>
    <scripting>
      <webServices>
        <jsonSerialization maxJsonLength="5000000" />
      </webServices>
    </scripting>
  </system.web.extensions>

Fertige ASPX:

Die fertige ASPX Seite ist bewusst einfach gehalten:

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

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
    
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server">
        <Services>
            <asp:ServiceReference Path="~/ScriptService.asmx" />
        </Services>
    </asp:ScriptManager>

    <button type="button" onclick="generateUserControl()">Generate UserControl!</button>
    <div id="result">
       
    </div>
    </form>
    
        <script type="text/javascript">       
        function generateUserControl()
        {
            document.getElementById("result").innerHTML = "Load...";
            DynamicASPControls.ScriptService.GetControlHtml("~/SampleUserControl.ascx", generateUserControlCompleted);
        }
    
        function ready(result)
        {
            alert(result);
        }
    
        function generateUserControlCompleted(result)
        {
            document.getElementById("result").innerHTML = result;
        }
        
        function failed(error)
        {
            alert(error);
        }
        
    </script>
</body>
</html>

Über die JavaScript Methoden greifen wir auf den Service zu, welcher uns das HTML liefert. In der "generateUserControlCompleted" Methode schreiben wir das HTML nur noch in unser "result" div:

image
(Design made by me ;) )

Ein komplexeres Beispiel:

Ein wesentlich komplexeres Beispiel findet ihr in diesem Blogpost, sowie eine jQuery Variante in diesem Blogpost.

Wo es Probleme geben könnte:

Da wir das HTML dynamisch zur Seite hinzufügen, funktioniert sicherlich das Postback System von ASP.NET nicht (bzw. könnte ich es mir vorstellen, dass es da Probleme gibt) - für ein einfaches Control, welches ohne PostBacks entwickelt wurde, ist es aber sicherlich eine tolle Sache.

[ Download Sourcecode ]


Written by Robert Muehsig

Software Developer - from Dresden, Germany, now living & working in Switzerland. Microsoft MVP & Web Geek.
Other Projects: KnowYourStack.com | ExpensiveMeeting | EinKofferVollerReisen.de

If you like the content and want to support me you could buy me a beer or a coffee via Litecoin or Bitcoin - thanks for reading!