Archive for the ‘ASP’ Category

Overriding checkbox behaviour adding a confirm popup

Tuesday, December 16th, 2008

I recently needed to add a confirm dialog for an asp checkbox. The confirm popup should determine whether the checkbox is posted back to the server or returned to its original state. Merely adding a onclick=”return confirm(’blah blah’)” here will not work because the post back will never occur (see why below). Because of this I whipped up this quick easy hack that I will share with you.

I defined a typical checkbox that auto post backs when its change event occurs:


<asp:CheckBox AutoPostBack="true" Text="Status" ID="chkExample" runat="server" OnCheckedChanged="OnCheckedChangeLabel" />

The markup that is generated for us by asp then looks like this:

<input id="chkExample" type="checkbox" name="chkExample" checked="checked" onclick="javascript:setTimeout('__doPostBack(\'chkExample\',\'\')', 0)" />

So essentially what we want to do is bypass this __doPostBack call so that we can post back only when our confirm popup returns true, so lets write a javascript function to do just that…

    <script type="text/javascript">

        function OverrideCheckPostback() {
            if (confirm("Are you sure?")) {
                __doPostBack('chkExample', '');
                return true;
            }
            else
                return false;
        }
    </script>

And then in our PageLoad we add the onclick attribute and specify the our function to be called.

    protected void Page_Load(object sender, EventArgs e)
    {
        chkExample.Attributes.Add("onclick", "return OverrideCheckPostback();");
    }

Now lets look at the code that gets generated for us. As we can see the original generated post back call is still there, but will never be called because we return before it has a chance while returning the value of our confirm popup. The post back is now handled in our own function only when confirm() returns true (aka the user presses ‘OK’).


<input id="chkExample" type="checkbox" name="chkExample" onclick="return OverrideCheckPostback();setTimeout('__doPostBack(\'chkExample\',\'\')', 0)" />

Double click AJAX Accordion bug

Saturday, December 13th, 2008

This post will be a short one summarizing a work around for the double click problem associated with the accordion control from the ASP.NET Ajax Control Toolkit. This problem occurs when controls are placed within the panes of the accordion control. When the page is loaded it takes two clicks of the control before the actual click event will be invoked.

Lets recreate an example to demonstrate and recreate this problem. First we will create an accordion and assign a button and label within its content template. The label we will use to test when our event is actually invoked. I will use my previous post’s books catalog example for this.

        <ajaxToolkit:Accordion
            ID="accordionBooks"
            runat="server"
            SelectedIndex="0"
            HeaderCssClass="accordionHeader"
            HeaderSelectedCssClass="accordionHeaderSelected"
            ContentCssClass="accordionContent"
            FadeTransitions="false"
            FramesPerSecond="40"
            TransitionDuration="250"
            AutoSize="None"
            RequireOpenedPane="false"
            SuppressHeaderPostbacks="true"
            onitemdatabound="accordionBooks_ItemDataBound">

                <HeaderTemplate>
                    <p>
                        <asp:Literal ID="litCategory" runat="server" Text="Category:" />
                        <asp:Label ID="lblBookCategory" Text='<%# DataBinder.Eval(Container.DataItem, "category_name") %>' runat="server" />
                    </p>
                </HeaderTemplate>

                <ContentTemplate>

                    <asp:HiddenField ID="hdnCategoryId" Value='<%# DataBinder.Eval(Container.DataItem, "id") %>' runat="server" />

                    <asp:Label ID="lblInvokeStatus" Text="Not Invoked" runat="server" />
                    <asp:Button ID="btnInvokeEvent" runat="server" Text="Invoke Event" OnClick="OnClickInvoke" />
                </ContentTemplate>
            </ajaxToolkit:Accordion>

And the code behind event…

    protected void OnClickInvoke(object sender, EventArgs e)
    {
        Label invokeLabel = (Label)
            accordionBooks.Panes[accordionBooks.SelectedIndex].FindControl("lblInvokeStatus");

        invokeLabel.Text = "Invoked!";
    }

Now, if you were to run the above code on a databound accordion you should see the double click problem occuring. The label will not change until the second click, but after that it will work each consecutive time.

