Frederik Vig – ASP.NET developer

Follow me

Archive for April 2010

Backup plan when loading the jQuery library from CDN

In most of my project I load the jQuery library from a CDN, either Google or Microsoft. This ensures that my page will load faster for my visitors, since the jQuery file will get sent to them from their nearest location, gzipped and compressed. When the visitor visits another site that use the same jQuery version from the same CDN, they don’t need to wait for their browser to download the library since it’s already in their temporary Internet files.

One of the drawbacks to this approach is when the CDN goes offline or becomes unavailable. Fortunately this has not happened to me – we should however have a backup plan!

This script will use a local copy of jQuery if jQuery is unavailable from Google in some way.

ListControl AppendDataBoundItems and DataBind

By default when using code like this, the list item will be cleared when data binding is performed.

<asp:DropDownList runat="server" ID="ddlCategories">
    <asp:ListItem>Please choose</asp:ListItem>
</asp:DropDownList>
protected override void OnLoad(EventArgs e)
{
	base.OnLoad(e);
 
	string[] categories = new[] { "C#", "ASP.NET", "EPiServer", "Umbraco", "jQuery" };
 
	ddlCategories.DataSource = categories;
	ddlCategories.DataBind();
}

Will give us.

 <select name="ctl00$MainContent$ddlCategories" id="MainContent_ddlCategories">
	<option value="C#">C#</option>
	<option value="ASP.NET">ASP.NET</option>
	<option value="EPiServer">EPiServer</option>
	<option value="Umbraco">Umbraco</option>
	<option value="jQuery">jQuery</option>
</select>

As you see “Please choose” is not there any more.

However if you set the AppendDataBoundItems property to true (default is false), the list items will be added before data binding is performed.

protected override void OnLoad(EventArgs e)
{
	base.OnLoad(e);
 
	...
	ddlCategories.AppendDataBoundItems = true;
	...
}

The markup is now correct.

<select name="ctl00$MainContent$ddlCategories" id="MainContent_ddlCategories">
	<option value="Please choose">Please choose</option>
	<option value="C#">C#</option>
	<option value="ASP.NET">ASP.NET</option>
	<option value="EPiServer">EPiServer</option>
	<option value="Umbraco">Umbraco</option>
	<option value="jQuery">jQuery</option>
</select>

Detecting Ajax requests on the server

If you use jQuery or ASP.NET AJAX you can easily detect Ajax requests on the server by simply checking for the HTTP_X_REQUESTED_WITH HTTP Header. Both libraries automatically add this to the HTTP Header when they send a request.

Here’s a little code snippet that returns true for Ajax requests.

public static bool IsAjaxRequest()
{
    return HttpContext.Current.Request.Headers["X-Requested-With"] != null && HttpContext.Current.Request.Headers["X-Requested-With"] == "XMLHttpRequest";
}

The cool thing about this is that you can use this in a base page or a HTTP Module, and customize the returned data. You could for instance return JSON or XML.

using System;
using System.Web.UI;
 
namespace MyWebApplication
{
    public class BasePage : Page
    {
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
 
            if (Helper.IsAjaxRequest())
            {
                Response.Headers.Clear();
                Response.ContentType = "application/json";
                Response.Write(json output);
                Response.End();
            }
        }
    }
}

Creating a simple image gallery with EPiServer

If you have visited sites like Smashing Magazine chances are high that you’ve seen articles with titles like “40 most used jQuery plugins”, or something similar to that. I find articles like that to be great for inspiration. You usually have a section for galleries/slideshows/carousels etc, I thought I’d implement one of those plugins in EPiServer, just to show you have simple it is.

jQuery GalleryView

GalleryView is a jQuery plugin for displaying a gallery of images. The plugin is easy to use and setup, simply download the files and include them in your project.

jQuery GalleryView files in Visual Studio

Dynamic Content

We’re going to create a dynamic content plugin that our editors can use to add a gallery to a page. Start by creating a new class and give it the name Gallery.cs. Make sure it implements the IDynamicContent interface.

I’m using the custom property Folder Browser Property from EPiCode to let the editor choose a root folder for the images. I’ve also added a user control for displaying the result to the users of the site.

The complete class looks like this.

using EPiServer.Core;
using EPiServer.DynamicContent;
using Meridium.FolderBrowserProperty.Web.UI.PropertyControls;
 
namespace EPiServer.Templates.Public.Plugins
{
    public class Gallery : IDynamicContent
    {
        protected PropertyFolderBrowser MediaFolder; 
 
        public Gallery()
        {
            MediaFolder = new PropertyFolderBrowser {Name = "Media folder"};
        }
 
        public System.Web.UI.Control GetControl(PageBase hostPage)
        {
            var gallery = hostPage.LoadControl("~/Templates/Public/Units/Gallery.ascx") as Units.Gallery;
 
            if (gallery == null)
            {
                return null;
            }
 
            gallery.Root = this.State;
 
            return gallery;
        }
 
