Monday, February 21, 2011

Data List Control (.NET) - Create a fully dynamic user control that handles URL links and Css Classes - ASP.NET

A few days ago I wrote a post on how to create a fully dynamic Link class in C# that will return a full query string based on what a user is clicking within a page. In the following example I'll present the table design, Stored Procedure, C# class, CSS Class and ASP.NET user control I'm using to


1- Present a Data List control in a user control that uses an Item Template with a HyperLink control inside that dynamically chooses its NavigateURL, Text, ToolTip and CssClass values directly from ASP.NET to a business tier.

2- Generate a Query String

The following is the SQL Table I'm using for this example:

The following is the Stored Procedure I'm using to retrieve the data

ALTER PROCEDURE [dbo].[CatalogGetCategoryDetails](@CategoryID INT)AS
SELECT DepartmentID, Name, Description FROM CategoryWHERE CategoryID = @CategoryID

The following is the C# Class to generate the Query Strings (This class in within the App_Code folder in my project)

In the above code I'm calling the Link.ToCategory class (shown above) directly from ASP.NET.

NavigateUrl='<%# Link.ToCategory(Request.QueryString["DepartmentID"], Eval("CategoryID").ToString()) %>'


Also, if the CategoryID of the page is the same as the value requested in the Query String, then the Css Class named CategorySelected will be apply to the HyperLink control , otherwise the control will use the CategoryUnselected one:

CssClass = '<%#Eval(CategoryID).ToString() == Request.QueryString["CategoryID"] ? "CategorySelected" : "CategoryUnselected" %>'>


Because in my project the Product Categories are dependent on the Department that is being clicked, when the page is loading I need to always know the DepartmentID. If there is no Department ID when the Query String is requested, then no Categories will show.

Also, if the DepartmentID is not null, I need to set the DataList DataSource value (which will access my business tier class and the business tier will access the data base StoredProcedure shown above), and bind the data to the control as shown below:



using System;
using System.Collections.Generic;
using System.Web;

/// <summary>
/// Summary description for Link
/// </summary>
public class Link
{
          public Link()
          {
                   //
                   // TODO: Add constructor logic here
                   //
          }

    //Builds an absolute URL
    private static string BuildAbsolute(string relativeUri)
    {
        //Get current Uri
        Uri uri = HttpContext.Current.Request.Url;

        //Build absolute path
        string app = HttpContext.Current.Request.ApplicationPath;
        if (!app.EndsWith("/")) app += "/";

        relativeUri = relativeUri.TrimStart('/');

        //Return the absolute path
        return HttpUtility.UrlPathEncode(String.Format("http://{0}:{1}{2}{3}", uri.Host, uri.Port, app, relativeUri));
    }

    //Generate a department url
    public static string ToDepartment(string departmentId, string page)
    {
        if (page == "1")
            return BuildAbsolute(String.Format("Catalog.aspx?DepartmentID={0}", departmentId));
        else
            return BuildAbsolute(String.Format("Catalog.aspx?DepartmentID={0}&Page={1}", departmentId, page));
    }

    //Generate a department URL for the first page
    public static string ToDepartment(string DepartmentId)
    {
        return ToDepartment(DepartmentId, "1");
    }

    public static string ToCategory(string departmentId, string categoryId, string page)
    {
        if (page == "1")
            return BuildAbsolute(String.Format("Catalog.aspx?DepartmentID={0}&CategoryID={1}", departmentId, categoryId));
        else
            return BuildAbsolute(String.Format("Catalog.aspx?DepartmentID={0}&CategoryID={1}&Page={2}", departmentId, categoryId, page));
    }

    public static string ToCategory(string departmentId, string categoryId)
    {
        return ToCategory(departmentId, categoryId, "1");
    }

    public static string ToProduct(string productId)
    {
        return BuildAbsolute(String.Format("Product.aspx?ProductId{0}", productId));
    }

    public static string ToProductImage(string fileName)
    {
        //build product URL
        return BuildAbsolute("/ProductImages/" + fileName);
    }
}



The Following is the ASP.NET Using Control code

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="CategoriesList.ascx.cs" Inherits="UserControls_CategoriesList" %>
<asp:DataList ID="list" runat="server" CssClass="CategoriesList" Width="200px">
    <HeaderStyle CssClass="CategoriesListHead" />
    <HeaderTemplate>
        Choose a Category
    </HeaderTemplate>
    <ItemTemplate>
       <asp:HyperLink ID="HyperLink1" Runat="server"
      NavigateUrl='<%# Link.ToCategory(Request.QueryString["DepartmentID"], Eval("CategoryID").ToString()) %>'
      Text='<%# HttpUtility.HtmlEncode(Eval("Name").ToString()) %>'
      ToolTip='<%# HttpUtility.HtmlEncode(Eval("Description").ToString()) %>'
      CssClass='<%# Eval("CategoryID").ToString() ==
               Request.QueryString["CategoryID"] ?
               "CategorySelected" : "CategoryUnselected" %>'>>
    </asp:HyperLink>
    </ItemTemplate>