So what I will now do is fake the button click, by applying our asp button outside our accordion where it will not be affected, and then force its click event from a button within the accordion using some javascript.

        <asp:Button ID="btnInvokeEvent" runat="server" Text="Invoke Event" OnClick="OnClickInvoke" />

        <ajaxToolkit:Accordion
            ID="accordionBooks"
            runat="server"
            SelectedIndex="0"
            HeaderCssClass="accordionHeader"
            HeaderSelectedCssClass="accordionHeaderSelected"
            ContentCssClass="accordionContent"
            FadeTransitions="false"
            FramesPerSecond="40"
            TransitionDuration="250"
            AutoSize="None"
            RequireOpenedPane="false"
            SuppressHeaderPostbacks="true"
            onitemdatabound="accordionBooks_ItemDataBound">

                <HeaderTemplate>
                    <p>
                        <asp:Literal ID="litCategory" runat="server" Text="Category:" />
                        <asp:Label ID="lblBookCategory" Text='<%# DataBinder.Eval(Container.DataItem, "category_name") %>' runat="server" />
                    </p>
                </HeaderTemplate>

                <ContentTemplate>

                    <asp:HiddenField ID="hdnCategoryId" Value='<%# DataBinder.Eval(Container.DataItem, "id") %>' runat="server" />

                    <asp:Label ID="lblInvokeStatus" Text="Not Invoked" runat="server" />
                    <input type="button" value="Invoke Event" onclick="javascript:$get('<%= btnInvokeEvent.UniqueID %>').click();" />

                </ContentTemplate>
            </ajaxToolkit:Accordion>

And now the click event should fire on the first click. When I tested this on an accordion with the panes hardcoded the bug didn’t show up, so it is likely that you will not need to use this workaround unless you are data binding your accordion. Hope this helped.

AJAX Toolkit: Accordion control with nested gridview

Friday, December 12th, 2008

The accordion control is a great control to add both effect and functionality to your ASP.NET Ajax enabled site. It presents data in a sleek way and allows for good use of screen space only showing you what you really need to see in a parent child fashion.

Today I am going to demonstrate the ability of nesting gridviews within an accordion control. This will allow you to map and display 1..* relationships in an efficient and logical way. In future posts I will also be showing how to extend this to use the modal popup control to allow easy editing of the data.

The example I am using is a simple books catalog database, where books are mapped to their related category. Here is the SQL for generating the database table structure;

DROP TABLE Book;
DROP TABLE BookCategory;

CREATE TABLE BookCategory (
	id					INT IDENTITY(1,1) PRIMARY KEY,
	category_name		VARCHAR(25) NOT NULL
);

CREATE TABLE Book (
	id					INT IDENTITY(1,1) PRIMARY KEY,
	title				VARCHAR(50) NOT NULL,
	publisher			VARCHAR(25) NOT NULL,
	ISBN				VARCHAR(10) NOT NULL,
	category_id			INT REFERENCES BookCategory(id)
);

Assuming you have the AJAX ASP.NET Control Toolkit installed the first thing will be too add an accordion control to your page. Define both a header and content section within the accordion. These templates will allow you to customize how the data is displayed. The header will represent our parent record details and when expanded the gridview will present the many rows associated to it.

        <asp:ScriptManager ID="ScriptManager" runat="server">
        </asp:ScriptManager>

        <ajaxToolkit:Accordion
            ID="accordionBooks"
            runat="server"
            SelectedIndex="0"
            HeaderCssClass="accordionHeader"
            HeaderSelectedCssClass="accordionHeaderSelected"
            ContentCssClass="accordionContent"
            FadeTransitions="false"
            FramesPerSecond="40"
            TransitionDuration="250"
            AutoSize="None"
            RequireOpenedPane="false"
            SuppressHeaderPostbacks="true"
            onitemdatabound="accordionBooks_ItemDataBound">

                <HeaderTemplate>
                    <p>
                        <asp:Literal ID="litCategory" runat="server" Text="Category:" />
                        <asp:Label ID="lblBookCategory" Text='<%# DataBinder.Eval(Container.DataItem, "category_name") %>' runat="server" />
                    </p>
                </HeaderTemplate>

                <ContentTemplate>

                    <asp:HiddenField ID="hdnCategoryId" Value='<%# DataBinder.Eval(Container.DataItem, "id") %>' runat="server" />

                    <asp:GridView
                    ID="gvBooks"
                    runat="server"
                    DataKeyNames="Id"
                    AutoGenerateColumns="false"
                    Width="100%">

                        <Columns>
                            <asp:BoundField DataField="id" runat="server" />
                            <asp:TemplateField>
                                <ItemTemplate>
                                    <asp:Label ID="lblTitle" Text='<%# DataBinder.Eval(Container.DataItem, "Title") %>' runat="server" />
                                </ItemTemplate>
                            </asp:TemplateField>

                            <asp:TemplateField>
                                <ItemTemplate>
                                    <asp:Label ID="lblPublisher" Text='<%# DataBinder.Eval(Container.DataItem, "Publisher") %>' runat="server" />
                                </ItemTemplate>
                            </asp:TemplateField>

                            <asp:TemplateField>
                                <ItemTemplate>
                                    <asp:Label ID="lblISBN" Text='<%# DataBinder.Eval(Container.DataItem, "ISBN") %>' runat="server" />
                                </ItemTemplate>
                            </asp:TemplateField>

                        </Columns>
                    </asp:GridView>
                </ContentTemplate>
            </ajaxToolkit:Accordion>

