Server-side Render to Create Client-Side Snippets of code (For Handlers)

Threaded View

Previous Post Previous Post   Next Post Next Post
  1. #1

    Server-side Render to Create Client-Side Snippets of code (For Handlers)

    I posted the following feature request here: http://forums.ext.net/showthread.php...lers)&p=124113

    The idea in a nutshell is to allow the developer to use server-side code to generate client-side code that can be used by Control Listeners. Example:
        var Renderer = new ClientScript( Context );
        Renderer.Script( () => {
                // Any server-side code placed in here will render to client-side string
                X.MessageBox.Alert( "Test", "Post-2nd Click Test." ).Show();
        } );
        Button1.OnClientClick = Renderer.ToScript();
    I got a little impatient and decided to see if I could implement a simple version myself that did not require modifying any of the Ext.Net code. I changed some of the names from what I originally proposed and extended it to support all the modes of operation I described in the proposal.

    I haven't done a lot of robust testing yet but it seems to work in the sample I will post at the end. It works by swapping out the base resource manager with a temporary one bound to a SelfRenderingPage.

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Web;
    using System.Web.UI;
    using Ext.Net;
    
    
    namespace Ext.Net.Extension {
    
        /// <summary>
        /// Create Client Script string using Ext.Net Server-side api that can be attached to Control Handlers
        /// </summary>
        public class ClientScript : IDisposable {
    
            protected HttpContext Context = null;
            protected string script = null;
            protected ResourceManager internalrm = null;
            protected ResourceManager baserm = null;
    
            public ClientScript() : this( HttpContext.Current ) { }
            public ClientScript( HttpContext context ) {
                this.Context = context;
                SaveBaseRM();
            }
    
            /// <summary>
            /// Prefered method to indicate Server-side api will be scripted to string that can be used by a Control Handler.
            /// </summary>
            /// <param name="ScriptFn">Function or delegate who's internal contents will be scripted</param>
            public virtual void Script( Action ScriptFn ) {
                if( Context == null )
                    throw new Exception( "Script Rendering was already Disposed." );
                // Create the temporary rm if necessary
                InitRM();
                // Set the Current RM to the internal rm before calling script fn
                SetRM( internalrm );
                // Run the code you want to convert to script
                ScriptFn();
                // Restore Current RM to base
                SetRM( baserm );
            }
    
    
    #if SupportingStartEnd
            public bool Running { get; private set; }
    
    
            /// <summary>
            /// Begin scripting to Client-side code. All server-side api calls between this call and ScriptEnd will be rendered to a string.
            /// Make sure to call ScriptEnd() or else Dispose will be left to the Garbage-Collector and that could be dangerous.
            /// </summary>
            public virtual void ScriptStart() {
                if( Context == null )
                    throw new Exception( "Script Rendering was already Disposed." );
                // Create the temporary rm if necessary
                InitRM();
                // Set the Current RM to the internal rm before calling script fn
                Running = true;
                SetRM( internalrm );
            }
            /// <summary>
            /// End scripting to Client-side code. All server-side api calls will be rendered back to response writer.
            /// </summary>
            public virtual void ScriptEnd() {
                if( internalrm == null )
                    throw new Exception( "Script Rendering was already Started." );
                if( Context == null )
                    throw new Exception( "Script Rendering was already Disposed." );
                // Set the Current RM to the internal rm before calling script fn
                Running = false;
                SetRM( baserm );
            }
    
            /// <summary>
            /// Begin scripting to Client-side code the section inside a using statement. using( var cs = ClientScript.ScriptUsing(Context)) ) { ... }
            /// Make sure you are using a using section.
            /// </summary>
            /// <param name="Context"></param>
            /// <returns></returns>
            public static ClientScript ScriptUsing( HttpContext Context ) {
                ClientScript cs = new ClientScript( Context );
                cs.ScriptStart();
                return cs;
            }
    #endif
    
            /// <summary>
            /// Render the script to a string that can be used for Control Handlers
            /// </summary>
            /// <returns></returns>
            public virtual string ToScript() {
                // Build the script if required
                if( script == null && internalrm != null ) {
    #if SupportingStartEnd
                    // If Script was started, we need to end it
                    if( Running )
                        ScriptEnd();
    #endif
                    script = internalrm.ToScript( true );
                    Dispose();
                }
                return this.script;
            }
    
            protected void SaveBaseRM() {
                // Check if we're already storing the BaseRM
                object o = Context.Items["BaseRM"];
                if( o == null )
                    // Save the original ResourceManager
                    Context.Items["BaseRM"] = baserm = ResourceManager.GetInstance( Context );
                else
                    baserm = (ResourceManager)o;
            }
    
            protected void InitRM() {
                // If the temp rm is already initialized, exit
                if( internalrm != null )
                    return;
                // Set scope of ResourceManager to new SelfRendering Page Context
                System.Web.UI.Page pageHolder = (System.Web.UI.Page)new SelfRenderingPage();
                internalrm = new ResourceManager( true );
                internalrm.RenderScripts = ResourceLocationType.None;
                internalrm.RenderStyles = ResourceLocationType.None;
                internalrm.IDMode = internalrm.IDMode;
                pageHolder.Controls.Add( internalrm );
            }
    
            protected void SetRM( ResourceManager rm ) {
                if( Context.CurrentHandler is Page )
                    ( (Page)Context.CurrentHandler ).Items[typeof( ResourceManager )] = rm;
                else
                    Context.Items[typeof( ResourceManager )] = rm;
            }
    
            public void Dispose() {
                // Do nothing if already disposed
                if( Context == null )
                    return;
                // If script not already rendered, render it now
                if( this.script == null )
                    this.script = ToScript();
                // Release resources
                internalrm = null;
                baserm = null;
                Context = null;
            }
    
        }
    }
    If you want to support the Start / End mechanism, define SupportingStartEnd at the beginning of the code. Even though it works, I decided it was a lot less safe than using a delegate.
    Last edited by michaeld; Feb 03, 2014 at 11:22 PM.

Similar Threads

  1. Replies: 3
    Last Post: Dec 26, 2011, 1:32 PM
  2. Replies: 1
    Last Post: Dec 01, 2010, 5:14 PM
  3. Replies: 4
    Last Post: Mar 19, 2010, 11:35 AM
  4. Replies: 6
    Last Post: Sep 01, 2009, 1:06 PM

Posting Permissions