</asp:DataList>


And the code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class UserControls_CategoriesList : System.Web.UI.UserControl
{
    protected void Page_Load(object sender, EventArgs e)
    {
        //Obtain the ID of the selected Department
        string departmentID = Request.QueryString["DepartmentID"];

        //continue only if DepartmentID exists in the query string
        if (departmentID != null)
        {
            //Catalog.GetCategoriesInDepartment returns a DataTable
            //object containing the data, which will be displayed in the
            //data list
            list.DataSource = CatalogAccess.GetCategoriesInDepartment(departmentID);

            //bind the datalist control
            list.DataBind();
        }
    }
}


The following is my business tier class that resolves the GetCategoriesInDepartment
 

public static DataTable GetCategoriesInDepartment(string departmentId)
    {
        //Get a configured DbCommand object
        DbCommand comm = GenericDataAccess.CreateCommand();

        //Set stored procedure name
        comm.CommandText = "CatalogGetCategoriesInDepartment";

        //Create a new parameter
        DbParameter param = comm.CreateParameter();
        param.ParameterName = "@DepartmentID";
        param.DbType = DbType.Int32;
        comm.Parameters.Add(param);

        //Execute the stored procedure
        return GenericDataAccess.ExecuteSelectCommand(comm);

    }


The above method in my business tier class calls another class in the business tier that handles Data Base related operations such as create and execute commands. The class  below handles both of the commands

using System;
using System.Collections.Generic;
using System.Web;
using System.Data;
using System.Data.Common;

/// <summary>
/// Class contains generic data access to be accessed from the BusinessTier
/// </summary>
public static class GenericDataAccess
{
    //Executes a command and returns the results as a DataTable object
    public static DataTable ExecuteSelectCommand(DbCommand command)
    {
        //DataTable object to be return
        DataTable table;

        //Execute the command, making sure the connection will be always closed

        try
        {
            //Open the data connection
            command.Connection.Open();

            //Execute the command and save the results into the DataTable object
            DbDataReader reader = command.ExecuteReader();
            table = new DataTable();
            table.Load(reader);

            //CLose the reader
            reader.Close();
        }
       catch (Exception ex)
        {
            Utilities.LogError(ex);
            throw;
        }
        finally
        {
            //Close the connection
            command.Connection.Close();
        }

        return table;
    }

    //Creates and prepares a nre DbCommand object on a new connection
    public static DbCommand CreateCommand()
    {
        //Get database provider name
        string dataProviderName = null;
       
        try
        {
            //string dataProviderName = BalloonShopConfiguration.DbProviderName;
            dataProviderName = BalloonShopConfiguration.DbProviderName;
        }
        catch (Exception ex)
        {
            ex.InnerException.ToString();
        }

        //Get the database connection string
        string connectionString = BalloonShopConfiguration.DbConnectionString;

        //Create a new data provider factory
        DbProviderFactory factory = DbProviderFactories.GetFactory(dataProviderName);

        //Get a database specific connection object
        DbConnection conn = factory.CreateConnection();

        //Set the connection string
        conn.ConnectionString = connectionString;

        //Create a database specific command object
        DbCommand comm = conn.CreateCommand();

        //Set the command type to stored procedure
        comm.CommandType = CommandType.StoredProcedure;

        //retun the initialized command object
        return comm;
    }


}



NOTE: Just remember that the class above uses a Web.Config value for the connection string. You will have to create your own, but below there is an example:
<connectionStrings>
<add name="Connection" connectionString="Data Source=Server; Database=DataBaseName; user=username; Password=password" providerName="System.Data.SqlClient"/>
</connectionStrings>

Finally, the following is the CSS code that will make the HyperLink control change styles depending on the categoryId being different to the Query String value retrieved (this is explained above)
.CategoriesList {
  border: #ea6d00 1px solid;
  text-align: center;
  margin-top: 20px;
}
.CategoriesListHead {
  border: #ea6d00 1px solid;
  background-color: #f8c78c;
}
a.CategoryUnselected {
  line-height: 25px;
  text-decoration: none;
  color: Black;
}
a.CategoryUnselected:hover {
  text-decoration: underline;
}
a.CategorySelected {
  line-height: 25px;
  font-weight: bold;
  text-decoration: none;
  color: Black;
}

.CatalogTitle {
  color: red;
  font-size: 24px;
  font-weight: bold;
}
.CatalogDescription {
  color: Black;
  font-weight: bold;
  font-size: 14px;
}



Happy programming!}
 

No comments:

Post a Comment

Thank you for your thoughts. Your comment will appear in my blog shortly after review.

Have a great day!