Monday 7 February 2011

Generate RDLC dynamically with unique column headers

I use the great example from Gotreportviewer since about six month that uses Microsoft Reportviewer and my biggest problem is giving columns titles that different to field names.

There is my solution that has an additional property: List<string> Headers that contains column titles with the following restriction:

Number of headers must be equal to number of columns or TableRdlGenerator throws the following exception:
Error creating report: header and field columns must be set and have to be the same count of elements both of them!

The entire solution can be downloaded from my public folder on skydrive.

Sunday 6 February 2011

jQuery based collapse extender web user control for asp.net

After I have been started to learn jquery I decided to replace my Ajax Control Toolkit controls for a jQuery-based solution.

My control for replace in that week is the CollapsiblePanelExtender. I need a solution that works on partial page postbacks and full page postbacks too and stores collapsed state during postbacks, and, of course I have to be able to change the collapse state on server side so I had to mix server and client side code for the solution.

Note: at the end of the post, I share the zipped full Visual Studio 2010 solution.

The extender needs the following informations:

    • Collapsed: The state if the target control collapsed or not.
    • TargetControlID: The ID of the panel that will be collapsed.
    • ToggleControlID: The ID of the control that we will click on when toggle or collapse.
    • ToggleTextID: The ID of the control that shows different texts on different states.
    • ToggleImageID: The ID of the control that show different images on different states.
    • ExpandedText: Text to show when the control is expanded.
    • CollapsedText: Text to show when the control is collapsed.
    • ExpandedImage: Image to show when the control is expanded.
    • CollapsedImage: Image to show ehen the control is collapsed.

The code behind of the web user control:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace JQueryCollapse
{
    public partial class jQueryToggleExtender : System.Web.UI.UserControl
    {
        #region properties
        /// <summary>
        /// Defines if the panel to collapse is collapsed or not.
        /// </summary>
        public bool Collapsed
        {
            get
            {
                return hfCollapsed.Value == "1" ? true : false;
            }
            set { hfCollapsed.Value = (value ? 1 : 0).ToString(); }
        }

        protected string _TargetControlID;
        /// <summary>
        /// A conntrol to collapse and expand
        /// </summary>
        public string TargetControlID
        {
            protected get
            {
                return GetClientID(_TargetControlID);
            }
            set
            {
                _TargetControlID = value;
            }
        }

        protected string _ToggleControlID;
        /// <summary>
        /// A control to toggle.
        /// </summary>
        public string ToggleControlID
        {
            protected get
            {
                return GetClientID(_ToggleControlID);
            }
            set
            {
                _ToggleControlID = value;
            }
        }

        protected string _ToggleTextID;
        /// <summary>
        /// ID for toggle text control.
        /// </summary>
        public string ToggleTextID
        {
            protected get
            {
                return GetClientID(_ToggleTextID);
            }
            set
            {
                _ToggleTextID = value;
            }
        }

        protected string _ToggleImageID;
        /// <summary>
        /// ID for toggle image control
        /// </summary>
        public string ToggleImageID
        {
            protected get
            {
                return GetClientID(_ToggleImageID);
            }
            set
            {
                _ToggleImageID = value;
            }
        }

        /// <summary>
        /// Text to show when expanded
        /// </summary>
        public string ExpandedText { get; set; }
        /// <summary>
        /// Text to show when collapsed
        /// </summary>
        public string CollapsedText { get; set; }
        /// <summary>
        /// Image to show when expanded
        /// </summary>
        public string ExpandedImage { get; set; }
        /// <summary>
        /// Image to show when collapsed
        /// </summary>
        public string CollapsedImage { get; set; }
        #endregion

        /// <summary>
        /// Get a ClientID for a control by ID
        /// </summary>
        /// <param name="id">Id of the control</param>
        /// <returns></returns>
        protected string GetClientID(string id)
        {
            Control c = ControlUtils.FindControlRecursive(this.Page, id);

            return null != c ? c.ClientID : null;
        }
    }
}

