I will like to share with you my experience on skinning the GridView. Skinning makes your web form so much cleaner. Remember to use skinning and CSS today.
If you are not sure what is a skin, just remember this:
Skin – only for ASP.NET server controls
CSS – for html
So can skin uses CSS? Yes of course, you can. What if I have a custom server control? Can I skin that as well? No you can’t. I had tried this but it didn’t work. If you can get it right, do let me know how?
Again why skin? Then I will ask you again, why use CSS? Hehe. If you never use either one of these, then take this:
Skin/CSS not only makes your codes cleaner but you are able to centralize your control’s display properties in 1 place. So it is easier to manage. Of course over the time, the skin/css file will grow in size. So in order to avoid this, you can compress it or try to avoid using comments inside it. So it makes sure the file size is smaller.
Second personalization. If you notice this is the latest trend for web development. Giving each user an access to customize the look and feel of their site or current space. Or maybe this current blog, I can choose which look and feel I want. If I choose Green, the whole look and feel will be green so is other colors.
Giving you a further example:
<codes>
Okay now back to my original topic. Skinning GridView at first looked easy, but it was not at all. This kept me wondering why? I will explain further below.
Let’s see a simple example below.
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>A basic example of skin/css</title>
<link href="App_Themes/Theme1/StyleSheet.css" rel="stylesheet" type="text/css" />
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:GridView ID="grvFundOwner" CssClass="gridStyle" runat="server" AutoGenerateColumns="False"
AllowSorting="True" AllowPaging="True" DataSourceID="sdsProduct" DataKeyNames="ProductID">
<AlternatingRowStyle CssClass="gridAlternatingItem" />
<HeaderStyle Font-Bold="True" ForeColor="Black" CssClass="gridHeader" />
<PagerStyle CssClass="gridPager" />
<Columns>
<asp:BoundField DataField="Name" HeaderText="Product Name" SortExpression="Name" />
<asp:BoundField DataField="ProductNumber" HeaderText="Product Number" SortExpression="ProductNumber" />
</Columns>
</asp:GridView>
<asp:SqlDataSource ID="sdsProduct" runat="server" ConnectionString="<%$ ConnectionStrings:AdventureWorksConnectionString %>"
SelectCommand="SELECT ProductID, Name, ProductNumber FROM Production.Product"></asp:SqlDataSource>
</div>
</form>
</body>
</html>
If you notice in the <head> I have this reference of the CSS file. Let’s see what is inside the CSS file.
StyleSheet.css
/* The overall style of the GridView */
.gridStyle
{
border-color:#A29F9F;
border:1;
width: 100%;
}
/* The header of this GridView */
.gridHeader
{
background-color: #BAB9B9 ;
font-weight:bold;
color:#000000;
}
/* One row white, another row #E6DACE */
.gridAlternatingItem
{
background-color: #E6DACE;
}
/* Paging */
.gridPager
{
background-color:#BAB9B9;
text-align:right;
}
/* Underline the selected page */
.gridPager span
{
text-decoration: underline;
}
Nothing special right. Take note, I will recommend you to use the direct code for color like #BAB9B9 instead of Blue or Red. Previously in Visual Studio .NET 2003 I am using the color naming directly and I have issues with it when I port it over to Visual Studio 2005. I am making an assumption that probably these IDE use different CSS version.
Then you ask why I even need Skin if I can do something like above. If you see properly this code:
<asp:GridView ID="grvFundOwner" CssClass="gridStyle" runat="server" AutoGenerateColumns="False"
AllowSorting="True" AllowPaging="True" DataSourceID="sdsProduct" DataKeyNames="ProductID">
<AlternatingRowStyle CssClass="gridAlternatingItem" />
<HeaderStyle Font-Bold="True" ForeColor="Black" CssClass="gridHeader" />
<PagerStyle CssClass="gridPager" />
<Columns>
<asp:BoundField DataField="Name" HeaderText="Product Name" SortExpression="Name" />
<asp:BoundField DataField="ProductNumber" HeaderText="Product Number" SortExpression="ProductNumber" />
</Columns>
</asp:GridView>
Assuming you have 5 of this same GridView in the same web form. It makes the whole page so bulky. The whole point is how to make it so much cleaner?
There is something CSS cannot do, example how do you handle the AlternatingRowStyle, HeadStyle and PagerStyle which are specific to GridView.
Let’s modify our code a bit to support skin.
Before that, I recommend you to create an ASP.NET folder called App_Themes. Inside this App_Themes folder, you create more than 1 themes. So you will be wondering why so many themes? Themes are suitable for personalization. Example you have 4 themes. Each theme represents a holiday celebration in Malaysia. Example:
App_Themes
è ChineseNewYearTheme
è HariRayaTheme
è DeepavaliTheme
è AllCultureTheme
In each Theme folder, you can have 1 skin and 1 css file. Of course you can have more than 1, but I will still recommend you have only 1 skin and 1 css to it is easier for maintenance.
So for this example, create a new skin file called “Controls.skin” under the ChineseNewYearTheme.
<asp:GridView SkinID="GeneralView" CssClass="gridStyle" runat="server" UseAccessibleHeader="False">
<AlternatingRowStyle CssClass="gridAlternatingItem" />
<HeaderStyle CssClass="gridHeader" />
<PagerStyle CssClass="gridPager" />
</asp:GridView>
There are 2 important things to note here:
SkinID – you can have this if you want to have specific controls using this Skin or not.
runat=”server” – a must to have
What if you want to have a Skin to apply all the GridView? Simple, just remove SkinID, maybe something like this:
<asp:GridView SkinID="GeneralView" AllowSorting="True" AutoGenerateColumns="False" AllowPaging="True" CssClass="gridStyle" runat="server" UseAccessibleHeader="False">
<AlternatingRowStyle CssClass="gridAlternatingItem" />
<HeaderStyle CssClass="gridHeader" />
<PagerStyle CssClass="gridPager" />
</asp:GridView>
Simple right.
Now update your Default.aspx file. 2 things to note.
a. Overwrite this code to your existing GridView code.
Old | New |
<asp:GridView ID="grvFundOwner" CssClass="gridStyle" runat="server" AutoGenerateColumns="False" AllowSorting="True" AllowPaging="True" DataSourceID="sdsProduct" DataKeyNames="ProductID"> <AlternatingRowStyle CssClass="gridAlternatingItem" /> <HeaderStyle Font-Bold="True" ForeColor="Black" CssClass="gridHeader" /> <PagerStyle CssClass="gridPager" /> <Columns> <asp:BoundField DataField="Name" HeaderText="Product Name" SortExpression="Name" /> <asp:BoundField DataField="ProductNumber" HeaderText="Product Number" SortExpression="ProductNumber" /> </Columns> </asp:GridView> | <asp:GridView ID="grvFundOwner" DataSourceID="sdsProduct" DataKeyNames="ProductID" runat="server" SkinID="GeneralView"> <Columns> <asp:BoundField DataField="Name" HeaderText="Product Name" SortExpression="Name" /> <asp:BoundField DataField="ProductNumber" HeaderText="Product Number" SortExpression="ProductNumber" /> </Columns> </asp:GridView> |
b. Add a new attribute at the Page directive
<%@ Page … StylesheetTheme="ChineseNewYearTheme" %>
Try to run your application and there will be the same output. Isn’t that much cleaner?
Then you will wonder, why I don’t put DataSourceID in the skin?
Let’s give it a try.
Update your GridView and skin file to this:
GridView in Default.aspx
<asp:GridView ID="grvFundOwner" DataKeyNames="ProductID" runat="server" SkinID="GeneralView">
<Columns>
<asp:BoundField DataField="Name" HeaderText="Product Name" SortExpression="Name" />
<asp:BoundField DataField="ProductNumber" HeaderText="Product Number" SortExpression="ProductNumber" />
</Columns>
</asp:GridView>
Controls.skin
<asp:GridView SkinID="GeneralView" DataSourceID="sdsProduct" AllowSorting="True" AutoGenerateColumns="False" AllowPaging="True" CssClass="gridStyle" runat="server" UseAccessibleHeader="False">
<AlternatingRowStyle CssClass="gridAlternatingItem" />
<HeaderStyle CssClass="gridHeader" />
<PagerStyle CssClass="gridPager" />
</asp:GridView>
Try building it, you will get this error:
Error 1 The 'DataSourceID' property of a control type System.Web.UI.WebControls.GridView cannot be applied through a control skin. C:\Users\chuawenching\Documents\Visual Studio 2005\SkinWebsite\App_Themes\ChineseNewYearTheme\Controls.skin 1
Error 2 Literal content ('<AlternatingRowStyle CssClass="gridAlternatingItem" /> <HeaderStyle CssClass="gridHeader" /> <PagerStyle CssClass="gridPager" /> </asp:GridView>') is not allowed within a 'skin file'. C:\Users\chuawenching\Documents\Visual Studio 2005\SkinWebsite\App_Themes\ChineseNewYearTheme\Controls.skin 2
Tell yourself why you need Skin and CSS at the first place. Skin and CSS are not meant to do beyond display / layout like DataSourceID.
So if you are new to skin, how can you know which attribute can be used or not? Easy, just trial and error. J
Wait, I have not talk on the advance skinning of this GridView yet. Let’s look further.
Okay what if I want to skin my BoundFields for my GridView. Since I am using a SkinID for each of my GridView, it is possible to skin the BoundFields too.
Let’s review our code again:
Default.aspx
Old | New |
<asp:GridView ID="grvFundOwner" DataSourceID="sdsProduct" DataKeyNames="ProductID" runat="server" SkinID="GeneralView"> <Columns> <asp:BoundField DataField="Name" HeaderText="Product Name" SortExpression="Name" /> <asp:BoundField DataField="ProductNumber" HeaderText="Product Number" SortExpression="ProductNumber" /> </Columns> </asp:GridView> | <asp:GridView ID="grvFundOwner" DataSourceID="sdsProduct" DataKeyNames="ProductID" runat="server" SkinID="GeneralView" /> |
Controls.skin
Old | New |
<asp:GridView SkinID="GeneralView" AllowSorting="True" AutoGenerateColumns="False" AllowPaging="True" CssClass="gridStyle" runat="server" UseAccessibleHeader="False"> <AlternatingRowStyle CssClass="gridAlternatingItem" /> <HeaderStyle CssClass="gridHeader" /> <PagerStyle CssClass="gridPager" /> </asp:GridView> | <asp:GridView SkinID="GeneralView" AllowSorting="True" AutoGenerateColumns="False" AllowPaging="True" CssClass="gridStyle" runat="server" UseAccessibleHeader="False"> <AlternatingRowStyle CssClass="gridAlternatingItem" /> <HeaderStyle CssClass="gridHeader" /> <PagerStyle CssClass="gridPager" /> <Columns> <asp:BoundField DataField="Name" HeaderText="Product Name" SortExpression="Name" /> <asp:BoundField DataField="ProductNumber" HeaderText="Product Number" SortExpression="ProductNumber" /> </Columns> </asp:GridView> |
1 line of code for the Default.aspx file. Nice. Very clean. Very impressive.
Okay what if I have a new scenario when I need to use TemplateField. How do I skin this TemplateField?
Honestly speaking, I had tested it a lot of times and I couldn’t find a way to skin it. Let’s look in details.
What if I add this piece of code inside my Default.aspx?
Old | New |
<asp:GridView ID="grvFundOwner" DataSourceID="sdsProduct" DataKeyNames="ProductID" runat="server" SkinID="GeneralView" /> | <asp:GridView ID="grvFundOwner" DataSourceID="sdsProduct" DataKeyNames="ProductID" runat="server" SkinID="GeneralView"> <Columns> <asp:TemplateField HeaderText="No" HeaderStyle-Width="25px" ItemStyle-Font-Bold="true" ItemStyle-HorizontalAlign="Center"> <ItemTemplate> <% RecordNumber++; Response.Write(((grvFundOwner.PageSize * grvFundOwner.PageIndex) + RecordNumber).ToString()); %> </ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView> |
Nothing special here. Basically what I want to achieve is to place a row number for each records. Okay off topic for a moment, some of them ask me why use this way since SQL Server 2005 already has the row_number feature?
Yes row_number works fine, but when you try to sort your column in the GridView, your rows are not in order anymore. If you know a better way to do this, let me know.
Back to topic.
There are no changes to Controls.skin file. Let’s the output below. Do apologize that I didn’t have time to take screenshot, actually I am suppose to go back to my hometown for holiday, but I feel like sharing this findings to everyone who reads my blog.
Product Name | Product Number | No |
Adjustable Race | AR-5381 | 1 |
Bearing Ball | BA-8327 | 2 |
… | … | … |
Wait a second. What happen? Why is my No column at the right hand side?
Apparently, the ASP.NET Page will read the BoundFields from the Skin file first before looking at the Default.aspx which is quite obvious to notice right.
I find no ways to solve this.
Now if you look at the TemplateField like below, and how do I skin those HeaderStyle-Width or ItemStyle-Font-Bold?
<asp:TemplateField HeaderText="No" HeaderStyle-Width="25px" ItemStyle-Font-Bold="true" ItemStyle-HorizontalAlign="Center">
<ItemTemplate>
</ItemTemplate>
</asp:TemplateField>
Let’s try this:
<asp:GridView SkinID="GeneralView" AllowSorting="True" AutoGenerateColumns="False" AllowPaging="True" CssClass="gridStyle" runat="server" UseAccessibleHeader="False">
<AlternatingRowStyle CssClass="gridAlternatingItem" />
<HeaderStyle CssClass="gridHeader" />
<PagerStyle CssClass="gridPager" />
<Columns>
<asp:TemplateField HeaderText="No" HeaderStyle-Width="25px" ItemStyle-Font-Bold="true" ItemStyle-HorizontalAlign="Center" />
<asp:BoundField DataField="Name" HeaderText="Product Name" SortExpression="Name" />
<asp:BoundField DataField="ProductNumber" HeaderText="Product Number" SortExpression="ProductNumber" />
</Columns>
</asp:GridView>
It looks normal from the code. When you run this, you see as below.
No | Product Name | Product Number | |
| Adjustable Race | AR-5381 | 1 |
| Bearing Ball | BA-8327 | 2 |
| … | … | … |
Okay that is funny. It basically adds another new column.
Maybe I should think out of the box and do this in my skin file instead.
Add this new line of code below the skin file.
<asp:TemplateField SkinID="TemplateView" runat="server" HeaderText="No" HeaderStyle-Width="25px" ItemStyle-Font-Bold="true" ItemStyle-HorizontalAlign="Center" />
Looking at it does make sense somehow. Basically <asp:TemplateFiled /> looks like an ASP.NET server control. Since skin file only accepts controls with the prefix of <asp: … />
Okay let’s try to add the SkinID in the TemplateField in Default.aspx
<asp:TemplateField>
Sigh. There is no such attribute on SkinID. Probably let me guess Intellisense problem which is common in Visual Studio 2005. I try to add in myself like this:
<asp:TemplateField SkinID="TemplateView">
Let’s build this.
Error 1 Type 'System.Web.UI.WebControls.TemplateField' does not have a public property named 'SkinID'. C:\Users\chuawenching\Documents\Visual Studio 2005\SkinWebsite\Default.aspx 15
Error 2 System.Web.UI.WebControls.DataControlFieldCollection must have items of type 'System.Web.UI.WebControls.DataControlField'. 'ItemTemplate' is of type 'System.Web.UI.HtmlControls.HtmlGenericControl'. C:\Users\chuawenching\Documents\Visual Studio 2005\SkinWebsite\Default.aspx 16
Okay maybe I can use a generic skin of <asp:TemplateField> without specifying SkinID. Let’s update this in Controls.skin file.
Old | New |
<asp:TemplateField SkinID="TemplateView" runat="server" HeaderText="No" HeaderStyle-Width="25px" ItemStyle-Font-Bold="true" ItemStyle-HorizontalAlign="Center" /> | <asp:TemplateField runat="server" HeaderText="No" HeaderStyle-Width="25px" ItemStyle-Font-Bold="true" ItemStyle-HorizontalAlign="Center" /> |
Running this will generate you an error:
Error 1 This object cannot be themed. Only controls of type System.Web.UI.Control are allowed at the top-level of a skin file. C:\Users\chuawenching\Documents\Visual Studio 2005\SkinWebsite\App_Themes\ChineseNewYearTheme\Controls.skin 11
Very disappointed at this stage. Why?
a. My columns are not in order
b. I can’t skin TemplateField
In order to use Skin, you will win something and lose something too:
a. Partial centralization. Why I will call it partial? If you see the explanation above, TemplateField is not supported. So you cannot centralize everything in skin field.
b. Based on my findings, I will recommend you to place your columns inside the Default.aspx file instead of placing it in skin file. Then have a generic GridView without the SkinID which you can apply to all your GridViews within this Theme “ChineseNewYearTheme”. The reason I say this as most of the time all of our data display tables are using the same coloring and layout standard. You seldom find a page with 5 GridViews with different colors. That is bad design.
c. Start using Skin and CSS file. Probably this will improve in the next generation of ASP.NET, will keep my fingers crossed.
Lastly, Skin and CSS are ways to make your controls and page cleaners. You can apply skins on web form using Master Pages. No much hassles.
Hope you like my findings.