        public PropertyDataCollection Properties
        {
            get
            {
                return new PropertyDataCollection {MediaFolder};
            }
        }
 
        public string Render(PageBase hostPage)
        {
            return string.Empty;
        }
 
        public bool RendersWithControl
        {
            get { return true; }
        }
 
        public string State
        {
            get
            {
                return MediaFolder.ToString();
            }
            set
            {
                MediaFolder.ParseToSelf(value);
            }
        }
    }
}

After you register it in episerver.config (web.config for CMS 5), you’ll be able to choose it in your sites WYSIWYG-editor.

<dynamicContent>
    <controls>
      ...
      <add description="Gallery" name="Gallery" type="EPiServer.Templates.Public.Plugins.Gallery, EPiServer.Templates.Public" />
    </controls>
</dynamicContent>

Gallery Dynamic Content in EPiServer Edit Mode

Gallery View Mode

The user control that displays the actual gallery to the users of the site.

<%@ Control Language="C#" AutoEventWireup="false" CodeBehind="Gallery.ascx.cs" Inherits="EPiServer.Templates.Public.Units.Gallery" %>
<link href="/galleryview-2.1.1/galleryview.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script src="/galleryview-2.1.1/jquery.easing.1.3.js" type="text/javascript"></script>
<script src="/galleryview-2.1.1/jquery.timers-1.2.js" type="text/javascript"></script>
<script src="/galleryview-2.1.1/jquery.galleryview-2.1.1.js" type="text/javascript"></script>
 
<asp:Repeater runat="server" ID="rptGallery">
    <HeaderTemplate>
        <ul class="gallery">
    </HeaderTemplate>
    <ItemTemplate>
    <%# SetImage(Container.DataItem) %>
 
        <li><img src="<%# Image.VirtualPath %>" alt="<%# Image.Summary.Title %>" />
            <div class="panel-overlay">
				<h3><a href="<%= Image.Summary.Dictionary["Website"] %>"><%= Image.Summary.Author %></a></h3>
				<p><%# Image.Summary.Dictionary["Description"] %></p>
			</div>
        </li>
    </ItemTemplate>
    <FooterTemplate>
        </ul>
    </FooterTemplate>
</asp:Repeater>
 
<script type="text/javascript">
    $('.gallery').galleryView({
        panel_width: 800,
        panel_height: 300,
        frame_width: 100,
        frame_height: 100
    });
</script>
using System;
using EPiServer.Web.Hosting;
 
namespace EPiServer.Templates.Public.Units
{
    public partial class Gallery : UserControlBase
    {
        public string Root
        {
            get; set;
        }
 
        protected UnifiedFile Image
        {
            get; private set;
        }
 
        protected string SetImage(object dataItem)
        {
            this.Image = dataItem as UnifiedFile;
 
            return string.Empty;
        }
 
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
 
            if (string.IsNullOrEmpty(this.Root))
            {
                return;
            }
 
            var rootFolder = System.Web.Hosting.HostingEnvironment.VirtualPathProvider.GetDirectory(this.Root) as UnifiedDirectory;
 
            if (rootFolder == null)
            {
                return;
            }
 
            this.rptGallery.DataSource = rootFolder.Files;
            this.rptGallery.DataBind();
        }
    }
}

Result

The gallery in EPiServer View Mode

Note on Folder Browser Property

I’ve added an EPiServer CMS 6 version of Folder Browser Property to EPiCode.

Usage

  1. SVN Checkout https://www.coderesort.com/svn/epicode/Meridium.FolderBrowserProperty/6.x/ (requires registration on EPiCode)
  2. Open up the project in Visual Studio and build
  3. Copy Meridium.FolderBrowserProperty.dll to your sites bin folder (remember to add a reference in your Visual Studio project as well)
  4. Create the folders “<Site root>\Meridium\FolderBrowserProperty\Dialogs”, and copy the file FolderBrowserDialog.aspx into it

Hope this helps.

EPiServer File Manager and File Summary

One hidden little feature that is nice to know about is the filesummary.config file. When you edit a file in EPiServer’s File Manager, you can edit the data for that file (simply right-click on the file and choose Edit File Summary).

Edit File Summary in EPiServer Edit Mode

Which gives us this form.

Edit File Summary in EPiServer Edit Mode form

This is the standard form that comes with EPiServer.

FileSummary.config

We can easily change the form by editing FileSummary.config. If we open it in a source code editor, the code looks something like this.

