Frederik Vig – ASP.NET developer

Follow me

Archive for May 2009

CSS tricks – part 1

Here are a few CSS tricks I’ve picked up along the way.

Always visible vertical scrollbar

When you have a fixed width centered web site open you’ve probably noticed that the page sometimes jumps a little. This is because the vertical scrollbar only is visible if the content is longer than the viewport, if no vertical scrollbar is visible in the browser. To always have the vertical scrollbar visible you can use the CSS hack below.

html {
    overflow-y: scroll;	
}

CSS reset styles

To make our lives as web developers easier we have a few css reset style rules which will help make things more consistent across browsers. It started out with simply setting the padding and margin of all elements to 0 with this CSS code.

* {
    margin: 0;
    padding: 0;
}

Though this caused a few problems with form elements. After a few years of using the technique above I started using a CSS reset stylesheet.

100% min-height

#wrapper {
    min-height: 600px;
    background-color: #000;
}
* html #wrapper {
    height: 600px;
}

Internet Explorer 6 has problems understanding the min-height property. Therefor I use the star selector hack to target IE6 and set the height property (IE6 uses the height property the same way as min-height, so if the content is bigger the page will still grow, like with min-height).

More information about the min-height property

Star selector hack

Applying * html {selector here} will only target Internet Explorer 5.5 and 6. Which makes this a nice way to still use valid CSS and only target those two browsers. Like in the min-height example above.

More information: Workarounds and Filters

Internet Explorer 6 double margin bug

When setting the float property to either left or right and having a margin set in the same direction as the float (either margin-left or margin-right) will give IE6 users twice the margin.

#sidebar {
    float: left;
    margin-left: 10px;
}

This will give IE6 users a left margin of 20px. Fortunately it is easy to fix. Simply add the display property and set it to inline.

#sidebar {  
    float: left;
    margin-left: 10px;
    display: inline;
}

This has no negative effect on other browsers. Also, when floating inline elements (a, span, em, etc), the element automatically becomes the same as when using display:block.

More on floats

hasLayout in Internet Explorer

“Layout” is an IE/Win proprietary concept that determines how elements draw and bound their content, interact with and relate to other elements, and react on and transmit application/user events.

This quality can be irreversibly triggered by some CSS properties. Some HTML elements have “layout” by default.

Microsoft developers decided that elements should be able to acquire a “property” (in an object-oriented programming sense) they referred to as hasLayout, which is set to true when this rendering concept takes effect.

Source: satzansatz.de

I mostly come across this when I have no width or height applied. Like in the code below.

#wrapper {
    overflow: hidden;
}
#sidebar {
    float: left;
    width: 200px;
}
#content {
    float: left;
    width: 600px;
}

Overflow: hidden will clear the floats in all browsers except for in IE6. I could fix this by giving #wrapper a width or by floating it. But that is not always possible. Instead I can use the star selector and give IE6 a height of 1% to just trigger hasLayout.

#wrapper {
    overflow: hidden;
}
* html #wrapper {
    height: 1%;
}
#sidebar {
    float: left;
    width: 200px;
}
#content {
    float: left;
    width: 600px;
}

More information on hasLayout

Displaying a pointer cursor for button, label and select elements

I always add this code when I start a new project.

input[type=submit], button, label, select { 
    cursor: pointer; 
}

Just to make it easier for users to see where they can click.

EPiServer filter – part 1

Often we need to filter some of the pages in our PageDataCollection. Perhaps we wish to only show the pages that are published or that the user has access to. For this EPiServer has a few filter classes. Below you’ll find some examples. More information in the EPiServer SDK under the EPiServer.Filter namespace

To start we create a PageDataCollection of the current pages children

var news = GetChildren(CurrentPage.PageLink);

Remember to add using EPiServer.Filters; to use the filter classes.

Remove pages that are not published

EPiServer 4

new FilterPublished().Filter(this, new FilterEventArgs(news));

EPiServer 5

new FilterPublished(PagePublishedStatus.Published).Filter(news);

More information about the PagePublishedStatus enum in the SDK.

Filter by both published and read access

EPiServer 5

FilterForVisitor.Filter(news);

Filter by access level

If the user does not have at least Edit access the page will be removed.

EpiServer 5

// using EPiServer.Security;
new FilterAccess(AccessLevel.Edit).Filter(news);

More information about the AccessLevel enum in the SDK.

Sort the collection after a property value

EPiServer 5

// Default is Ascending
new FilterPropertySort("PageName").Filter(news);
new FilterPropertySort("PageName", FilterSortDirection.Descending).Filter(news);

Filter out pages where a property has no value

EPiServer 5

new FilterRemoveNullValues("MainBody").Filter(news);

Filter by a custom property

EPiServer 5

new FilterCompareTo("PageVisibleInMenu", "true").Filter(news);

In the EPiServer SDK there is a nice list of EPiServer properties.

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>

Code behind:

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.

Master Pages and EPiServer CurrentPage property

When you use user controls or web forms in your EPiServer web application you usually inherit from either UserControlBase or TemplatePage. Which gives you, among other things, the CurrentPage property. The problem is when you need the CurrentPage property in your Master Page.

The way I usually fix this is with this code.

var page = (PageBase) Page;
...
page.CurrentPage.PageName;
page.CurrentPage.LinkURL;
...

This should work fine in both EPiServer 4 and 5.

EPiServer Link Collection Property

In EPiServer CMS 5 R2, EPiServer added a new property called the link collection. This 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);

Measuring JavaScript performance

