How to create an HTML table with frozen headers and columns

May 30, 2009

I have moved this article to a new blog location at my new company web site. The article may be found here: How to create an HTML table with frozen headers and columns


How to Style the Ajax Control Toolkit Calendar Extender control

May 9, 2009

I have moved this article to a new blog location at my new company web site. The article may be found here: How to Style the Ajax Control Toolkit Calendar Extender control


How to add a close button to the Ajax Control Toolkit Calendar Extender control

May 9, 2009

In this series of educational articles, I have been explaining how to enhancing the Ajax Control Toolkit Calendar Extender control to support a number of different modes, such as Day, Month, Quarter, Year, Decade. I have also introduced implemented the InitialView property, which allows you to select an initial mode that you want the calendar to display in. These features may be used to reduce the number of clicks required to select a birthdate from 9 to 4, which is a productivity improvement for anyone seriously using the calendar for data entry purposes

These are the articles so far:

How to patch the Ajax Control Toolkit CalendarExtender to add Decade support and InitialView – Part 1 

How to patch the Ajax Control Toolkit Calendar Extender control to show a Quarter view

How to patch the Ajax Control Toolkit Calendar Extender control to show a Day, Month, MonthYearDecade, Quarter, YearDecade and Decade view

The code for the starting point for these modifications is found here:

http://cid-5e237543fffb2891.skydrive.live.com/self.aspx/Public/AjaxControlToolkit-Framework3.5SP1withDecadeYearQuarterMonthDayandInitialView.zip

Unzip this code and place it in your projects folder. All code here is supplied under the Microsoft Public Licence, found here: http://ajaxcontroltoolkit.codeplex.com/license

The code:

To start with, you’ll need a Close Button. The close button is 15 pixels by 15 pixels. I have created one based on the Internet Explorer X button used to stop web pages from executing. You can download my image by right clicking this image and saving it onto your local drive:

Close Button for Ajax Control Toolkit calendar extender control

This close button image is called close-button.gif (note that it’s a dash, not an underscore).

Copy this image into the Calendar folder in the Ajax Control Toolkit, and set it’s Build Action to Embedded Resource. This is important, as without it, the image is not contained within the Ajax Control Toolkit dll.

Adding the close button image

Adding the close button image

Next, you need to tell the compiler about the image by making it a Web Resource. This is done by adding an entry at the top of the CalendarExtender.cs file, as follows. Note that I am using S5/E5 to tag changes.

Original code:


#region [ Resources ]

[assembly: System.Web.UI.WebResource("AjaxControlToolkit.Calendar.CalendarBehavior.js", "text/javascript")]
[assembly: System.Web.UI.WebResource("AjaxControlToolkit.Calendar.Calendar.css", "text/css", PerformSubstitution = true)]
[assembly: System.Web.UI.WebResource("AjaxControlToolkit.Calendar.arrow-left.gif", "image/gif")]
[assembly: System.Web.UI.WebResource("AjaxControlToolkit.Calendar.arrow-right.gif", "image/gif")]
#endregion

Modified code:


#region [ Resources ]

[assembly: System.Web.UI.WebResource("AjaxControlToolkit.Calendar.CalendarBehavior.js", "text/javascript")]
[assembly: System.Web.UI.WebResource("AjaxControlToolkit.Calendar.Calendar.css", "text/css", PerformSubstitution = true)]
[assembly: System.Web.UI.WebResource("AjaxControlToolkit.Calendar.arrow-left.gif", "image/gif")]
[assembly: System.Web.UI.WebResource("AjaxControlToolkit.Calendar.arrow-right.gif", "image/gif")]
//S5
[assembly: System.Web.UI.WebResource("AjaxControlToolkit.Calendar.close-button.gif", "image/gif")]
//E5
endregion

Next, we want to add a property to the Calendar Extender to allow us to turn the close button display on or off – not everyone wants a close button on their calendar. Most people are probably content with clicking off the calendar. I will add a property called ShowCloseButton to the extender control. In the CalendarExtender.cs file, I copy the EnabledOnClient property and paste it to the location next to it. Then I rename the variables to be consistent with the property I want.

Code added under EnabledOnClient property:


