HoverPanel Windows

HoverPanel Windows allow you to create a window with content from a URL, with the result popping ontop of the active Web Page at the current mouse position or in a static position. Pop up elements can also be made draggable, closable and provide some basic effects like drop shadows and fades.

As with all the AJAX controls there is both a client side jQuery plug-in and a server side ASP.NET server control that can drive the the hover behavior. The client component can work on its own and the server control simply automates the client side features.

Creating Hover Windows is a very simple process. Typically you'll need to do the following:

  • Place a HoverPanelControl on the page
  • Set the EventHandlerMode to ShowHtmlAtMousePosition (or ShowIFrameAtMousePosition)
  • Set the ServerUrl to point at an external URL, or leave blank to fire back to the current page
  • Create a page or page method to create an HTML fragment to display
  • Hookup the appropriate event in the client script code (for example, onmouseover or onclick)
  • From the script call the StartCallback function
  • Pass the control, id, event object (or null) and a querystring that provides the context

Take a look at the HoverList.aspx example which is similar to the figure above. It displays Invoice detail for an invoice list.

Start by adding a HoverPanel control to the page:

        
<ww:HoverPanel id="LookupPanel" runat="server" style="display: none; background: white;" 
            NavigateDelay="400" 
            EventHandlerMode="ShowHtmlAtMousePosition"
            ServerUrl="HoverList.aspx"
            PanelOpacity=".92"  
            ShadowOpacity=".15"
            ShadowOffset="8"
            HoverOffsetRight="15"
            HoverOffsetBottom="10"
            PostBackMode="Post"
            AdjustWindowPosition="true"
>
</ww:HoverPanel>

This sets up the HoverPanel control run the callback in ShowHtmlAtMousePosition mode which shows the result content in the panel at the mouse position. The main thing you need to set here is the ServerUrl which specifies where the control goes to retrieve the content. In this sample I'll go back to the same page as the main content so in theory the ServerUrl property does not need to be set. We'll use the IsCallback property of the control in code to determine the callback status and return a custom response on this page. Most of the other properties are display related to set up the shadow and opacity of the panel.

The URL goes back to the page, but usually you'll need to dynamically change the URL to include some sort of dynamic value like an ID to look up. In this example with the Northwind database we'll use the invoice list and Invoice number. On the client side this value - usually retrieved from a list - is passed as part of the client code that initates the callback

Finally the PostBackMode is set to POST which means any form variables on the page are posted back to the server. This allows you to reference controls like you normally would on the server (ie. this.txtName.Text).

There are a number of ways that the hover events can be hooked up. One way is to hook up the event handlers in the GridView/DataGrid by hooking the onmouseover and onmouseout client side events. I do this using a template column in the example:

<asp:TemplateField  ItemStyle-HorizontalAlign="Center">
		<ItemTemplate>
               	<a href='InvoiceAbstract.aspx?id=<%# Eval("OrderId") %>' 
                	   onmouseover='ShowInvoice(event,<%# Eval("OrderId") %>);'
                	   onmouseout='HideInvoice();' >
                	<%# Eval("OrderId") %></a>
	</ItemTemplate>
</asp:TemplateField>

The event handler is hooked up to a function in the current page. Note that I pass the OrderId as a parameter - this will be our call context for retrieving the result from the server. Note you can also hook up the event using unobtrusive JavaScript by using the jQuery.hover() function, but this requires that you can retrieve the primary key somehow from the generated HTML (you can look at the sample to see how this can be done).

Next we need to add the functions to handle these client events in the same HoverList.aspx page:

<script type="text/javascript">
function ShowInvoice(event,Id)
{
    LookupPanel.startCallback(event,"id=" + Id.toString(),null,OnError);    
}    
function HideInvoice()
{
    LookupPanel.hide();
    
    // *** If you don't use shadows, you can fade out
    //LookupPanel.fadeout();
}
function OnError(Result)
{
    alert("*** Error:\r\n\r\n" + Result.message);
}
</script>

I could have added these directly into the DataGrid code, but it's much cleaner to separate functions. Note that I pass the event object explicitly as a parameter so we can use it to retrieve the last mouse position!