In the markup we need to include our javascript files. For getting this work on partial page and full page postbacks and any other cases we rendering script block in InlineScripts as shown in my previous post before. We store collapsed state in a simple asp.net hiddenfield because we can reach it’s value on server and client side too.
Based on these the markup for the web user control is:

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="jQueryToggleExtender.ascx.cs"Inherits="JQueryCollapse.jQueryToggleExtender" %>
<asp:ScriptManagerProxy runat="server" ID="smp">
    <Scripts>
        <asp:ScriptReference Path="~/js/jquery-1.4.4.min.js" />
        <asp:ScriptReference Path="~/js/jquery-ui-1.8.8.custom.min.js" />
    </Scripts>
</asp:ScriptManagerProxy>
<wc:InlineScript runat="server" ID="inlineScripts">
    <script type="text/javascript">
        function pageLoad() {
            var collapsed = $get('<%= hfCollapsed.ClientID %>');

            $('#<%= ToggleControlID %>').click(
                function () {
                    if (collapsed.value == 1) {
                        $('#<%= ToggleImageID %>').attr('src', '<%= ExpandedImage %>');
                        $('#<%= ToggleTextID %>').text('<%= ExpandedText %>');
                    }
                    else {
                        $('#<%= ToggleImageID %>').attr('src', '<%= CollapsedImage %>');
                        $('#<%= ToggleTextID %>').text('<%= CollapsedText %>');
                    }

                    $('#<%= TargetControlID %>').toggle('blind', {}, 500);
                    collapsed.value = 1 - collapsed.value;
                });

            if (collapsed.value == 1) {
                $('#<%= TargetControlID %>').hide();
                $('#<%= ToggleTextID %>').text('<%= CollapsedText %>').html();
                $('#<%= ToggleImageID %>').attr('src', '<%= CollapsedImage %>');
            } else {
                $('#<%= ToggleTextID %>').text('<%= ExpandedText %>').html();
                $('#<%= ToggleImageID %>').attr('src', '<%= ExpandedImage %>');
            }
        }
    </script>
</wc:InlineScript>
<asp:HiddenField runat="server" ID="hfCollapsed" />

We have to register our control in the web.config before using:

<?xml version="1.0"?>

<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=169433
  -->

<configuration>
    <system.web>
        <pages>
            <controls>
                <!--Registering inline script-->
                <add tagPrefix="wc" assembly="JQueryCollapse" namespace="JQueryCollapse.WebServerControls" />
                <!--Registering toggle extender-->
                <add tagPrefix="wuc" tagName="Toggle" src="~/WebUserControls/jQueryToggleExtender/jQueryToggleExtender.ascx" />
            </controls>
        </pages>
        <compilation debug="true" targetFramework="4.0" />
    </system.web>

</configuration>

A simple example page for testing:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="JQueryCollapse.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>jquery toggle for asp.net</title>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server">
    </asp:ScriptManager>
    <asp:UpdatePanel runat="server" ID="upMain">
        <ContentTemplate>
            <asp:Button runat="server" ID="btnClickMe" Text="ClickMe" />
            <a id="hrefToggle" href="#" runat="server">
                <asp:Panel ID="pnToggle" runat="server" Width="100%" BackColor="Azure">
                    <asp:ImageButton runat="server" ID="imgCollapse" AlternateText="Click here to collapse or expand" />
                    <asp:Label runat="server" ID="lblCollapse"></asp:Label>
                </asp:Panel>
            </a>
            <asp:Panel runat="server" ID="pnContent">
                Hello
            </asp:Panel>
        </ContentTemplate>
    </asp:UpdatePanel>
    <asp:Button runat="server" ID="btnPb" Text="Postback" />
    <br />
    <asp:Button runat="server" ID="btnCollapse" OnClick="btnCollapse_Click" Text="Toggle" />
    <%--ToggleControlExtender--%>
    <wuc:Toggle runat="server" ID="tTest" TargetControlID="pnContent" ToggleControlID="hrefToggle"
        ToggleImageID="imgCollapse" ToggleTextID="lblCollapse" ExpandedText="Click here to collapse"
        ExpandedImage="Images/collapse.gif" CollapsedText="Click here to expand" CollapsedImage="Images/expand.gif"
        Collapsed="true"></wuc:Toggle>
    </form>
</body>
</html>

And the code behind of the page:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace JQueryCollapse
{
    public partial class Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
        }

        /// <summary>
        /// Toggle the extender
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void btnCollapse_Click(object sender, EventArgs e)
        {
            tTest.Collapsed = !tTest.Collapsed;
        }
    }
}

 

And at last the full source code of the solution can be downloaded my public folder on skydrive.