XP Style Navigation Bar Server Control with collection property and embedded resources
by Navaneeth.K.N
May 11th 08
This article explains how to create an ASP.NET server control which has a collection property and uses embedded web resources. In this, we will be creating an XP look and feel navigation server control.
Introduction
This article will focus on:
- How to create a server control with collection property
- Show collection editor on the designer for collection property
- Implement Web Resources concept
Here is a preview of the control rendered on "Internet Explorer 6.0".
Why this Article?
Why am I writing this control? Creating server control with properties is not at all a tough job. But creating a control which has a collection property requires a few steps. I have not found any good examples that manage with collection properties and design time support. This article focuses on creating collection classes and adding the collection items from Visual Studio Designers collection editor.
A custom server control should be always easy to use like other ASP.NET controls. Many custom controls use JavaScripts and images, and instruct the users to copy those to a location on the server to get the control working. In this article, I will explain how this can be avoided by embedding resources in the Assembly.
Introduction to ASP.NET Server Controls
This article is not a step by step explanation of creating a server control. That has been written by several writers before. If you are reading this article without any prior knowledge of ASP.NET Custom Server Controls, I refer you to read
this before you continue reading this article.
ASP.NET allows developing custom reusable controls. These controls are inherited from
System.Web.UI.WebControl. When a new control library project is created,
Render() method in
WebControl class will be overridden. ASP.NET processes each control in the page and asks them to render by calling the
Render() method of control.
Render() method takes
HTMLTextWriter object and writes rendered HTML to the response. In this example, I have also overridden the
OnInit() method to register the startup client side scripts.
Class Diagram
NavigationBarItems Collection Class
Collection class holds similar types of objects which are accessible using an index. .NET allows different methods to create collection classes.
NavigationBarItemsCollection class holds
NavigationBarItem objects. This collection class implements
ICollection and
IList interfaces, which forces a class to implement collection functionality. Objects are kept inside an
ArrayList. The following code snippet shows common functions used in this collection class.
Code |
public int Add(NavigationBarItem item) { return this._Items.Add(item); }
public void Clear() { this._Items.Clear(); }
public void RemoveAt(int index) { this._Items.RemoveAt(index); }
public int Count { get { return this._Items.Count; } }
|
To make collection members accessible through an index, the following property is used:
Code |
public NavigationBarItem this[int index] { get { return (NavigationBarItem)this._Items[index]; } set { this._Items[index] = (NavigationBarItem)value; } }
|
This enables a programmer to access
NavigationBarItem object from the collection as follows:
Code |
NavigationBarItem item = NavigationBar1.Items[1];
|
Setting Collection Class to Property
Code |
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content), Editor(typeof(System.ComponentModel.Design.CollectionEditor), typeof(System.Drawing.Design.UITypeEditor)), PersistenceMode(PersistenceMode.InnerDefaultProperty), MergableProperty(false)] public NavigationBarItemsCollection Items { get { return _Items; } }
|
Simply setting collection class to property won't work correctly in VSDesigner. It will show
System.Object when a new item is added through the collection editor. But using code this can be added.
To add items through design view, we need the below attributes to be specified:
Code |
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content), Editor(typeof(System.ComponentModel.Design.CollectionEditor), typeof(System.Drawing.Design.UITypeEditor)), PersistenceMode(PersistenceMode.InnerDefaultProperty), MergableProperty(false)]
|
DesignerSerializationVisibility attribute is used to serialize a property on design time. For more information on the attribute, please check
MSDN documentation.
PersistenceMode is another attribute which tells how a property should be on a page.
PersistenceMode.InnerDefaultProperty tells ASP.NET to persist the items as inner tags.
To set this, a class that inherits
ControlDesigner is needed.
ControlDesigner is the base class which helps to extend design time behavior of a web control.
NavigationBarDesigner is a derived class from
ControlDesigner. The following snippet shows the overridden
Initialize() method.
Code |
public override void Initialize(System.ComponentModel.IComponent component) { base.Initialize (component); this._NavigationBar = (NavigationBar)component; }
|
NavigationBarItemBuilder Class
This class is derived from
ControlBuilder class. Most controls in ASP.NET will be associated with
ControlBuilder class, which helps to parse both control and its inner tags.
NavigationBarItemBuilder overrides methods like
AllowWhitespaceLiterals() and
HtmlDecodeLiterals().
AllowWhitespaceLiterals() parses white space in control tag content provided a true value is returned. This method always returns true value. So the functionality has been overridden to return a false value.
HtmlDecodeLiterals() is overridden to decode HTML characters found on tag content. Check the following snippet:
Code |
public override bool AllowWhitespaceLiterals() { return false; }
public override bool HtmlDecodeLiterals() { return true; }
|
Embedding Resources with Control
I have seen many custom controls with usage guidelines like:
* Copy images to images folder on server
* Copy js files to include directory on server, etc.
Embedding resources with control is a concept that helps to create a variety of server controls without these prerequisites.
Problems with Control Usage Guidelines
In ASP.NET 1.1, the external resources like style sheets, JavaScript files are deployed in a package and installed in the client machine, which stop functioning when these files are saved in the incorrect location.
WebResource concept in ASP.NET 2.0 overcame all these problems, so that, all these external resources can be embedded with the assembly and loaded at runtime. While running a page with NavigationBar control, a reference to
WebResource.axd can be seen from page's source code, which handles these embedded resources. The querystring variables represent time stamp value and requested resource name. To embed a file, set build action of the file to "Embedded resource". The following image explains how web resources are working.
Retrieving from Embedded Resource
EmbeddedResourceManager is a class which manages all embedded resources. The following snippet shows retrieving header image from an embedded resource.
Code |
public string HeaderBackground { get { string BgUrl = _NavigationBar.Page.ClientScript.GetWebResourceUrl (_NavigationBar.GetType(), "System.Web.UI.WebControls.headerbg.gif"); return BgUrl; } }
|
Page.ClientScript.GetWebResourceUrl is used to get the embedded resources URL. GetWebResourceUrl() returns the temporary URL for the resource.
Looking into AssemblyInfo.cs
This section deals with adding embedded resources details in AssemblyInfo.cs file.
Code |
[assembly: TagPrefix("System.Web.UI.WebControls", "W3Hearts")]
|
The above statement is used to set the tagprefix for the control. Here is an example for tagprefix.
Code |
<W3Hearts:NavigationBar id=NavigationBar1 runat="server"> </W3Hearts:NavigationBar>
|
Page level tag prefix can be modified by the user. When custom controls are added, Visual Studio editor generates a similar tag in the page header.
Code |
<%@ Register Assembly="NavigationBar" Namespace="System.Web.UI.WebControls" TagPrefix="W3Hearts" %>
|
The following code is used for the embedded resources:
Code |
[assembly: System.Web.UI.WebResource ("System.Web.UI.WebControls.NavigationBar.js", "text/js", PerformSubstitution = true)] [assembly: System.Web.UI.WebResource ("System.Web.UI.WebControls.headerbg.gif", "image/gif")] [assembly: System.Web.UI.WebResource ("System.Web.UI.WebControls.downarrow.gif", "image/gif")] [assembly: System.Web.UI.WebResource ("System.Web.UI.WebControls.left1.gif", "image/gif")] [assembly: System.Web.UI.WebResource ("System.Web.UI.WebControls.spacer.gif", "image/gif")] [assembly: System.Web.UI.WebResource ("System.Web.UI.WebControls.uparrow.gif", "image/gif")]
|
This specifies the resource name and its type. The resource name should start with the default namespace. This method fails when embedded resources are in subfolders.
JavaScript for Expanding/Collapsing Menu
This control uses NavigationBar.js file for JavaScript functions, which contains the function intended to Expand/Collapse navigation bar. The top and bottom arrow on the right side of the control is loaded from the embedded resource. The following example shows how to retrieve the embedded resource URL in a JavaScript function:
Code |
function Toggle(obj,NavigationBarId) { if(document.getElementById(obj).style.display == 'none') { document.getElementById(obj).style.display = 'block'; document.getElementById(NavigationBarId + "_Arrow").src = ''; } else { document.getElementById(obj).style.display = 'none'; document.getElementById(NavigationBarId + "_Arrow").src = ''; } }
|
Known Issues with this Control
* ViewState managing problem for NavigationBarItems
* Lack of binding data directly from a Datasource
Conclusion
In this article, we dealt with creating custom server control with collection property and embedded resources. Collection property holds a collection of NavigationBarItem objects which can be added from design view.
WebResource is a new concept in ASP.NET 2.0 which helps control developers to use these controls without deploying resource files separately.
Discussions
Need to discuss about this article ?
Click here