//S5
[DefaultValue(false)]
[ExtenderControlProperty]
[ClientPropertyName("showCloseButton")]
public virtual bool ShowCloseButton
{
    get { return GetPropertyValue("ShowCloseButton", false); }
    set { SetPropertyValue("ShowCloseButton", value); }
}
//E5

The ClientPropertyName attribute contains the client-side javascript variable name. So next we go into the CalendarBehavior.js file and add in a couple of javascript variables. The first is the showCloseButton variable, shown above, and the second is the placeholder variable for the close button html content that will be created when the close button is required to be displayed. This code goes in the declarations section of the CalendarBehavior.js file. I put mine just below the declaration for this._nextArrow = null;


/*S5*/
this._showCloseButton = false;
this._closeButton = null;
/*E5*/

Add the getter and setter next. I put mine just below the set_enabled function block.


//S5
get_showCloseButton: function() {
    /// <value type="Boolean">
    /// Whether this behavior is available for the current element
    /// </value>

    return this._showCloseButton;
},
set_showCloseButton: function(value) {
    if (this._showCloseButton != value) {
        this._showCloseButton = value;
        this.invalidate();
        this.raisePropertyChanged("showCloseButton");
    }
},
//E5

The function that builds the calendar in the first place is called _buildHeader. This is where we need to add code to test if the close button is supposed to display, create the div that contains the image, and attach the event handlers. This is also where the stylesheet class is attached to the div. The image itself is referenced from that stylesheet class, called ajax__calendar_close.


/*S5 - because the close button is floated right, it needs to go before the nextArrow div as that is
also floated right, and right floating reverses the order of display*/
if (this._showCloseButton) {
    var closeButtonWrapper = $common.createElementFromTemplate({ nodeName: "div" }, this._header);
    this._closeButton = $common.createElementFromTemplate({
        nodeName: "div",
        properties: {
            id: id + "_closeButton",
            mode: "close"
        },
        events: this._cell$delegates,
        cssClasses: ["ajax__calendar_close"]
    }, closeButtonWrapper);
}
/*E5*/

 

 Note in that code the reference to the mode property. That is the string that gets passed into the _cell_onclick event handler. So on clicking the close button image, we want the calendar to execute the same code that is executed when the Escape button is clicked. So I had a look in the _button_onkeypress function and saw the code that is being executed when the Escape key is pressed. I will copy that code into the _cell_onclick function, and add a case for the ‘close’ mode, as follows:

Original code:


case "prev":
case "next":
    this._switchMonth(target.date);
    break;

Modified code:


case "prev":
case "next":
    this._switchMonth(target.date);
    break;
/*S5*/
case "close":
    e.stopPropagation();
    e.preventDefault();
    this.hide();
    break;
/*E5*/

Finally, you need to dispose of the close button content correctly. In the dispose function, add the following modifications:

Original code:

if (this._nextArrow) {
    $common.removeHandlers(this._nextArrow, this._cell$delegates);
    this._nextArrow = null;
}

Modified code:

if (this._nextArrow) {
    $common.removeHandlers(this._nextArrow, this._cell$delegates);
    this._nextArrow = null;
}
/*S5*/
if (this._showCloseButton && this._closeButton) {
    $common.removeHandlers(this._closeButton, this._cell$delegates);
    this._closeButton = null;
}
/*E5*/

Finally, you need to add the calendar extender css class for the close button. Open the Calendar.css file, and just below the entry for .ajax__calendar_next, add the following line:


/*S5*/
.ajax__calendar_close {cursor:pointer;width:15px;height:15px;float:right;background-repeat:no-repeat;background-position:50% 50%;background-image:url(<%=WebResource("AjaxControlToolkit.Calendar.close-button.gif")%>);}
/*E5*/

 

Run it up, add a calendar extender, set it’s ShowCloseButton property to true, and this is what you see:

Calendar Close Button Image

Calendar Close Button Image

Note that I have deliberatelychosen a non-intrusive close button, the same size as the prev and next buttons. The close button just needs to be there – it doesn’t need to out-weigh the other elements on the calendar. I used to have a bright red button, but that is so 20th century.