According to Response Time overview, the response time for JavaScript code to execeute should be no more than 0.1 seconds (100 milliseconds). Especially on JavaScript/Ajax heavy sites this can be a problem. That’s why it is always good thing to check the performance of your code.
To measure your JavaScript you can use the code below:

function somefunction() {
    var start = new Date().getMilliseconds();
    // code here
    var stop = new Date().getMilliseconds();
    var executionTime = stop - start;
    console.log("Execution time " + executionTime +
        " milliseconds"); // or alert("Execution time " + executionTime + " milliseconds");
}

Note that Firebug for Firefox also has a code profiler which does the same thing. To use it install Firebug, enable firebug (f12), activate the console window, press the profile button, refresh the page, click the profile button again (more information: Firebug JavaScript Debugger and Profiler).
Firebug profiler

Transparent PNGs

This is mostly for my own reference, but hopefully useful for others as well. I always forget to add a pngfix for Internet Explorer 6 when using transparent PNGs.
There are a few fixes out there.

The one I’ve mostly used is SuperSleight – mostly because it is so easy to setup. You only have to download the JavaScript file and transparent gif, include it in your project, update the path to the transparent gif in the JavaScript file, and then include it inside the head section of your page with IE conditional comments.

<!--[if lte IE 6]>
<script type="text/javascript" src="supersleight-min.js"></script>
<![endif]-->

If you’re using jQuery there is also a jQuery version of SuperSleight. You include it in the same way as the regular JavaScript version, though remember to add the jQuery library file before. Then you can call it like this.

// Checks that the SuperSleight plugin is loaded
if (jQuery().supersleight) {
    // Here you can use regular jQuery selectors to select where you want to add SuperSleight
    $('body').supersleight();
}

EPiServer Extension Methods

Here are some EPiServer extension methods I’ve started to use in my projects (remember that you need to use .NET 3.5).

Update

These, and more have been added to the EPiServer World Community project EPiCode.Extensions on EPiCode. Be sure to check it out!

IsValue

public static bool IsValue(this PageData page, string propertyName)
{
    var propertyData = page.Property[propertyName];
 
    return (propertyData != null && !propertyData.IsNull);
}

We can now use it on every PageData object.

// using MyNamespace;
if (eventPage.IsValue("Heading"))
{
    ltlEventHeading.Text = eventPage["Heading"].ToWebString();
}

IsPublished

public static bool IsPublished(this PageData page)
{
    return page.StopPublish > DateTime.Now && page.StartPublish < DateTime.Now;
}

Or better yet, taken from the EPiServer.dll:

public static bool IsPublished(this PageData page)
{
    return CheckPublishedStatus(page, PagePublishedStatus.Published);
}
 
public static bool CheckPublishedStatus(PageData pageData, PagePublishedStatus status)
{
    if (status == PagePublishedStatus.Ignore)
    {
         return true;
    }
 
    if (pageData.PendingPublish)
    {
         return false;
    }
 
    if (pageData.Status != VersionStatus.Published)
    {
         return false;
    }
 
    if ((status >= PagePublishedStatus.PublishedIgnoreStopPublish) && (pageData.StartPublish > Context.Current.RequestTime))
    {
        return false;
    }
 
    if ((status >= PagePublishedStatus.Published) && (pageData.StopPublish < Context.Current.RequestTime))
    {
        return false;
    }
 
    if (pageData.LinkType != PageShortcutType.FetchData)
    {
        return true;
    }
 
    var data = pageData.Property.Get("PageShortcutLink");
    if (data == null)
    {
        return true;
    }
 
    var pageLink = (PageReference)data.Value;
    return (!PageReference.IsValue(pageLink) || CheckPublishedStatus(DataFactory.Instance.GetPage(pageLink), status));
}

Heading

public static string Heading(this PageData page)
{
    if (page.IsValue("Heading"))
    {
	return page["Heading"].ToWebString();
    }
 
    return page.PageName.ToWebString();
}

ToWebString (from EPiServer.dll using reflector)

public static string ToWebString(this object obj)
{
    string[] parsedUISafeHtmlTags = ParsedUISafeHtmlTags;
    string input = obj.ToString().Replace("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;");
    if (((parsedUISafeHtmlTags != null) && (parsedUISafeHtmlTags.Length != 0)) && ((parsedUISafeHtmlTags.Length != 1) || (parsedUISafeHtmlTags[0].Length != 0)))
    {
	// using System.Text.RegularExpressions;
	return Regex.Replace(input, BuildRegularExpression(parsedUISafeHtmlTags), "<$1>", RegexOptions.IgnoreCase);
    }
    return input;
}
 
private static string BuildRegularExpression(string[] safeTags)
{
    return ("&lt;(/?(" + string.Join("|", safeTags) + @")/?\s*)&gt;");
}
 
private static string[] ParsedUISafeHtmlTags
{
    get
    {
	// using EPiServer.Configuration;
	string uISafeHtmlTags = Settings.Instance.UISafeHtmlTags;
	if (!string.IsNullOrEmpty(uISafeHtmlTags))
	{
	    return uISafeHtmlTags.Split(new char[] { ',' });
	}
	return new string[0];
    }
}

Safely Cast from bool? to bool

This is a little code snippet for when you need to cast a nullable type to its value type.

if (startPage.ShowMainFeatured.HasValue)
{
   // Safe to cast
   if ((bool) startPage.ShowMainFeatured)
   {
       mainfeatured.Visible = true;
   }
}

© Copyright Frederik Vig. Based on Fluid Blue theme