Above you can see that we bind our category name to our header template. The content template then contains the gridview which contains the details of each book.

Essentially we will be binding our data in two steps. On page load we will bind our accordion to the book categories from our ‘BookCategory’ table. The category name will be assigned to the label within the header template. The Id of the category will be stored in a hidden field within the content template. This will allow us to get the id needed to be able to bind our books gridview by category type later in the ‘item data bound’ event.

    protected void Page_Load(object sender, EventArgs e)
    {
        try
        {
            accordionBooks.DataSource = BookDB.GetAllBookCategories().Tables[0].DefaultView;
            accordionBooks.DataBind();
        }
        catch
        {
            DisplayError("Error while getting data");
        }
    }

Note that we are using the default view here. While writing this post I forgot this step and couldn’t figure out why I was getting no data showing up. You cannot assign the data source a data table or dataset, however generic list collections seem to bind fine.

The second step to binding is using the the item data bound event, which allows us to bind our data to our gridview as we move through each accordion item. We need to perform a check for item type within this function because it is executed for both header and content sections. We obviously want the content section. We also use the AccordionItemEventArgs to get the current accordion item so that we can find the gridview and hidden field controls and reference them specifically. We then extract the id, perform a database query and bind the gridview with the books related to that category.

    protected void accordionBooks_ItemDataBound(object sender, AjaxControlToolkit.AccordionItemEventArgs e)
    {
        try
        {
            if (e.ItemType == AjaxControlToolkit.AccordionItemType.Content)
            {
                GridView gdvBooks = (GridView)e.AccordionItem.FindControl("gvBooks");
                HiddenField hdnCategoryId = (HiddenField)e.AccordionItem.FindControl("hdnCategoryId");

                int categoryId;
                if (!int.TryParse(hdnCategoryId.Value, out categoryId))
                    throw new Exception("Unable to parse id");

                gdvBooks.DataSource = BookDB.GetAllBooksByCategoryId(categoryId);
                gdvBooks.DataBind();
            }
        }
        catch
        {
            DisplayError("Error while getting data");
        }
    }

And that is all that is there is to it. I have included my database code below for those who are interested as well as the stored procedures.

	public static DataSet GetAllBooksByCategoryId(int categoryId)
	{
        DataSet ds = null;
        try
        {
            using (SqlConnection conn = new SqlConnection("Data Source=LEAF-DEV\\SQLEXPRESS;Initial Catalog=BookCatalog;Integrated Security=SSPI;"))
            {
                conn.Open();
                using (SqlCommand cmd = new SqlCommand("spGetBooksByCategory", conn))
                {
                    cmd.Parameters.Add("@category_id", SqlDbType.Int).Value = categoryId;
                    cmd.CommandType = CommandType.StoredProcedure;

                    using (SqlDataAdapter da = new SqlDataAdapter(cmd))
                    {
                        ds = new DataSet();
                        da.Fill(ds);
                    }
                }
            }
        }
        catch(Exception ex)
        {
            throw ex;
        }
        return ds;
	}

    public static DataSet GetAllBookCategories()
    {
        DataSet ds = null;
        try
        {
            using (SqlConnection conn = new SqlConnection("Data Source=LEAF-DEV\\SQLEXPRESS;Initial Catalog=BookCatalog;Integrated Security=SSPI;"))
            {
                conn.Open();
                using (SqlCommand cmd = new SqlCommand("spGetBookCategories", conn))
                {
                    cmd.CommandType = CommandType.StoredProcedure;
                    using (SqlDataAdapter da = new SqlDataAdapter(cmd))
                    {
                        ds = new DataSet();
                        da.Fill(ds);
                        int rowCount = ds.Tables[0].Rows.Count;
                    }
                }
            }
        }
        catch (Exception ex)
        {
            throw ex;
        }
        return ds;
    }
