Tuesday, April 25, 2006

ASP.NET : Binding XML Data to TreeView with Checked state of Checkboxes

Often we come across data that is hierarchial in nature.

Let us take a real world example of a profile page of a person that takes the interests of a person. The Interest categories are categorized in a hierarchial structure, with Categories such as Books, Entertainment, Sports, etc. There will be sub categories, for example, for Books category there might be sub categories as Religion, Literature, etc. and Region might have sub sub categories such as Christianity, Hinduism, etc.

The best way such type of Hierarchial data can be shown is through a Treeview control. To bind the entire set of categories, sub categories, sub sub categories, the best way is to bind the TreeView with XML.

In ASP.NET 2.0 we have an XmlDataSource control that serves exactly this purpose. The XMLDataSource can take up any static XML file that contains XML data.

Ex:
<asp:XmlDataSource ID="CategoriesXmlDataSource" runat="server" DataFile="MyXml.xml"></asp:XmlDataSource>

The XmlDataSource also has a XPath property that is used to filter data in the xml.In case the data to be bound comes from database, we can use the Data property of XmlDataSource control to directly bind the xml string from database to the treeview. From the example we have taken on the Categories and Sub categories, we intend to show check boxes on the treeview, and also want the check boxes to be in checked state according to how the profile has been updated for the person. Let us now check on how we can achieve this.

The first step is to write a proper query that gives an xml document as output. This Xml document must basically contain the entire hierarchial structure that must be displayed as a treeview.If SQL Server is your database, you can easily achieve this by using joins and using the "for xml auto" clause in your query.

Now if you wish to bind the checked state of check boxes as well, using the data from database, you must also bring this checked state as another attribute of every node.

Ex: The xml from database must be something like:

<Categories>
<Category ID="1" Name="Books" checked="false">
<SubCategory Id="2" Name="Religon" checked="false">
<SubSubCategory Id="Hinduism" checked="true" />
<SubSubCategory Id="Christianity" checked="false" />
<SubSubCategory Id="Islam" checked="true" />
</SubCategory>
</Category>
</Categories>

The next step is to make this xml from database to be the datasource for XmlDatasource control. To do this, add this declaration in your aspx page:

<asp:XmlDataSource ID="XmlDataSource1" runat="server" XPath="/Categories/*"></asp:XmlDataSource>

The XPath expression filtes out the top root node and displays the rest of the nodes.
Now in the code behind page add this code in Page_load event:

XmlDataSource1.Data = GetXmlData();

The GetXmlData method retuns the entire xml structure as shown above from database.
The final step is to bind the Treeview control with this XmlDataSource. This is done using the <DataBindings> property of the ASP.NET Treeview.

To the <DataBindings> of the Treeview we add the TreeNodeBinding, for each of the different levels in the TreeView.

For this, add the following declaration in your aspx page:

<asp:TreeView runat="server" ID="tvwCategories" ExpandDepth=1 OnTreeNodeDataBound="tvwCategories_TreeNodeDataBound" ShowCheckBoxes="All" >
<DataBindings>
<asp:TreeNodeBinding DataMember="Country" SelectAction=None PopulateOnDemand=true TargetField="checked" ValueField="ID" extField="Name"></asp:TreeNodeBinding>
<asp:TreeNodeBinding DataMember="Geography" PopulateOnDemand=true SelectAction=None TargetField="checked" ValueField="ID" TextField="Name"></asp:TreeNodeBinding>
<asp:TreeNodeBinding DataMember="Region" TargetField="checked" SelectAction=None PopulateOnDemand=true ValueField="ID" TextField="Name"></asp:TreeNodeBinding>
<asp:TreeNodeBinding DataMember="State" TargetField="checked" SelectAction=None PopulateOnDemand=true ValueField="ID" TextField="Name"></asp:TreeNodeBinding> </DataBindings>
</asp:TreeView>

The DataMember property in TreeNodeBinding is used to specify the different XML elements that will be bound to the TreeView.

The ValueField and TextField properties correspond to the Xml attributes that will assign the Text and Value fields to each node in the TreeView.

The TargetField is here used to set the Checked state of the nodes from the corresponding Xml attribute (i.e. checked). (The TargetField in actual usage is for assigning the "target" html attribute, so that any link from the TreeView will open in the target window accordingly.)

The SelectAction=None specifies that the nodes in the TreeView do not have links or any other click action.

Note the method that is called in OnTreeNodeDataBound event. This method will basically set the checked state of the checkboxes.

The following is the code for the event that has to be added in the codebehind page:

protected void tvwCategories_TreeNodeDataBound(object sender, TreeNodeEventArgs e)
{
if (e.Node.Target == Convert.ToString("true"))
{
e.Node.Checked = true;
}
else
{
e.Node.Checked = false;
}
}

The page is now ready to be compiled and browsed. Of course, there are several ways to set the checked state of checkboxes in treeviews. However, using the above method we avoid the unnecessary for/foreach loops that may have to be used for doing the same.

Thanks.

Thursday, April 20, 2006

ASP.NET : Export to Excel/Word from Nested GridViews


Exporting a GridView to an Excel/Word is one common requirement in ASP.NET applications. In case of simple GridViews this is a pretty easy and the code for the same can be found in my earlier article.

In case of Nested GridViews, when trying to export to Word/Excel, the output comes would always be rendered inverted in Word/Excel.

In this scenario, let us see how we can export the entire Nested GridView.

The first step is to insert a <div > that embeds the entire Nested GridView.
For Ex :

<div id="divNestedGrid" runat="server">
<asp:GridView id=.. >
...
<asp:GridView .... />
---
</asp:GridView>
</asp:GridView>
</div>


Next step is add a hidden variable in the same aspx form.

<input type="hidden" id="hdnInnerHtml" value="" runat="server" />

Now add a javascript function that in the aspx that fills the hidden variable with the inner html of the div.

function getInnerHtml()
{
var element = document.getElementById("divpreview");
var store = document.getElementById("hdnInnerHtml");
//add the css styles you have used inside the nested GridView
var css = "<style type=\"text/css\" id=\"style1\">.textbold {font-family: Arial, Helvetica, sans-serif;font-size: 11px;color: #000000;font-weight: bold;text-decoration: none;}.row1 {background-color: #FFFFFF; font-family: Arial, Helvetica, sans-serif;font-size: 11px;color: #000000;height: 18px;padding-left: 5px;}.;
store.value = css + element.innerHTML;
}

Now in the Code Behind, first add the javascript function to be triggered on the click on the Export button.

btnExport.Attributes.Add("OnClick", "getInnerHtml();");

Finally, write the following code in the Export Button click event, to Export to Word:

string html = hdnInnerHtml.Value;
Response.Cache.SetExpires(DateTime.Now.AddSeconds(1));
Response.Clear();
Response.AppendHeader("content-disposition", "attachment;filename=FileName.doc");
Response.Charset = "";
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.ContentType = "application/vnd.ms-word";
this.EnableViewState = false;
Response.Write("\r\n");
Response.Write(html);
Response.End();

In case of Exporting to Excel, you can change the code as follows:

string html = hdnInnerHtml.Value;
Response.Cache.SetExpires(DateTime.Now.AddSeconds(1));
Response.Clear();
Response.AppendHeader("content-disposition", "attachment;filename=FileName.xls");
Response.Charset = "";
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.ContentType = "application/vnd.ms-excel";
this.EnableViewState = false;
Response.Write("\r\n");
Response.Write(html);
Response.End();


In this article we discussed different ways of Exporting Data to Excel and Word when dealing with Nested Grid Views.

Thanks.