Frederik Vig – ASP.NET developer

Follow me

Archive for the ‘ASP.NET’ Category.

Add class attribute to body element with ASP.NET web forms and Master Pages

Often I have to add a class to the HTML body element. This is a little code snippet that allows you to easily do this.

<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Site1.master.cs" Inherits="WebApplication1.Site1" %>
 
<!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></title>
    <asp:ContentPlaceHolder ID="head" runat="server">
    </asp:ContentPlaceHolder>
</head>
<body runat="server" id="BodyTag">
    <form id="form1" runat="server">
    ...
    </form>
</body>
</html>

Add the public property BodyClass.

public string BodyClass
{
    set
    {
        BodyTag.Attributes.Add("class", value);
    }
}

Then in your .aspx, you need to make the Master Page strongly typed by adding MasterType (you can also manually cast it to the right type).

<%@ MasterType VirtualPath="~/Site1.Master" %>

You’ll now have access to the Master Pages BodyClass property.

Master.BodyClass = "home";
 
// Or by manually casting it
((Site1)Master).BodyClass = "home";
...
<body id="ctl00_BodyTag" class="home">
...

Adding CSS and JavaScript files dynamically to your ASP.NET page

When starting a new project I always create a Master Page that holds the content that is most used across the site. In my Master Page I have a PlaceHolder for my JavaScript files at the bottom of the page, right before the closing body tag.

<body>
<form id="form1" runat="server">
...
</form>
    <asp:PlaceHolder runat="server" ID="phdBottomScripts" />
</body>
public PlaceHolder BottomScriptsPlaceHolder
{
    get
    {
        return phdBottomScripts;
    }
}

The reason for having it at the bottom instead of the head is performance.

Now I have the ability to add JavaScript files to the bottom and CSS and JavaScript files to the head (through the pages Header property).

Lets add two methods for adding CSS and JavaScript files.

// using System.Web.UI.HtmlControls;
public static HtmlLink CreateCssLink(string cssFilePath, string media)
{
	var link = new HtmlLink();
	link.Attributes.Add("type", "text/css");
	link.Attributes.Add("rel", "stylesheet");
	link.Href = link.ResolveUrl(cssFilePath);
	if (string.IsNullOrEmpty(media))
	{
		media = "all";
	}
 
	link.Attributes.Add("media", media);
	return link;
}
 
public static HtmlGenericControl CreateJavaScriptLink(string scriptFilePath)
{
	var script = new HtmlGenericControl();
	script.TagName = "script";
	script.Attributes.Add("type", "text/javascript");
	script.Attributes.Add("src", script.ResolveUrl(scriptFilePath));
 
	return script;
}

The media attribute is to specify which media types the CSS file should be applied to.

Media types

  • all
  • braille
  • embossed
  • handheld
  • print
  • projection
  • screen
  • speech
  • tty
  • tv

Example

protected override void OnLoad(EventArgs e)
{
	base.OnLoad(e);
 
	Page.Header.Controls.Add(Helper.CreateJavaScriptLink("~/Scripts/swfobject.js"));
	Page.Header.Controls.Add(Helper.CreateCssLink("~/Styles/styles.css", "screen"));
}
<head>
...
<script type="text/javascript" src="/Scripts/swfobject.js"></script>
<link type="text/css" rel="stylesheet" href="/Styles/styles.css" media="screen" />
</head>

To add JavaScript files to the bottom we need to add MasterType to our .aspx file to make my Master Page strongly typed (and thus able to access the BottomScriptsPlaceHolder property).

<%@ MasterType VirtualPath="~/MasterPages/MasterPage.master" %>
Master.BottomScriptsPlaceHolder.Controls.Add(Helper.CreateJavaScriptLink("~/Scripts/master.js"));
...
<script type="text/javascript" src="/Scripts/master.js"></script>
</body>

Other methods

Response.Write method

<link href="<%= ResolveUrl("~/Styles/Styles.css") %>" rel="stylesheet" type="text/css" />

ClientScriptManager

From MSDN.

The ClientScriptManager class is used to manage client scripts and add them to Web applications

Methods of the ClientScriptManager class.

ResolveUrl and ResolveClientUrl

Simply put, the difference between these two methods is that ResolveUrl will create a path from the sites root (/Styles/Styles.css), while ResolveClientUrl will create a path that is relative from the page the user is on. (../Styles/Styles.css).

ASP.NET web forms and jQuery Thickbox plugin

Update

Thickbox is no longer supported. Use another of jQuery’s overlay/lightbox plugins. This post will remain for legacy purposes only.

I’m very found of jQuery, and use the Thickbox plugin a lot in my project. I’ve not used it much for images, but rather for content. To retrieve content you have three options.

  • Inline content
  • Iframed content
  • Ajax content

Inline content

This is pretty straight forward, you have some content on the same page that you’d like to display inside the Thickbox. This one I use a lot, since it is easy to make accessible for search engines, screen readers and other devices. You simply hide the content with JavaScript (in jQuery you can use the hide function: $(selector).hide();), and then trigger opening the content inside the Thickbox with either a link or button.