<root xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <model>
    <instance>
      <Author />
      <DocumentType />
      <Category />
      <Publisher />
      <Description />
    </instance>
  </model>
  <table id="id_matrix" border="0">
    <tr>
      <td class="EP-tableCaptionCell">
        <label for="id_field2" id="id_field1" style="margin-bottom: 10px;">Author</label>
      </td>
      <td valign="top">
        <xforms:input ref="Author" value="" id="id_field2" size="40" class="episize240" />
      </td>
    </tr>
    <tr>
      <td class="EP-tableCaptionCell">
        <label for="id_field13" id="id_field6">Publisher</label>
      </td>
      <td valign="top">
        <xforms:input ref="Publisher" value="" id="id_field13" size="-1" class="episize240" />
      </td>
    </tr>
    <tr>
      <td class="EP-tableCaptionCell">
        <br />
        <label for="id_field12" id="id_field5">Type of document</label>
      </td>
      <td valign="top">
        <xforms:select1 appearance="full" ref="DocumentType" id="id_field12">
          <xforms:item>
            <xforms:label>Whitepaper</xforms:label>
            <xforms:value>Whitepaper</xforms:value>
          </xforms:item>
          <xforms:item>
            <xforms:label>Technote</xforms:label>
            <xforms:value>Technote</xforms:value>
          </xforms:item>
          <xforms:item>
            <xforms:label>Manual</xforms:label>
            <xforms:value>Manual</xforms:value>
          </xforms:item>
        </xforms:select1>
      </td>
    </tr>
    <tr>
      <td class="EP-tableCaptionCell">
        <br />
        <label for="id_field121" id="id_field5">Category</label>
      </td>
      <td valign="top">
        <xforms:select appearance="full" ref="Category" id="id_field121">
          <xforms:item>
            <xforms:label>Product1</xforms:label>
            <xforms:value>Product1</xforms:value>
          </xforms:item>
          <xforms:item>
            <xforms:label>Product2</xforms:label>
            <xforms:value>Product2</xforms:value>
          </xforms:item>
          <xforms:item>
            <xforms:label>Product3</xforms:label>
            <xforms:value>Product3</xforms:value>
          </xforms:item>
        </xforms:select>
      </td>
    </tr>
    <tr>
      <td class="EP-tableCaptionCell">
        <label for="id_field14">Description</label>
      </td>
      <td valign="top">
        <div id="id_field8">
          <xforms:textarea cols="37" rows="6" ref="Description" value="" id="id_field14" size="-1" />
        </div>
      </td>
    </tr>
  </table>
</root>

A combination of XML , HTML and XForm for storing the data. To add a new property we simply need to update the markup a little. Lets start by adding a new element inside model and instance.

<root xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <model>
    <instance>
      <Author />
      <Website />
      <DocumentType />
      <Category />
      <Publisher />
      <Description />
    </instance>
  </model>
...

Notice that I’ve added the <Website /> tag. We also need to insert a new row in the table with a XForm input element.

<tr>
  <td class="EP-tableCaptionCell">
	<label for="id_field31" id="id_field30" style="margin-bottom: 10px;">Website</label>
  </td>
  <td valign="top">
	<xforms:input ref="Website" value="" id="id_field31" size="40" class="episize240" />
  </td>
</tr>

If we save and go back to Edit Mode and the File Manager, the form should look something like this.

Edit File Summary in EPiServer Edit Mode update form with website field

For more information on XForm see: Developing with XForms.

Accessing the data

We can now access the property data by creating a new UnifiedFile instance of our file.

var file = System.Web.Hosting.HostingEnvironment.VirtualPathProvider.GetFile("/Global/Images/Portrait Employees/Robert.jpg") as UnifiedFile;

When we type file. in Visual Strudio, we see that we have access to a few public properties. The one we’re interested in is the Summary property.

The Summary property exposes a IUnifiedSummary type, which gives us access to properties like Author, Comments, Category etc, and more importantly a Dictionary for accessing other properties we create (like Website).

file.Summary.Dictionary["Website"]

Putting it all together

Here is a simple example that shows a picture with a caption.

<div class="featured">
	<img src="<%= Image.VirtualPath %>" alt="<%= Image.Summary.Title %>" />
	<p>
		<a href="<%= Image.Summary.Dictionary["Website"] %>"><%= Image.Summary.Author %></a>
		<em><%= Image.Summary.Dictionary["Description"]%></em>
	</p>
</div>
public UnifiedFile Image
{
	get; private set;
}
 
protected override void OnLoad(System.EventArgs e)
{
	base.OnLoad(e);
 
	this.Image = System.Web.Hosting.HostingEnvironment.VirtualPathProvider.GetFile("/Global/Images/Portrait Employees/Robert.jpg") as UnifiedFile;
}

A little CSS for making it look pretty.

.featured p
{
	background-color: rgba(0,0,0,.7);
	display: block;
	position: absolute;
	width: 100%;
	bottom: 0;
	left: 0;
	font-family: Georgia, serif;
	font-size: 1em;
	font-weight: normal;
	line-height: 1.3em;
	color: #ccc;
	margin-bottom: 0;
}
.featured
{
	width: 40%;
	overflow: hidden;
	position: relative;
}
 
.featured p a
{
	display: block;
	padding: 2px 10px 0 10px;
	font-weight: normal;
	font-style: italic;
	color: #fff;
}
.featured p em
{
	display: block;
	padding: 0 10px 2px 10px;
	font-style: normal;
	color: #e3c887;
}

And here’s the result!

Result with CSS

© Copyright Frederik Vig. Based on Fluid Blue theme