A single method call starts off the callback. The control creates a mapped client object with the same name as the wwHoverPanel control - in this case LookupPanel - which is a client instance of the wwHoverPanel class. The key method is startCallback which starts the event callback against the server.

I pass the event object (for the mouse position) and a query string which lets the server side code figure out what to return to hte client. You can also pass any custom POST data if you choose (null here) and a client side handler that will be notified if an error occurs. Note that the callback is asynchronous so the result is 'delayed' until the callback completes.

On the server in the ASPX page I can check for a Callback by checking the controls, IsCallback property:

   protected void Page_Load(object sender, EventArgs e)
    {
            Invoice = new busInvoice();

            if (this.LookupPanel.IsCallback)
                this.ProcessCallback();

		 // *** Normal Page Display code
            int ResultCount = Invoice.GetLastInvoices(100);
            if (ResultCount < 0)
            {
                this.ShowError(Invoice.ErrorMessage);
                return;
            }

            this.gvInvoiceList.DataSource = Invoice.DataSet.Tables["TInvoiceList"].DefaultView;
            this.gvInvoiceList.DataBind();
    }

Notice the LookupPanel.IsCallback property which lets our server side code know whether we are in a callback. Based on that option we can decide whether to process the page normally or branch off and do callback specific processing. In this case the ProcessCallback method shown below is called.

The actual code that generates the invoice detail is part of a business object which - in this case - can generate the invoice detail Html internally. For the demos this turns out to be handy because this Invoice list will be reused in many places. The purpose of the following method is to basically generate an HTML fragment which is then returned with plain Response.Write code:

protected void ProcessCallback()
{
    string Output = "";

    // *** Use 'static' Html generation in business object
    string strOrderId = Request.QueryString["Id"];

    int OrderId = -1;
    if (!int.TryParse(strOrderId, out OrderId))
        Response.End();

    // *** Notice that we can access form variables here
    // *** IF we specified to POST variables from the HoverPanel
    if (!this.chkFullInvoice.Checked)
        Output = ShowInvoiceDetail(OrderId);
    else
        Output = ShowAbstractInvoice(OrderId);

    // *** No further output
    Response.Write(Output);
    Response.End();
}

And that's it. Now when you hover over any of the links in the grid the MouseOver fires and the remote call is made and the window pops up at the current mouse position.

Note that any code you write here should return an HTML FRAGMENT only not a complete HTML page! You should return this fragment by directly writing to the Response object and then calling Response.End() to close out this request and not continue with the default processing.

Calling another Page

In the example above I call back to the same page, but you can also call back to a complete different page. This maybe useful if you want to externalize the output generation completely from the original page - often times this can be much cleaner to separate the display logic.

Make sure when you use a separate page you remember to return an HTML FRAGMENT not a complete page! Remove all HTML header content from the page and turn off themes and master page merging to avoid accidentally returning a full HTML page.

Inline HTML vs. IFRAME full HTML Documents

In the example above I use the EventHandlerMode.ShowHtmlAtMousePosition, which causes a DIV tag to hold the HTML retrieved from the callback. Because the content is displayed as part of the current document, the HTML returned must follow a few rules:
  • Should be an HTML fragment not a full HTML document (no headers, no body tag etc.)
  • No <form> tags as ASP.NET can't nest forms.

Displaying full Pages
This works fine for true informational popups, but if you want to display a fully self contained Page as opposed to a fragment you can use the IFrame EventHandlerModes. The IFrame versions like EventHandlerModes.ShowIFrameAtMousePosition cause the result to be displayed in an IFRAME. Because IFRAMEs are self contained browser windows these IFRAMEs can contain full pages including HTML Headers and their own frames. In fact these pages are fully free standing pages.

There are a few rules that govern IFRAMEs operation:

  • No data can be posted to an IFRAME - only querystrings are passed
  • In IFRAME mode the popup window is fixed in size to the size of the hoverpanel

Also note that IFRAME windows will affect the browser history so if you use the back button the IFRAME will navigate backwards.

© West Wind Technologies, 1996-2016 • Updated: 04/10/13
Comment or report problem with topic