<a href="#TB_inline?height=155&amp;width=300&amp;inlineId=contentId" class="thickbox">Show hidden content.</a>

Iframe content

<a href="Default.aspx?keepThis=true&amp;TB_iframe=true&amp;height=300&amp;width=500" title="thickbox title" class="thickbox">Iframe content</a>

The nice thing about this is that you can easily include existing pages inside the Thickbox. Another thing to note is that users without JavaScript, like search engines, still can navigate the content, since the a element’s href attribute points to the page. This is also the easiest way to still have functionality that you have inside your ASP.NET web form page like Postbacks, sessions etc.

Ajax content

Like with iframed content people without JavaScript will still be able get to the content.

<a href="ajaxContent.aspx?height=300&amp;width=300" title="thickbox title" class="thickbox">Ajax conten</a>

The one problem I’ve had in the past with this method is when posting data back to the server with ASP.NET web forms (same problem with inline content). Until recently I always used iframed content when having to deal with form controls. The reason being that the content inside the Thickbox got added outside the form element on the page (right before the closing body tag).

Thickbox code right before closing body tag

To fix this you need to change this line of code inside the thickbox.js file.

$("body").append("<div id='TB_overlay'></div><div id='TB_window'></div>");

Add form:first to the selector to target the first form element on the page.

$("body form:first").append("<div id='TB_overlay'></div><div id='TB_window'></div>");

Thickbox code inside the form tag now

Now the form controls will work and you’ll be able to have the user fill out the form inside the Thickbox and post it back to the server.

The Thickbox is also very easy to style and extend, below are some screen shots of sites where I’ve used it.

thickbox screen shot

Thickbox screen shot

Other resources

Using multiple forms on an ASP.NET web forms page

I came across this simple technique while watching Building great standards based websites for the big wide world with ASP.NET 4.0.

The cool thing about being able to have multiple forms on the same page is that you don’t have to use JavaScript to fix the default button problem, or use unnecessary ASP.NET web controls or logic on the server (like the Response.Redirect method). The drawback, for some, is that you cannot have the forms inside the form (web form) with the runat=”server” attribute, which means that you cannot use all of the ASP.NET web controls.

Example

Three forms on an ASP.NET web forms page

Default.aspx

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_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></title>
</head>
<body>
    <form id="form1" runat="server">
    <fieldset>
    <legend>ASP.NET web form (POST)</legend>
        <asp:Label runat="server" AssociatedControlID="txtSearch">Name: </asp:Label><asp:TextBox runat="server" ID="txtSearch" /><asp:Button runat="server" ID="btnSend" Text="Search" OnClick="btnSend_Click" />
    </fieldset>
    </form>
    <form method="get" action="Search.aspx">
        <fieldset>
        <legend>Regular HTML form using GET</legend>
            <label for="name-text">Name: </label><input type="text" id="name-text" name="q" /><input type="submit" value="Search" />
        </fieldset>
    </form>
    <form method="post" action="Search.aspx">
        <fieldset>
        <legend>Regular HTML form using POST</legend>
            <label for="name-text2">Name: </label><input type="text" id="name-text2" name="q" /><input type="submit" value="Search" />
        </fieldset>
    </form>
</body>
</html>
using System;
 
public partial class _Default : System.Web.UI.Page
{
    protected void btnSend_Click(object sender, EventArgs e)
    {
        Response.Redirect(string.Format("Search.aspx?q={0}", txtSearch.Text));
    }
}

Search.aspx

Displaying result of multiple forms on ASP.NET web forms page

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Search.aspx.cs" Inherits="Search" %>
 
<!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></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:Label runat="server" ID="lblResult" />
    </div>
    </form>
</body>
</html>
using System;
using System.Web;
 
public partial class Search : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!string.IsNullOrEmpty(Request.QueryString["q"]))
        {
            lblResult.Text = HttpUtility.HtmlEncode(Request.QueryString["q"]);
            return;
        }
 
        // For the regular html form POST method
        lblResult.Text = HttpUtility.HtmlEncode(Request.Form["q"]);
    }
}

ASP.NET Validation and jQuery

Recently I was working on a simple form that used the ASP.NET Validation controls for validation. This worked fine when doing a regular Postback to the server with the data. However I wanted to use Ajax to make it a little faster and the user experience better.

One of the problems I had was that when I hooked into my buttons click event with jQuery I needed to make sure the form validated before sending it to my web method. That’s when I came across this little piece of JavaScript.

if (Page_IsValid) {
}

Which does the same thing as Page.IsValid on the server

Below is some simple sample code.

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_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></title>
 
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" type="text/javascript"></script>
 
    <style type="text/css">
        fieldset em { color: Red; }
    </style>