USE BookCatalog;
DROP PROCEDURE spGetBookCategories;
DROP PROCEDURE spGetBooksByCategory;

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE PROCEDURE spGetBookCategories
AS
BEGIN
	SELECT category_name, id
	FROM BookCategory
END
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE PROCEDURE spGetBooksByCategory(@category_id INT)
AS
BEGIN
	SELECT
		id,
		title,
		publisher,
		ISBN,
		category_id
	FROM Book
	WHERE category_id = @category_id
END
GO

Gridview ObjectDataSource binding with multiple tiers

Sunday, September 28th, 2008

Binding a DataSource control to a gridview is a understandable way of grabbing data from your database and presenting it to the end user. However when building enterprise style applications it is often best to use a multi layer approach, usually consisting of a user layer, business logic layer, and data layer. Having a datasource directly connected and pulling data from the database does not really make as much sense in this style application especially if there is some logic that needs to be performed in the business layer before hand. This is where the value of the ObjectDataSource comes in. It allows you to use objects as your data source, providing you with the task of coding the functionality for pulling down the data.

What I will demonstrate below will be a simple use of a ObjectDataSource for simply pulling data from the database using the “select method” attribute.

Here is what our user layer source will look like.


<asp:ObjectDataSource
    TypeName="UserLib.User"
    SelectMethod="GetAll"
    ID="odsUsers"
    runat="server"
/>

<asp:GridView ID="gvUsers" DataSourceID="odsUsers" runat="server"
    AutoGenerateColumns="False">
    <Columns>
        <asp:TemplateField HeaderText="Username">
            <ItemTemplate>
                <asp:Label ID="lblUsernameRow" runat="server" Text='<%# Bind("username") %>'/>
            </ItemTemplate>
            <EditItemTemplate>
                <asp:TextBox ID="lblUsernameRow" runat="server" Text='<%# Bind("username") %>'></asp:TextBox>
            </EditItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>

So lets pull this apart. We define a object data source, specifying two important attributes “select method” and “typename”. The select method specifies the method which we want to call on our object to get our data. This method must return an object of IEnumerable or ICollection, so we can return a DataSet. The typename specifies the object that we will call the method on.

In the gridview we bind the datasource by specifying the “datasourceID” attribute and giving it the ID of our object datasource.


public DataSet GetAll()
{
    DataSet ds = null;
    try
    {
        UserData.User data = new UserData.User();
        ds = data.GetAll();
    }
    catch (Exception ex)
    {
        throw ex;
    }
    return ds;
}

Above is our business logic layer. Theres really no logic here, all we are doing is calling to our data layer to grab the data from the database and load it into a dataset for us. When we receive the dataset from the data layer we will then pass this dataset back to the user layer to be binded to the gridview.


        public DataSet GetAll()
        {
            SqlConnection conn  = null;
            SqlDataAdapter da   = null;
            SqlCommand cmd      = null;
            DataSet ds = null;

            try
            {
                conn = new SqlConnection(CONN_STRING);
                conn.Open();

                ds = new DataSet();

                cmd = new SqlCommand();
                cmd.Connection = conn;
                cmd.CommandText = "spGetAllUsers";
                cmd.CommandType = CommandType.StoredProcedure;

                da = new SqlDataAdapter(cmd);
                da.Fill(ds, "users");

            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                if (conn != null && conn.State == ConnectionState.Open)
                {
                    conn.Close();
                    conn.Dispose();
                }

                if (cmd != null)
                    cmd.Dispose();
                if (da != null)
                    da.Dispose();
            }
            return ds;
        }

Here is our data layer method which does all of our database interactions.

Today I only demonstrated the select method, but updates, deletes and inserts can also be setup and performed by the ObjectDataSource making it a very powerful and customizable way of interacting with the database.

Ensuring safe database resource cleanup even in failure cases

Sunday, September 28th, 2008

Often when I see database code on forums and newsgroups I notice resource management in a situation of failure is non-existent. Exceptions can be handled gracefully and allow the application to continue running, so why should we ignore the mess of open connections and resources in the background that are not cleaned up in most cases due to cleanup code being bypassed when an exception is thrown.