If you would like to see if you’ve got the code right, I have a completed version to compare with here:

AjaxControlToolkit3.5SP1withModesAndCloseButton.zip


Sharepoint, AjaxControlToolkit, ModalPopup and DocType problems

May 4, 2009

I am currently integrating a Sharepoint web site with an ASP.Net web site. A Sharepoint web site is essentially an ASP.Net web site too, and Microsoft have provided a way to integrate exiting web sites into that, essentially by inheriting a Sharepoint master page.

My ASP.Net web site uses the AjaxControlToolkit. The problem is, the AjaxControlToolkit by default uses the XHTML DocType. Sharepoint doesn’t actually have a DocType specified, which means it defaults to HTML 4.0 transitional. That mode is ok. There are a number of reasons why you might want to use HTML 4.0 transitional. Most of the component libraries out there still use the old DocTypes because they offered a richer set of functionality. Recently I used the Intersoft control suite. These operates very strangely under  XHTML, and in fact they don’t support that mode.

But back to Sharepoint and the AjaxControlToolkit. I was trying to use the modal popup extender, so that I can have one of my forms displayed on clicking a button on the page. Without the DocType of XHTML, every time I clicked on the button, it would display the bottom-right hand corner of the dialog in the upper left-hand corner of my form, effectively clipping the control and making the control otherwise useless.

There are workarounds. You can go into the Modal Popup Extender and set the X and Y co-ordinates. The problem with that is that often you don’t know the exact co-ordinates where you want the popup to be displayed. Part of the desire to use the modal popup is because it centres on the page and will move with the page when you scroll. It also repositions itself when you adjust the form.

The answer to this is to obtain a re-write of the getClientBounds javascript function that goes into the AjaxControlToolkit source code, common.js file. This re-write may be obtained from here: http://forums.asp.net/t/1002123.aspx?PageIndex=1 and was put together by Umer Farooq of Islamabad.

Here it is repeated below:


getClientBounds : function() {
        /// <summary>
        /// Gets the width and height of the browser client window (excluding scrollbars)
        /// </summary>
        /// <returns type="Sys.UI.Bounds">
        /// Browser's client width and height
        /// </returns>

        var clientWidth;
        var clientHeight;
        switch(Sys.Browser.agent) {
            case Sys.Browser.InternetExplorer:
                if (document.documentElement && document.documentElement.clientWidth)
                    clientWidth = document.documentElement.clientWidth;
                else if (document.body)
                    clientWidth = document.body.clientWidth;
                //clientWidth = document.documentElement.clientWidth;
                if (document.documentElement && document.documentElement.clientHeight)
                    clientHeight = document.documentElement.clientHeight;
                else if (document.body)
                    clientHeight = document.body.clientHeight;
                //clientHeight = document.documentElement.clientHeight;               
                break;
            case Sys.Browser.Safari:
                clientWidth = window.innerWidth;
                clientHeight = window.innerHeight;
                break;
            case Sys.Browser.Opera:
                clientWidth = Math.min(window.innerWidth, document.body.clientWidth);
                clientHeight = Math.min(window.innerHeight, document.body.clientHeight);
                break;
            default:  // Sys.Browser.Firefox, etc.
                clientWidth = Math.min(window.innerWidth, document.documentElement.clientWidth);
                clientHeight = Math.min(window.innerHeight, document.documentElement.clientHeight);
                break;
        }
        return new Sys.UI.Bounds(0, 0, clientWidth, clientHeight);
    },

Basically, it works by detecting various instances of objects on the client in the Internet Explorer part of th code. If XHTML is present, document.documentElement.clientWidth exists, otherwise it will use the default document.body.clientWidth.

There is a second issue, that is to do with the positioning of the modal background. Basically, because the modal popup defaults to position:fixed, the background positions itself within the content part of the page. This is not desirable, as we want it to make the entire page inoperable while our dialog is showing. I actually don’t like the use of position:fixed, because it is unavailable in IE6. To solve this problem, simply find references to ‘fixed’ in the ModalPopupBehavior.js file in the AjaxControlToolkit and change them to ‘absolute’. You might want to alter the left and top values for the background element in this file too. I changed mine from zero (0) to minus 1 (-1).