Click here to Skip to main content
Click here to Skip to main content
Go to top

The AnywherePlaceHolder. Part 1: The Simple AnywherePlaceHolder

, 8 Sep 2005
Rate this:
Please Sign up or sign in to vote.
Eliminating the boundaries of frames. Writing your ASP.NET controls on any desired location in any frame.

Example of html page with IFrame. The .aspx page in the IFrame writes controls in it's parent

Introduction

When writing accessible websites, we are told that frames are wrong. But with web-applications for the local intranet of your company or your customers, frames can be very useful. But why is it that we ASP.NET developers rarely use frames for our web-applications? Not because we don't like frames, but because ASP.NET limits us to the page we are currently building, disallowing us to reach out of our 'frame' and altering other frames, without refreshing them constantly.

This article is part one of a trilogy, in which I present the AnywherePlaceHolder. A control that functions like a normal PlaceHolder (with children), but that enables you to render the child controls in any frame of your web-application. The control is built in the new ASP.NET 2.0 and uses generics and is deeply coupled with the ASP.NET 2.0 client side JavaScript functions.

In this first article I'll present the BaseAnywherePlaceHolder and the SimpleAnywherePlaceHolder. The SimpleAnywherePlaceHolder is only used to explain the basics of the real AnywherePlaceHolder that will be explained in part two. The SimpleAnywherePlaceHolder can write normal controls like Labels, Tables, GridViews to any desired frame.

In part two I will explain the working of the AnywherePlaceHolder that enables you to write basically any content, including submit buttons, to any other frame, with respect to their functionality. Even JavaScript code will still work.

In part three I will explain the working of the AnywhereValidationSummaryPlaceHolder. This is a BaseAnywherePlaceHolder with the functionality of a normal ValidationSummary. This special PlaceHolder allows you to write your custom messages and validation error messages into the frame of choice.

Using the code

In this part we'll be examining the BaseAnywherePlaceHolder and SimpleAnywherePlaceHolder classes. Below is the class diagram.

The class diagram