</head>
<body>
    <form id="form1" runat="server">
    <fieldset>
        <p>
            <asp:Label runat="server" AssociatedControlID="txtName">Name: <em title="Required">*</em></asp:Label>
            <asp:TextBox runat="server" ID="txtName" ValidationGroup="Form" />
            <asp:RequiredFieldValidator runat="server" ControlToValidate="txtName" Text="Name is missing" ValidationGroup="Form" />
        </p>
        <p>
            <asp:Button runat="server" ID="btnSend" Text="Submit" ValidationGroup="Form" OnClick="btnSend_Click" />
        </p>
    </fieldset>
    <p id="result">
            <asp:Literal runat="server" ID="ltlResult" />
        </p>
    </form>
 
    <script type="text/javascript">
        $("#<%= btnSend.ClientID %>").click(function() {
            if (Page_IsValid) {
                var name = $("#<%= txtName.ClientID %>").val();
 
                $.ajax({
                    type: "POST",
                    url: "Default.aspx/YourName",
                    data: "{name:'" + name + "'}",
                    contentType: "application/json; charset=utf-8",
                    dataType: "json",
                    success: function(msg) {
                        $("#result").text(msg.d);
                    }
                });
 
                return false;
            }
        });
    </script>
</body>
</html>
using System;
using System.Web;
using System.Web.Services;
using System.Web.UI;
 
public partial class _Default : Page 
{
    protected void btnSend_Click(object sender, EventArgs e)
    {
        if (Page.IsValid)
        {
            ltlResult.Text = string.Format("Your name is: {0}", HttpUtility.HtmlEncode(txtName.Text));
        }
    }
    // Make sure to add the [WebMethod] attribute and make the method static
    [WebMethod]
    public static string YourName(string name)
    {
        return string.Format("Your name is: {0}", HttpUtility.HtmlEncode(name));
    }
}

The reason I still have the btnSend_Click method is in case the user does not have JavaScript enabled.

EPiServer Link Collection Property

The EPiServer Link Collection Property allows you to add links to web pages, documents and e-mail addresses. For a nice overview see this post: EPiServer 5 R2 and Link Collection property

In this example I’m going to show you how to use the link collection property to get a page reference to EPiServer pages and retrieve some content from them. This could be used for a related content sidebar or something similar.

Start by creating a new link collection property in admin mode to an existing page type or a new one, name it RelatedContent. Then go to edit mode and add a few links to various pages.

Now we’ll start on the page template, open up your web form and add this markup.

<asp:Repeater runat="server" ID="rptRelatedContent" OnItemDataBound="rptRelatedContent_ItemDataBound">
    <HeaderTemplate>
	<ul id="relatedContent">
    </HeaderTemplate>
    <ItemTemplate>
	<li>
	    <h3><asp:HyperLink runat="server" ID="lnkHeading" /></h3>
	    <p><asp:Literal runat="server" ID="ltlText" /></p>
	</li>
    </ItemTemplate>
    <FooterTemplate>
	</ul>
    </FooterTemplate>
</asp:Repeater>

Then in your code-behind.

protected void Page_Load(object sender, EventArgs e)
{
    if (IsValue("RelatedContent"))
    {
	// using EPiServer.SpecializedProperties;
	var relatedContent = (LinkItemCollection)CurrentPage["RelatedContent"];
 
	if (relatedContent != null)
	{
	    rptRelatedContent.DataSource = relatedContent;
	    rptRelatedContent.DataBind();
	}
    }
}
protected void rptRelatedContent_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
    if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
    {
	var linkItem = (LinkItem)e.Item.DataItem;
	var lnkHeading = (HyperLink)e.Item.FindControl("lnkHeading");
	var ltlText = (Literal)e.Item.FindControl("ltlText");
 
	if (linkItem != null && lnkHeading != null && ltlText != null)
	{
	    var url = new UrlBuilder(linkItem.Href);
 
	    // using EPiServer.Web;
	    bool isEPiServerPage = PermanentLinkMapStore.ToMapped(url);
 
	    if (isEPiServerPage)
	    {
		var page = DataFactory.Instance.GetPage(PermanentLinkUtility.GetPageReference(url));
 
		if (page != null)
		{
		    lnkHeading.Text = page["Heading"] as string ?? page.PageName;
		    lnkHeading.NavigateUrl = page.LinkURL;
 
		    ltlText.Text = GetText(page, 255);
		}
	    }
	}
    }
}

The GetText method I usually place in a static helper class. It is just to get 255 characters of text from the pages MainIntro property or if it is empty from the MainBody property:

public static string GetText(PageData page, int textLength)
{
    if (page == null)
    {
	return string.Empty;
    }
 
    var text = page["MainIntro"] as string;
 
    if (string.IsNullOrEmpty(text))
    {
	text = page["MainBody"] as string;
    }
 
    if (string.IsNullOrEmpty(text))
    {
	return string.Empty;
    }
 
    // using EPiServer.Core.Html;
    return TextIndexer.StripHtml(text, textLength);
}

You could also add it as an extension method:

// Simply add the this keyword in front of PageData
public static string GetText(this PageData page, int textLength)
{
...
}

Then you can call it like this instead:

ltlText.Text = page.GetText(255);

© Copyright Frederik Vig. Based on Fluid Blue theme