Allow me to give a typical example…

public DataSet Load(string username)
{
    DataSet ds = null;
    try
    {
        SqlConnection conn = new SqlConnection(CONN_STRING);
        SqlDataAdapter da = new SqlCommand();
        SqlCommand cmd = new SqlDataAdapter(cmd);

        cmd.CommandText = "GetUser";
        cmd.CommandType = System.Data.CommandType.StoredProcedure;
        cmd.Connection = conn;

        cmd.Parameters.Add("@username", SqlDbType.VarChar, 50).Value = username;

        conn.Open();
        da.Fill(ds, "user");

        conn.Close();

    }
    catch (System.Exception ex)
    {
        throw ex;
    }

    return ds;
}

If you look above you may think, “we are closing the connection so what is the problem?” By closing the connection within the try block, we risk that the call to close on the connection object may never be called if an exception is thrown from our data adapter after the connection is opened. We are also are relying on the garbage collector to dispose all of our current database resources which isn’t very efficient.

So lets clean this up a bit with the addition of a ‘finally’ to our try catch.

public DataSet Load(string username)
{
    DataSet ds = null;
    SqlConnection conn = null;
    SqlDataAdapter da = null;
    SqlCommand cmd = null;

    try
    {
        conn = new SqlConnection(CONN_STRING);
        cmd = new SqlCommand();
        da = new SqlDataAdapter(cmd);

        cmd.CommandText = "GetUser";
        cmd.CommandType = System.Data.CommandType.StoredProcedure;
        cmd.Connection = conn;

        cmd.Parameters.Add("@username", SqlDbType.VarChar, 50).Value = username;

        conn.Open();
        da.Fill(ds, "user");

    }
    catch (System.Exception ex)
    {
    throw ex;
    }
    finally
    {
        if (conn != null)
        {
            if(conn.State == System.Data.ConnectionState.Open)
                conn.Close();
            conn.Dispose();
        }

        if(cmd != null)
            cmd.Dispose();
        if(da != null)
            da.Dispose();
    }

    return ds;
}

The advantage of using a finally block here is that whether or not an exception is thrown or is not thrown, the finally block will always be executed. This gives us the perfect place to put any final cleanup and connection code that we must guarantee will be executed, even in the event of an exception.

Note the ‘null’ checks on all objects before calling close and dispose methods on them. We do this because cannot guarantee that the objects have been initialized when they reach the finally block because the code could have thrown an exception very early within the try block before full initialization took place. We also need to place the object declarations outside of the try catch in order for them to be in proper scope for the finally block.

It is great to handle exceptions gracefully and continue application execution but you also need to be aware of what is going on behind the scenes with resources. Overtime with applications throwing many exceptions and many connections being left open, you are likely to see a slight performance hit in your applications, especially with a large user base.

Now this is all good, we now have successful closing of our connections, we have disposing even when exceptions are thrown, but personally I don’t like how long this code has become. Add a SqlDataReader to this code and it grows even more. So what can we do while still ensuring that clean up will happen?

C# offers a great solution called “using”. “Using” provides scope for your resources, and ensures that resources and even connections are both disposed and closed when they reach the end of their life. Underneath the hood a try finally is being wrapped around the objects, and providing the closing and disposing calls for you. Lets see what our example looks like with this new syntax.

public DataSet Load(string username)
{
    try
    {
        using(SqlConnection conn = new SqlConnection(CONN_STRING))
        {
            conn.Open();
            using(SqlCommand cmd = new SqlCommand("spGetUser", conn))
            {
                cmd.CommandType = System.Data.CommandType.StoredProcedure;
                cmd.Parameters.Add("@username", SqlDbType.VarChar, 50).Value = username;

                using(SqlDataAdapter da = new SqlDataAdapter(cmd))
                {
                    da.Fill(ds, "users");
                }
            }
        }
    }
    catch (System.Exception ex)
    {
        throw ex;
    }
    return ds;
}

So as you can see our finally clause is now gone. We have also removed the need to declare our Sql objects at the beginning of our function and have moved the stored procedure and connection into the command object’s constructor. To me this provides much nicer code and organizes everything into nice workable chunks.

So in the end its really up to you which method you choose, but if you prefer organized code the latter will be the most likely choice. Remember, the more code you have to write the larger the chance for a bug to occur or a missed closing call.