The BaseAnywherePlaceHolder is an abstract class that inherits from System.Web.UI.WebControls.Panel rather than System.Web.UI.WebControls.PlaceHolder, what its name does expect. A PlaceHolder does not render itself, but only its children and we need the control to render itself. The name PlaceHolder however, tells more intuitively what the control does. Here are the basic methods and properties of the control:

  • DestinationControlId

    Gets or sets the ID of the element in what the child controls should be inserted. When this property is specified the child elements will be inserted into the parent frame through JavaScript innerHTML function. Otherwise the child controls are normally rendered.

  • DestinationFrame

    Gets or sets the name of the frame in what the child controls should be inserted.

  • SourceControl

    Gets or sets the control in what the child controls should be inserted, when the DestinationControlId could not be found during execution on the client. The default is the current object (so the this pointer is returned).

  • SourceFrame

    Gets or sets the client side reference to the frame in what the current aspx page is that renders this control. Needed when child controls need a reference back, i.e. when a child control uses JavaScript or submits to the form.

  • JavaScriptCode

    The JavaScript code that should be registered on the client. This code inserts the child controls into another frame. A descendant could override this property to implement a different JavaScript function. The ScriptName should then also be overridden to return a different name.

  • ScriptName

    The JavaScript function that should be rendered on the client. The name of the script is defined by the JavaScriptCode property.

  • RenderMethod

    Enumeration with what the rendering method can be set. Three options:

    • RenderNormal: Renders the child controls like a normal PlaceHolder.
    • RenderInDestination: Renders the child controls in the assigned destination element.
    • RenderBoth: Renders in the destination, but leaves children visible in source.

    Default is RenderInDestination.

    • CreateScriptCall

      Creates and returns some JavaScript code that calls the JavaScript function defined in JavaScriptCode and ScriptName, using a HTML string as input and all the other properties listed above.

    • PreRenderCheck

      Checks if all properties are set correctly and throws an exception otherwise. This method should be called from within the Render method, when it's overridden by a descendant.

    • OnPreRender

      Overridden from Control.OnPreRender. This method registers the JavaScript function to the page, using the Page.ClientScript.RegisterClientScriptBlock method.

    The SimpleAnywherePlaceHolder control inherits from BaseAnywherePlaceHolder and only implements the Render method. Most of the work has been done by its base class. The Render method looks like this:

    /// <summary>Overridden.</summary>
    /// <param name="writer">The <see cref="System.Web.UI.HtmlTextWriter"/> object</param>
    protected override void Render(System.Web.UI.HtmlTextWriter writer)
    {
        // Checks if Properties are set correctly.
        this.PreRenderCheck();
    
        // Render the control
        base.Render(writer);
    
        // Check if the RenderMethod is RenderNormal. If so, than we're done.
        if (this.RenderMethod == AnywhereRenderMethodType.RenderNormal) return;
    
        // Create a HtmlTextWriter to buffer the output
        HtmlTextWriter buffer = new HtmlTextWriter(
            new StringWriter(CultureInfo.InvariantCulture));
    
        // get the output of the ChildControls in the buffer
        base.RenderChildren(buffer);
    
        // Put the buffered text into a string
        string renderedChildren = buffer.InnerWriter.ToString();
    
        // Write the call to the JavaScript function to the page.
        this.Page.ClientScript.RegisterStartupScript(
            this.GetType(),
            this.DestinationControlId,
            this.CreateScriptCall(renderedChildren),
            true
        );
    }

    The Render method just calls the base.Render() method, so that the control is rendered normally. That seems strange, when you know that the AnywherePlaceHolder should inject the HTML code into another frame. It is the JavaScript function however, that hides or shows the HTML code using the stylesheet 'display' property. Still it doesn't really seem useful to write that HTML code, when it will be hidden. Sometimes however, that HTML code is really needed. This will be shown in part 2 of the article, when explaining the complete version of the AnywherePlaceHolder.

    After calling the base.Render() method, the method will return when the RenderMode is set to RenderNormal. Otherwise a HtmlTextWriter object is created to buffer the output from the RenderChildren method. This code will be used to generate some JavaScript to inject HTML into the destination element. That code could look like this:

    AnywherePlaceHolderWriter(
        'Title', 'TitlePlaceHolder', parent, true,
        '<img src="myimage.gif" src="" />Function page dynamic title'
    );

    As you can see the call to the AnywherePlaceHolderWriter function holds some raw HTML code, packed in a JavaScript string. This HTML code has been rendered by the base.RenderChildren(buffer); line. The code for the AnywherePlaceHolderWriter is given below:

    function AnywherePlaceHolderWriter(destid, sourceid, destframe, 
        hideSource, destHtml)
    {
        var dest = document.all ? destframe.document.all[destid] : 
            destframe.document.getElementById(destid);
    
        var source = document.all ? document.all[sourceid] : 
            document.getElementById(sourceid);
    
        if (dest) {
            dest.innerHTML = destHtml;
            if (source && hideSource) 
                source.style.display = 'none';
        }
        else {
            if (!source) alert('Can not find ' + sourceid + '.');
        }
    }

    The AnywherePlaceHolderWriter code is fairly straightforward. The destid and sourceid parameters are strings that represent the ID of an HTML element and the destframe is a reference to another frame (so not a string). The destHtml contains the HTML code that should be injected into the destination element. That is done with this code: dest.innerHTML = destHtml;.

    Using the SimpleAnywherePlaceHolder

    Using the SimpleAnywherePlaceHolder is rather simple, but you must take care of both frames during design. Look at the following examples:

    Destination.aspx

    <html>
    <body>
        <div id="DestElement" />
        <iframe name="sourceframe" src="source.aspx" />
    </body>
    </html>

    Source.aspx

    <%@ Register Namespace="DeKale.WebControls" TagPrefix="kale" %>
    <html>
    <body>
        <form runat="server">
            <kale:SimpleAnywherePlaceHolder runat="server" 
                ID="TitlePlaceHolder" DestinationControlId="DestElement" 
                DestinationFrame="parent" SourceFrame="FunctionFrame" 
                RenderMethod="RenderInDestination">
                <asp:image runat="server" src="images/myimage.gif" />
                Function page dynamic title
            </kale:AnywherePlaceHolder>
        </form>
    </body>
    </html>

    As you can see, the HTML of the source.aspx contains a SimpleAnywherePlaceHolder control with the properties DestinationControlId and DestinationFrame. In this case, the destination.aspx can be reached using the browsers 'parent' property. But it is also possible that the destination.aspx is not a parent but a brother frame within a frameset. Then the DestinationFrame property should be something like: 'parent.destination'. The frameset must then use the name property to define the destination. In other words, we can always reach that other frame.

    To wrap things up

    As you saw, using fairly simply HTML injection through the innerHTML property, we are able to render a control into another frame. "But does this always work well?", you may ask: Well, no, not exactly. When you use the SimpleAnywherePlaceHolder to write buttons, links or something that contains JavaScript, this method will break. Is there a solution for this? Yes, behold the AnywherePlaceHolder in The AnywherePlaceHolder. Part 2: The Advanced AnywherePlaceHolder.!

    History

    • 22 August, 2005 - Version 1.0. Initial version.

    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

    Share

    About the Author

    DeKale
    Web Developer
    Netherlands Netherlands
    No Biography provided

    Comments and Discussions

     
    QuestionWorks w frameSet? like cross-frame? Pinmemberluissaraiva7-Mar-07 6:20 
    AnswerRe: Works w frameSet? like cross-frame? PinmemberDeKale7-Mar-07 10:15 
    GeneralRe: Works w frameSet? like cross-frame? Pinmemberluissaraiva7-Mar-07 22:47 
    GeneralRe: Works w frameSet? like cross-frame? PinmemberDeKale8-Mar-07 10:20 
    QuestionWorks for .NET 1.1? Pinmemberhbdtnt13-Sep-05 4:50 
    AnswerRe: Works for .NET 1.1? PinmemberDeKale19-Sep-05 7:21 
    GeneralCurious about frames PinmemberRay Cassick8-Sep-05 18:43 
    GeneralRe: Curious about frames PinmemberAnna-Jayne Metcalfe9-Sep-05 3:44 

    General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

    Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

    | Advertise | Privacy | Mobile
    Web04 | 2.8.140916.1 | Last Updated 8 Sep 2005
    Article Copyright 2005 by DeKale
    Everything else Copyright © CodeProject, 1999-2014
    Terms of Service
    Layout: fixed | fluid