Export GridView To Word/Excel/PDF/CSV in ASP.Net
In this article, I will explain how to export GridView to Word, Excel, PDF and CSV formats.
Exporting to Word, Excel and CSV can be easily achieved using ASP.Net without any third party tools, but for exporting GridView to PDF I am using iTextSharp which is a free library for exporting html to PDF.
To start with I have a GridView in which I am showing Customers records from the NorthWind Database.
The HTML markup of the GridView is as shown below
<asp:GridView ID="GridView1" runat="server"
AutoGenerateColumns = "false" Font-Names = "Arial"
Font-Size = "11pt" AlternatingRowStyle-BackColor = "#C2D69B"
HeaderStyle-BackColor = "green" AllowPaging ="true"
OnPageIndexChanging = "OnPaging" >
<Columns>
<asp:BoundField ItemStyle-Width = "150px" DataField = "CustomerID"
HeaderText = "CustomerID" />
<asp:BoundField ItemStyle-Width = "150px" DataField = "City"
HeaderText = "City"/>
<asp:BoundField ItemStyle-Width = "150px" DataField = "Country"
HeaderText = "Country"/>
<asp:BoundField ItemStyle-Width = "150px" DataField = "PostalCode"
HeaderText = "PostalCode"/>
</Columns>
</asp:GridView>
In the figure below the GridView is shown with four buttons
1. Export To Word
2. Export To Excel
3. Export To PDF
4. Export To CSV
GridView with Sample Data
Export to Microsoft Word Format
C#
protected void btnExportWord_Click(object sender, EventArgs e)
{
Response.Clear();
Response.Buffer = true;
Response.AddHeader("content-disposition",
"attachment;filename=GridViewExport.doc");
Response.Charset = "";
Response.ContentType = "application/vnd.ms-word ";
StringWriter sw= new StringWriter();
HtmlTextWriter hw = new HtmlTextWriter(sw);
GridView1.AllowPaging = false;
GridView1.DataBind();
GridView1.RenderControl(hw);
Response.Output.Write(sw.ToString());
Response.Flush();
Response.End();
}
VB.Net
Protected Sub btnExportWord_Click(ByVal sender As Object,
ByVal e As EventArgs)
Response.Clear()
Response.Buffer = True
Response.AddHeader("content-disposition",
"attachment;filename=GridViewExport.doc")
Response.Charset = ""
Response.ContentType = "application/vnd.ms-word "
Dim sw As New StringWriter()
Dim hw As New HtmlTextWriter(sw)
GridView1.AllowPaging = False
GridView1.DataBind()
GridView1.RenderControl(hw)
Response.Output.Write(sw.ToString())
Response.Flush()
Response.End()
End Sub
The above function renders the GridView contents as Microsoft Word format. You will notice I have disabled paging before exporting, so that all the pages are exported.
The Output Exported File
GridView data exported to Word Document
Export to Microsoft Excel Format
For exporting the document to Excel if you do it directly as done in case of word the row background color is applied throughout to all the columns in the Excel Sheet hence in order to avoid it. I have done a workaround below.
First I am changing the background color of each row back to white.
Then I am applying the background color to each individual cell rather than the whole row. Thus when you export now you will notice that the formatting is applied only to the GridView cells and not all
Also I am applying textmode style class to all cells and then adding the style CSS class to the GridView before rendering it, this ensures that all the contents of GridView are rendered as text.
protected void btnExportExcel_Click(object sender, EventArgs e)
{
Response.Clear();
Response.Buffer = true;
Response.AddHeader("content-disposition",
"attachment;filename=GridViewExport.xls");
Response.Charset = "";
Response.ContentType = "application/vnd.ms-excel";
StringWriter sw = new StringWriter();
HtmlTextWriter hw = new HtmlTextWriter(sw);
GridView1.AllowPaging = false;
GridView1.DataBind();
//Change the Header Row back to white color
GridView1.HeaderRow.Style.Add("background-color", "#FFFFFF");
//Apply style to Individual Cells
GridView1.HeaderRow.Cells[0].Style.Add("background-color", "green");
GridView1.HeaderRow.Cells[1].Style.Add("background-color", "green");
GridView1.HeaderRow.Cells[2].Style.Add("background-color", "green");
GridView1.HeaderRow.Cells[3].Style.Add("background-color", "green");
for (int i = 0; i < GridView1.Rows.Count;i++ )
{
GridViewRow row = GridView1.Rows[i];
//Change Color back to white
row.BackColor = System.Drawing.Color.White;
//Apply text style to each Row
row.Attributes.Add("class", "textmode");
//Apply style to Individual Cells of Alternating Row
if (i % 2 != 0)
{
row.Cells[0].Style.Add("background-color", "#C2D69B");
row.Cells[1].Style.Add("background-color", "#C2D69B");
row.Cells[2].Style.Add("background-color", "#C2D69B");
row.Cells[3].Style.Add("background-color", "#C2D69B");
}
}
GridView1.RenderControl(hw);
//style to format numbers to string
string style = @"<style> .textmode { mso-number-format:\@; } </style>";
Response.Write(style);
Response.Output.Write(sw.ToString());
Response.Flush();
Response.End();
}
VB.Net
Protected Sub btnExportExcel_Click(ByVal sender As Object,
ByVal e As EventArgs)
Response.Clear()
Response.Buffer = True
Response.AddHeader("content-disposition",
"attachment;filename=GridViewExport.xls")
Response.Charset = ""
Response.ContentType = "application/vnd.ms-excel"
Dim sw As New StringWriter()
Dim hw As New HtmlTextWriter(sw)
GridView1.AllowPaging = False
GridView1.DataBind()
'Change the Header Row back to white color
GridView1.HeaderRow.Style.Add("background-color", "#FFFFFF")
'Apply style to Individual Cells
GridView1.HeaderRow.Cells(0).Style.Add("background-color", "green")
GridView1.HeaderRow.Cells(1).Style.Add("background-color", "green")
GridView1.HeaderRow.Cells(2).Style.Add("background-color", "green")
GridView1.HeaderRow.Cells(3).Style.Add("background-color", "green")
For i As Integer = 0 To GridView1.Rows.Count - 1
Dim row As GridViewRow = GridView1.Rows(i)
'Change Color back to white
row.BackColor = System.Drawing.Color.White
'Apply text style to each Row
row.Attributes.Add("class", "textmode")
'Apply style to Individual Cells of Alternating Row
If i Mod 2 <> 0 Then
row.Cells(0).Style.Add("background-color", "#C2D69B")
row.Cells(1).Style.Add("background-color", "#C2D69B")
row.Cells(2).Style.Add("background-color", "#C2D69B")
row.Cells(3).Style.Add("background-color", "#C2D69B")
End If
Next
GridView1.RenderControl(hw)
'style to format numbers to string
Dim style As String = "<style>.textmode{mso-number-format:\@;}</style>"
Response.Write(style)
Response.Output.Write(sw.ToString())
Response.Flush()
Response.End()
End Sub
The Output Exported File
GridView data exported to Excel Document
Export to Portable Document Format
For exporting the GridView to PDF I am using the iTextSharp Library. You will need to Add Reference for the iTextSharp Library in your Website.
Then import the following Namespaces
C#
using iTextSharp.text;
using iTextSharp.text.pdf;
using iTextSharp.text.html;
using iTextSharp.text.html.simpleparser;
VB.Net
Imports iTextSharp.text
Imports iTextSharp.text.pdf
Imports iTextSharp.text.html
Imports iTextSharp.text.html.simpleparser
By default the iTextSharp Library does not support background color of table cells or table rows
Hence when you render it as PDF your GridView is rendered without any formatting.
Recently I read an article on hamang.net where the author has provided the snippet to modify the iTextSharp so that it exports the HTML with background color.
For this tutorial, I have already modified the iTextSharp Library DLL so that the GridView is rendered with all the background color used. You can refer the code for exporting GridView to PDF below
C#
protected void btnExportPDF_Click(object sender, EventArgs e)
{
Response.ContentType = "application/pdf";
Response.AddHeader("content-disposition",
"attachment;filename=GridViewExport.pdf");
Response.Cache.SetCacheability(HttpCacheability.NoCache);
StringWriter sw = new StringWriter();
HtmlTextWriter hw = new HtmlTextWriter(sw);
GridView1.AllowPaging = false;
GridView1.DataBind();
GridView1.RenderControl(hw);
StringReader sr = new StringReader(sw.ToString());
Document pdfDoc = new Document(PageSize.A4, 10f,10f,10f,0f);
HTMLWorker htmlparser = new HTMLWorker(pdfDoc);
PdfWriter.GetInstance(pdfDoc, Response.OutputStream);
pdfDoc.Open();
htmlparser.Parse(sr);
pdfDoc.Close();
Response.Write(pdfDoc);
Response.End();
}
VB.Net
Protected Sub btnExportPDF_Click(ByVal sender As Object,
ByVal e As EventArgs)
Response.ContentType = "application/pdf"
Response.AddHeader("content-disposition",
"attachment;filename=GridViewExport.pdf")
Response.Cache.SetCacheability(HttpCacheability.NoCache)
Dim sw As New StringWriter()
Dim hw As New HtmlTextWriter(sw)
GridView1.AllowPaging = False
GridView1.DataBind()
GridView1.RenderControl(hw)
Dim sr As New StringReader(sw.ToString())
Dim pdfDoc As New Document(PageSize.A4, 10.0F, 10.0F, 10.0F, 0.0F)
Dim htmlparser As New HTMLWorker(pdfDoc)
PdfWriter.GetInstance(pdfDoc, Response.OutputStream)
pdfDoc.Open()
htmlparser.Parse(sr)
pdfDoc.Close()
Response.Write(pdfDoc)
Response.End()
End Sub
The Output Exported File
GridView data exported to PDF Document
Export to Text/CSV
Finally comes exporting GridView to CSV or Text File delimited by a separator like comma.
To export the GridView as CSV, I am running a two for loops. While looping through the GridView columns and appending comma after each column and while looping through rows appending new line character. Refer the code below.
C#
protected void btnExportCSV_Click(object sender, EventArgs e)
{
Response.Clear();
Response.Buffer = true;
Response.AddHeader("content-disposition",
"attachment;filename=GridViewExport.csv");
Response.Charset = "";
Response.ContentType = "application/text";
GridView1.AllowPaging = false;
GridView1.DataBind();
StringBuilder sb = new StringBuilder();
for (int k = 0; k < GridView1.Columns.Count; k++)
{
//add separator
sb.Append(GridView1.Columns[k].HeaderText + ',');
}
//append new line
sb.Append("\r\n");
for (int i = 0; i < GridView1.Rows.Count; i++)
{
for (int k = 0; k < GridView1.Columns.Count; k++)
{
//add separator
sb.Append(GridView1.Rows[i].Cells[k].Text + ',');
}
//append new line
sb.Append("\r\n");
}
Response.Output.Write(sb.ToString());
Response.Flush();
Response.End();
}
VB.Net
Protected Sub btnExportCSV_Click(ByVal sender As Object,
ByVal e As EventArgs)
Response.Clear()
Response.Buffer = True
Response.AddHeader("content-disposition",
"attachment;filename=GridViewExport.csv")
Response.Charset = ""
Response.ContentType = "application/text"
GridView1.AllowPaging = False
GridView1.DataBind()
Dim sb As New StringBuilder()
For k As Integer = 0 To GridView1.Columns.Count - 1
'add separator
sb.Append(GridView1.Columns(k).HeaderText + ","c)
Next
'append new line
sb.Append(vbCr & vbLf)
For i As Integer = 0 To GridView1.Rows.Count - 1
For k As Integer = 0 To GridView1.Columns.Count - 1
'add separator
sb.Append(GridView1.Rows(i).Cells(k).Text + ","c)
Next
'append new line
sb.Append(vbCr & vbLf)
Next
Response.Output.Write(sb.ToString())
Response.Flush()
Response.End()
End Sub
The Output Exported File
GridView data exported to CSV File
When you run the application first time and click export you might receive the following error
Error encountered when you click export
To avoid the error you will need to add this event which ensures that the GridView is Rendered before exporting.
C#
public override void VerifyRenderingInServerForm(Control control)
{
/* Verifies that the control is rendered */
}
VB.Net
Public Overloads Overrides Sub VerifyRenderingInServerForm
(ByVal control As Control)
' Verifies that the control is rendered
End Sub
This completes the article you can view the live demo here
The source code is available in C# and VB.Net here
Wednesday, August 26, 2009
Tuesday, August 25, 2009
Adding QueryString Parameters to the SiteMapNode
There is no way to add querystring parameters to the SiteMapNode in a SiteMap control "out of the box." I'm quite surprised there is no option but luckily the SiteMap uses the provider model. After a Live search, I was able to find a solution provided by Bobby DeRosa. By creating a custom provider, this can be accomplished. The project I was on is an update of an ASP.net app written in VB.net so here is my VB.net port:
Imports System.Collections.Specialized
Imports System.Web
Namespace Configuration
Public Class ExtendedSiteMapProvider
Inherits XmlSiteMapProvider
Public Overrides Sub Initialize(ByVal name As String, ByVal attributes As NameValueCollection)
MyBase.Initialize(name, attributes)
Dim resolveHandler As New SiteMapResolveEventHandler(AddressOf SmartSiteMapProvider_SiteMapResolve)
AddHandler Me.SiteMapResolve, resolveHandler
End Sub
Function SmartSiteMapProvider_SiteMapResolve(ByVal sender As Object, ByVal e As SiteMapResolveEventArgs) As SiteMapNode
If (SiteMap.CurrentNode Is Nothing) Then Return Nothing
Dim this As New XmlSiteMapProvider
Dim temp As SiteMapNode
temp = SiteMap.CurrentNode.Clone(True)
Dim u As Uri = New Uri(e.Context.Request.Url.ToString())
Dim tempNode As SiteMapNode = temp
While Not tempNode Is Nothing
Dim qs As String = GetQueryString(tempNode, e.Context)
If Not qs Is Nothing Then
If Not tempNode Is Nothing Then
tempNode.Url += qs
End If
End If
tempNode = tempNode.ParentNode
End While
Return temp
End Function
Private Function GetQueryString(ByVal node As SiteMapNode, ByVal context As HttpContext) As String
If node("queryStringToInclude") Is Nothing Then Return Nothing
Dim values As NameValueCollection = New NameValueCollection
Dim vars() As String = node("queryStringToInclude").Split(",".ToCharArray())
Dim s As String
For Each s In vars
Dim var As String = s.Trim()
If context.Request.QueryString(var) Is Nothing Then Continue For
values.Add(var, context.Request.QueryString(var))
Next
If values.Count = 0 Then Return Nothing
Return NameValueCollectionToString(values)
End Function
Private Function NameValueCollectionToString(ByVal col As NameValueCollection) As String
Dim parts(col.Count - 1) As String
Dim keys() As String = col.AllKeys
For i As Integer = 0 To keys.Length - 1
parts(i) = keys(i) & "=" & col(keys(i))
Next
Dim url As String = "?" & String.Join("&", parts)
Return url
End Function
End Class
End Namespace
I added the following to the web.config:
<siteMap defaultProvider="ExtendedSiteMapProvider" enabled="true">
<providers>
<clear />
<add name="ExtendedSiteMapProvider" type="Configuration.ExtendedSiteMapProvider" siteMapFile="web.sitemap" securityTrimmingEnabled="true" />
</providers>
</siteMap>
If I had a page of products on products.aspx, displayed the details of the product on details.aspx, and had a separate update.aspx page, my web.sitemap might look like this:
<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
<siteMapNode url="~/" title="Main Page">
<siteMapNode url="~/products.aspx" title="Products" >
<siteMapNode url="~/details.aspx" title="Product Details" queryStringToInclude="ProductID" >
<siteMapNode url="~/update.aspx" title="Updating a Product" />
</siteMapNode>
</siteMapNode>
</siteMapNode>
</siteMap>
So, on /update.aspx?ProductID=3, the SiteMapNode for Product Details would have a url of /details.aspx?ProductID=3.
Hopefully a similar feature will be added soon to the ASP.net SiteMap control.
<span style="font-weight:bold;">C#:</span>
Great job! I took your C# code and had to make a few small changes to get it working correctly (on VS2008 & .NET 3.5 sp1):
#region Using Directives
using System.Collections.Specialized;
using System.Web;
#endregion
namespace Configuration
{
public class ExtendedSiteMapProvider : XmlSiteMapProvider
{
public override void Initialize(string name, NameValueCollection attributes)
{
base.Initialize(name, attributes);
this.SiteMapResolve += SmartSiteMapProvider_SiteMapResolve;
}
static SiteMapNode SmartSiteMapProvider_SiteMapResolve(object sender, SiteMapResolveEventArgs e)
{
if ((SiteMap.CurrentNode == null)) return null;
SiteMapNode temp = SiteMap.CurrentNode.Clone(true);
SiteMapNode tempNode = temp;
while (tempNode != null)
{
string qs = GetQueryString(tempNode, e.Context);
if (qs != null)
{
tempNode.Url += qs;
}
tempNode = tempNode.ParentNode;
}
return temp;
}
private static string GetQueryString(SiteMapNode node, HttpContext context)
{
if (node["queryStringToInclude"] == null) return null;
NameValueCollection values = new NameValueCollection();
string[] vars = node["queryStringToInclude"].Split(",".ToCharArray());
foreach (string s in vars)
{
string var = s.Trim();
if (context.Request.QueryString[var] == null) continue;
values.Add(var, context.Request.QueryString[var]);
}
if (values.Count == 0) return null;
return NameValueCollectionToString(values);
}
private static string NameValueCollectionToString(NameValueCollection col)
{
string[] parts = new string[col.Count];
string[] keys = col.AllKeys;
for (int i = 0; i <= keys.Length - 1; i++)
{
parts[i] = keys[i] + "=" + col[keys[i]];
}
return "?" + string.Join("&", parts);
}
}
}
Imports System.Collections.Specialized
Imports System.Web
Namespace Configuration
Public Class ExtendedSiteMapProvider
Inherits XmlSiteMapProvider
Public Overrides Sub Initialize(ByVal name As String, ByVal attributes As NameValueCollection)
MyBase.Initialize(name, attributes)
Dim resolveHandler As New SiteMapResolveEventHandler(AddressOf SmartSiteMapProvider_SiteMapResolve)
AddHandler Me.SiteMapResolve, resolveHandler
End Sub
Function SmartSiteMapProvider_SiteMapResolve(ByVal sender As Object, ByVal e As SiteMapResolveEventArgs) As SiteMapNode
If (SiteMap.CurrentNode Is Nothing) Then Return Nothing
Dim this As New XmlSiteMapProvider
Dim temp As SiteMapNode
temp = SiteMap.CurrentNode.Clone(True)
Dim u As Uri = New Uri(e.Context.Request.Url.ToString())
Dim tempNode As SiteMapNode = temp
While Not tempNode Is Nothing
Dim qs As String = GetQueryString(tempNode, e.Context)
If Not qs Is Nothing Then
If Not tempNode Is Nothing Then
tempNode.Url += qs
End If
End If
tempNode = tempNode.ParentNode
End While
Return temp
End Function
Private Function GetQueryString(ByVal node As SiteMapNode, ByVal context As HttpContext) As String
If node("queryStringToInclude") Is Nothing Then Return Nothing
Dim values As NameValueCollection = New NameValueCollection
Dim vars() As String = node("queryStringToInclude").Split(",".ToCharArray())
Dim s As String
For Each s In vars
Dim var As String = s.Trim()
If context.Request.QueryString(var) Is Nothing Then Continue For
values.Add(var, context.Request.QueryString(var))
Next
If values.Count = 0 Then Return Nothing
Return NameValueCollectionToString(values)
End Function
Private Function NameValueCollectionToString(ByVal col As NameValueCollection) As String
Dim parts(col.Count - 1) As String
Dim keys() As String = col.AllKeys
For i As Integer = 0 To keys.Length - 1
parts(i) = keys(i) & "=" & col(keys(i))
Next
Dim url As String = "?" & String.Join("&", parts)
Return url
End Function
End Class
End Namespace
I added the following to the web.config:
<siteMap defaultProvider="ExtendedSiteMapProvider" enabled="true">
<providers>
<clear />
<add name="ExtendedSiteMapProvider" type="Configuration.ExtendedSiteMapProvider" siteMapFile="web.sitemap" securityTrimmingEnabled="true" />
</providers>
</siteMap>
If I had a page of products on products.aspx, displayed the details of the product on details.aspx, and had a separate update.aspx page, my web.sitemap might look like this:
<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
<siteMapNode url="~/" title="Main Page">
<siteMapNode url="~/products.aspx" title="Products" >
<siteMapNode url="~/details.aspx" title="Product Details" queryStringToInclude="ProductID" >
<siteMapNode url="~/update.aspx" title="Updating a Product" />
</siteMapNode>
</siteMapNode>
</siteMapNode>
</siteMap>
So, on /update.aspx?ProductID=3, the SiteMapNode for Product Details would have a url of /details.aspx?ProductID=3.
Hopefully a similar feature will be added soon to the ASP.net SiteMap control.
<span style="font-weight:bold;">C#:</span>
Great job! I took your C# code and had to make a few small changes to get it working correctly (on VS2008 & .NET 3.5 sp1):
#region Using Directives
using System.Collections.Specialized;
using System.Web;
#endregion
namespace Configuration
{
public class ExtendedSiteMapProvider : XmlSiteMapProvider
{
public override void Initialize(string name, NameValueCollection attributes)
{
base.Initialize(name, attributes);
this.SiteMapResolve += SmartSiteMapProvider_SiteMapResolve;
}
static SiteMapNode SmartSiteMapProvider_SiteMapResolve(object sender, SiteMapResolveEventArgs e)
{
if ((SiteMap.CurrentNode == null)) return null;
SiteMapNode temp = SiteMap.CurrentNode.Clone(true);
SiteMapNode tempNode = temp;
while (tempNode != null)
{
string qs = GetQueryString(tempNode, e.Context);
if (qs != null)
{
tempNode.Url += qs;
}
tempNode = tempNode.ParentNode;
}
return temp;
}
private static string GetQueryString(SiteMapNode node, HttpContext context)
{
if (node["queryStringToInclude"] == null) return null;
NameValueCollection values = new NameValueCollection();
string[] vars = node["queryStringToInclude"].Split(",".ToCharArray());
foreach (string s in vars)
{
string var = s.Trim();
if (context.Request.QueryString[var] == null) continue;
values.Add(var, context.Request.QueryString[var]);
}
if (values.Count == 0) return null;
return NameValueCollectionToString(values);
}
private static string NameValueCollectionToString(NameValueCollection col)
{
string[] parts = new string[col.Count];
string[] keys = col.AllKeys;
for (int i = 0; i <= keys.Length - 1; i++)
{
parts[i] = keys[i] + "=" + col[keys[i]];
}
return "?" + string.Join("&", parts);
}
}
}
SiteMapProvider doesn't take QueryString into consideration
I was pretty stoked to find that ASP.NET 2.0 included a new control called SiteMap. This control will display a breadcrumb trail of links as you navigate through your site. By default, the data is loaded from a web.sitemap file that sits in the web app root. Although you can load the data from any source, this is the default and uses the XmlSiteMapProvider provider to populate the data.
This is an example of data found in the web.sitemap file:
Copy code to clipboard in IE or select code for Firefox
<?xml version="1.0" encoding="utf-8" ?>
<siteMap>
<siteMapNode url="~/default.aspx" title="Home">
<siteMapNode url="~/hotels/list.aspx" title="Hotel Listings">
<siteMapNode url="~/hotels/details.aspx" title="Hotel Information">
<siteMapNode url="~/hotels/update.aspx" title="Update Hotel"/>
</siteMapNode>
</siteMapNode>
</siteMapNode>
</siteMap>
This works fine if you aren't ever using querystring variables. But let's say when you get down to the details.aspx page, you tag on a HotelID variable to the querystring. Same thing happens when you go deeper into update.aspx.
Your breadcrumb will look like this:
Home > Hotel Listings > Hotel Information > Update Hotel
Given my current url is "update.aspx?HotelID=1234", I should have some way to propagate the current HotelID querystring variable to the other links, if they need it, which details.apx does.
I tried endlessly to get around this. I tried derriving my own class from XmlSiteProvider, but ASP.NET crashes as soon as you mess w/ the child nodes in the BuildSiteMap() method.
I tried binding an event handler to the SiteMap.SiteMapResolve event - but the Request collection is always unavailable at the time when this event is triggered.
I tried to modify the url's of the nodes showing on the screen dynamically, but supid SiteMapNode.Url property is Read-Only - ARGGG!!
It just amazes me that MS didn't see this coming & allow for it in the XML config. They could have added a "incorporate" attribute in the node that allowed you to specify a comma-delimited list of querystring variables that would be needed for that URL to work, like this:
Copy code to clipboard in IE or select code for Firefox
<siteMapNode url="~/hotels/details.aspx" title="Hotel Information" reliantOn="HotelID, UserID">
or give me a way to add in the querystring variables I want to pull from the current context like this:
Copy code to clipboard in IE or select code for Firefox
<siteMapNode url="~/hotels/details.aspx?HotelID={HotelID}" title="Hotel Information">
And the SiteMapProvider would be smart enough to replace the vars in braces w/ the variable from the current querystring.
I never thought getting something so simple to work would be so hard. If anyone has a solution, pony up.
This is an example of data found in the web.sitemap file:
Copy code to clipboard in IE or select code for Firefox
<?xml version="1.0" encoding="utf-8" ?>
<siteMap>
<siteMapNode url="~/default.aspx" title="Home">
<siteMapNode url="~/hotels/list.aspx" title="Hotel Listings">
<siteMapNode url="~/hotels/details.aspx" title="Hotel Information">
<siteMapNode url="~/hotels/update.aspx" title="Update Hotel"/>
</siteMapNode>
</siteMapNode>
</siteMapNode>
</siteMap>
This works fine if you aren't ever using querystring variables. But let's say when you get down to the details.aspx page, you tag on a HotelID variable to the querystring. Same thing happens when you go deeper into update.aspx.
Your breadcrumb will look like this:
Home > Hotel Listings > Hotel Information > Update Hotel
Given my current url is "update.aspx?HotelID=1234", I should have some way to propagate the current HotelID querystring variable to the other links, if they need it, which details.apx does.
I tried endlessly to get around this. I tried derriving my own class from XmlSiteProvider, but ASP.NET crashes as soon as you mess w/ the child nodes in the BuildSiteMap() method.
I tried binding an event handler to the SiteMap.SiteMapResolve event - but the Request collection is always unavailable at the time when this event is triggered.
I tried to modify the url's of the nodes showing on the screen dynamically, but supid SiteMapNode.Url property is Read-Only - ARGGG!!
It just amazes me that MS didn't see this coming & allow for it in the XML config. They could have added a "incorporate" attribute in the node that allowed you to specify a comma-delimited list of querystring variables that would be needed for that URL to work, like this:
Copy code to clipboard in IE or select code for Firefox
<siteMapNode url="~/hotels/details.aspx" title="Hotel Information" reliantOn="HotelID, UserID">
or give me a way to add in the querystring variables I want to pull from the current context like this:
Copy code to clipboard in IE or select code for Firefox
<siteMapNode url="~/hotels/details.aspx?HotelID={HotelID}" title="Hotel Information">
And the SiteMapProvider would be smart enough to replace the vars in braces w/ the variable from the current querystring.
I never thought getting something so simple to work would be so hard. If anyone has a solution, pony up.
Subscribe to:
Posts (Atom)