Welcome to MSDN Blogs Sign in | Join | Help

inventive title

News, samples, tips and tricks for Windows Mobile, SharePoint, ASP.NET and the Ajax Control Toolkit
Promises and Futures in JavaScript

A Promise is a programming model that deals with deferred results in concurrent programming.  The basic idea around promises are that rather than issuing a blocking call for a resource (IO, Network, etc.) you can immediately return a promise for a future value that will eventually be fulfilled.  This allows you to write non-blocking logic that executes asynchronously without having to write a lot of synchronization and plumbing code.

For example, lets say we want to load an RSS feed via HTTP.  To accomplish this in JavaScript we have to leverage the XMLHttpRequest object.  The following example shows how one might load the RSS feed from this blog using conventional means:

   1:  function fetchSync(url) {
   2:    var xhr = new XMLHttpRequest();
   3:    xhr.open(url, "GET", false, null, null);
   4:    xhr.send(null);
   5:    if (xhr.status == 200) {
   6:      return xhr;
   7:    }
   8:    throw new Error(xhr.statusText);
   9:  }
  10:   
  11:  function fetchAsync(url, callback, errorCallback) {
  12:    var xhr = new XMLHttpRequest();
  13:    xhr.onreadystatechange = function() {
  14:      if (xhr.readyState == 4) {
  15:        if (xhr.status == 200) {
  16:          callback(xhr);
  17:        }
  18:        else {
  19:          errorCallback(new Error(xhr.statusText));
  20:        }
  21:      }
  22:    }
  23:    xhr.open(url, "GET", false, null, null);
  24:    xhr.send(null);
  25:  }

 

In the first example, the RSS feed is fetched synchronously (see line 3).  In this case the code is straightforward but the request is blocking, which means script execution is suspended until the request is completed or fails.  This is not optimal in an AJAX application as it can impact the interactivity of the page.

The second example attempts to alleviate the blocking call by introducing a callback that will be executed when the request completes.  This is advantageous as it is non-blocking, however the programming model can become inconsistent if you are working with different asynchronous data sources including loading local resources, performing computationally intensive tasks (such as xml transforms or other data transformation), etc.

To actually use the resulting feed you might have to do some additional work:

   1:  fetchAsync(
   2:    "http://blogs.msdn.com/rbuckton/rss.xml", 
   3:    function (rss) {
   4:      fetchAsync(
   5:        "/some/stylesheet.xsl",
   6:         function (xsl) {
   7:           document.getElementById("host").innerHTML = rss.responseXML.transformNode(xsl.documentElement);
   8:         },
   9:         function (e) {
  10:           document.getElementById("host").innerHTML = "Error: " + ex.message;
  11:         })
  12:    },
  13:    function (ex) {
  14:      document.getElementById("host").innerHTML = "Error: " + ex.message;
  15:    });

In this example we now have two asynchronous calls for content, however one depends on the other and as a result we can’t take advantage of concurrent requests to speed up the process. 

Now lets look at a concurrent example:

   1:  var rss = null;
   2:  var xsl = null;
   3:   
   4:  function tryComplete() {
   5:    if (rss != null && xsl != null) {
   6:      document.getElementById("host").innerHTML = rss.responseXML.transformNode(xsl.documentElement);
   7:    }
   8:  }
   9:   
  10:  fetchAsync(
  11:    "http://blogs.msdn.com/rbuckton/rss.xml",
  12:     function(rssXhr) {
  13:       rss = rssXhr;
  14:       tryComplete();
  15:     },
  16:     function (e) {
  17:       document.getElementById("host").innerHTML += "Error: " + e.message + "<br />";
  18:     });
  19:   
  20:  fetchAsync(
  21:    "/some/stylesheet.xsl",
  22:    function (xslXhr) {
  23:      xsl = xslXhr;
  24:      tryComplete();
  25:    },
  26:     function (e) {
  27:       document.getElementById("host").innerHTML += "Error: " + e.message + "<br />";
  28:     });

Now the above example can utilize concurrent requests and synchronize the results, but wow that’s a lot code.

Using promises you can instead do the following:

   1:  var prss = Promise.fetch("http://blogs.msdn.com/rbuckton/rss.xml");
   2:  var pxsl = Promise.fetch("/some/stylesheet.xsl");
   3:  Promise.whenOnly(
   4:    Promise.join(prss, pxsl),
   5:    function() {
   6:      var rss = prss.get_value();
   7:      var xsl = prss.get_value();
   8:      document.getElementById("host").innerHTML = rss.responseXML.transformNode(xsl.responseXML);
   9:    }, 
  10:    function(ex) {
  11:      document.getElementById("host").innerHTML = "Error: " + ex.message;
  12:    });

 

 

Another feature of Promises is pipelining, the ability to create a series of operations based on the result of promises.  The Promise.when() function is used to pipeline a promise, creating a new Promise based on the eventual output of the source.  For example:

   1:  var pfeed = Promise.fetch("somefeed.xml");
   2:   
   3:  var pfeedXml = Promise.when(pfeed, function(feed) { return feed.responseXML; });
   4:  Promise.whenOnly(pfeedXml, function(feedXml) { alert(feedXml.xml); });
   5:   
   6:  var pfeedText = Promise.when(pfeed, function(feed) { return feed.responseText; });
   7:  Promise.whenOnly(pfeedText, function(feedText) { console.log(feedText); });

On line 1 we fetch an xml document from the server.  On line 3 we create a pipeline that will eventually convert the response into an XML document.  On line 4 when the document is finally available we can alert the user.  On line 6 we create a pipeline that will eventually get the response value as text.  On line 7 we can log it to the console.

Here’s an example of using pipelined Promises to get the public twitter timeline in JSONP:

   1:  Promise
   2:    .jsonp("http://twitter.com/statuses/public_timeline.json")
   3:    .whenOnly(function(timeline) {
   4:      alert(timeline.length);
   5:    });

 

A promise can be created for other deferred actions as well. For example:

   1:  var pinput = new Promise(
   2:    function(fulfillPromise, breakPromise) {
   3:      var btn = document.getElementById("button");
   4:      var txt = document.getElementById("txt");
   5:      btn.onclick = function() {
   6:        fulfillPromise(txt.value);
   7:      }
   8:    }
   9:  );
  10:   
  11:  pinput.whenOnly(function(value) { alert(value); });

 

The above sample creates a promise for eventual input that you can act on at any point.

Whether a promise completes after a delay or very quickly (even synchronously), the programming model doesn’t change.  The Promise model can even handle present information, so that present and future data can be mixed within the model.  This is due to Promise.immediate(), which creates a promise that is fulfilled synchronously.  All of the “static” methods on the Promise object will implicitly create a Promise for any non-future data (data that is not already encapsulated in a Promise).

Here’s an example of a future factorial (based on the Wikipedia example):

   1:  function factorial(value) {
   2:    return new Promise(
   3:      function(fulfillPromise, breakPromise) {
   4:        setTimeout(
   5:          function() {
   6:            var result = 0;
   7:            do {
   8:              result += value;
   9:            }
  10:            while (--value > 0);
  11:            fulfillPromise(result);
  12:          },
  13:          1);
  14:      }
  15:    );
  16:  }
  17:   
  18:  var pfactorial = factorial(100000)
  19:    .when(function(result) { return result + 3; })
  20:    .whenOnly(function(result) { alert(result); });

This example uses setTimeout (at line 4) to ensure the request returns immediately and does not block linear execution of the code.

*NOTE: The attached API does not overcome the Same Origin Policy when used in the browser.  This means that you can only make requests back to the same domain as the caller.  Same Origin Policy does not apply for Gadgets (Windows 7 and Windows Vista) or for Widgets (Windows Mobile 6.5), which is where I’ve used this library for the most part. I have added some JSONP support in the library however JSONP is very limited.

Here is a quick overview of the current Promise API:

// constructor

var p = new Promise(initCallback) 
// initCallback: callback function that takes two 
//     parameters, the fulfillPromise and breakPromise 
//     callback functions.

// members

p.get_isComplete() 
// Gets whether the promise is complete
// returns: Boolean value that is True if the promise is 
//     complete

p.get_value()
// Gets the value of the promise or 'undefined' if the 
//     promise has not completed. If the promise was 
//     broken during execution an exception will be thrown
// returns: Object value 

p.call(name, params)
// Calls a named function on the future value of the 
//    promise
// name: String value for name of the field on the 
//     promise result object that contains a function 
//     to call
// params: parameter array of arguments to the 
//     function. specified as in-line arguments not as an
//     array: p.call("foo", 1, 2, 3)
// returns: a promise for the result of the call

p.getValue(name)
// Gets the value of a named field on the future
//    value of the promise
// name: String value for the name of a field on the
//     promise result object
// returns: a promise for the value of the field

p.setValue(name, value)
// Sets the value of a named field on the future
//     value of the promise
// name: String value for the name of a field on the
//     promise result object
// value: Object value to set on the field for the
//     promise result object
// returns: none

p.when(fulfillPromise, breakPromise, context)
// Creates a promise for the value of an action taken
//     on the future value of the promise
// fulfillPromise: Function that takes the value of the
//     promise when it completes and is used to act
//     on the future value.  The result is used as the 
//     value of the new promise
// breakPromise: Function that takes the exception
//     raised by the promise when it fails and is used
//     to act on the failure.  The result is used as the
//     value of the new promise
// context: Optional Object to use as the 'this' value
//     for the fulfillPromise or breakPromise callbacks
// returns: a Promise for the result of actions taken
//     in fulfillPromise or breakPromise

p.whenOnly(fulfillPromise, breakPromise, context)
// Enqueues an action to be taken on the future 
//     value of the promise
// fulfillPromise: Function that takes the value of the
//     promise when it completes and is used to act
//     on the future value.  
// breakPromise: Function that takes the exception
//     raised by the promise when it fails and is used
//     to act on the failure. 
// context: Optional object to use as the 'this' value
//     for the fulfillPromise or breakPromise callbacks
// returns: none

p.join(promises)
// Creates a new promise that completes when all
//    joined promises have completed
// promises: Parameter array of other promises
// returns: a new Promise


// static/type members

Promise.when(promise, fulfillPromise, breakPromise, context)
// Creates a promise for the value of an action taken
//     on the future value of the promise
// promise: The promise on which to act
// fulfillPromise: Function that takes the value of the
//     promise when it completes and is used to act
//     on the future value.  The result is used as the 
//     value of the new promise
// breakPromise: Function that takes the exception
//     raised by the promise when it fails and is used
//     to act on the failure.  The result is used as the
//     value of the new promise
// context: Optional Object to use as the 'this' value
//     for the fulfillPromise or breakPromise callbacks
// returns: a Promise for the result of actions taken
//     in fulfillPromise or breakPromise

Promise.whenOnly(promise, fulfillPromise, breakPromise, context)
// Enqueues an action to be taken on the future 
//     value of the promise
// promise: The promise on which to act
// fulfillPromise: Function that takes the value of the
//     promise when it completes and is used to act
//     on the future value.  
// breakPromise: Function that takes the exception
//     raised by the promise when it fails and is used
//     to act on the failure. 
// context: Optional object to use as the 'this' value
//     for the fulfillPromise or breakPromise callbacks
// returns: none

Promise.join(promises)
// Creates a new promise that completes when all
//    joined promises have completed
// promises: Parameter array of other promises
// returns: a new Promise

Promise.make(value)
// Makes a promise on a value.  If the value is a promise it 
//     is returned.  If the value is not a promise an immediate
//     promise is returned
// value: an Object that may or may not be a promise
// returns: a Promise for the value

Promise.broken(exception)
// Makes a promise that is immediately broken
// exception: The error for the promise
// returns: a broken Promise for the exception

Promise.immediate(value)
// Makes a promise that is immediately fulfilled
// value: The value for the promise
// returns: a fulfilled Promise for the value

Promise.fetch(url, method, username, password, headers, query, body)
// Creates a Promise for a completed XMLHttpRequest for the 
//    requested resource
// url: String value for the url
// method: Optional string value for the method. default is "GET"
// username: Optional string value for the username for Basic 
//     Authentication. default is 'null'
// password: Optional string value for the password for Basic
//     Authentication. default is 'null'
// headers: Optional Object containing name/value pairs for 
//     HTTP headers. default is 'null'
// query: Optional Object or String containing name/value pairs
//     to be added to the url as a querystring.  default is 'null'
// body: Optional Object or String containing the request
//     body for the request. A String is sent as-is while an Object
//     contains name/value pairs that are url form encoded. 
//     default is null
// returns: A Promise for the completed XMLHttpRequest

Promise.json(url, method, username, password, headers, query, body)
// Creates a Promise for a JSON object parsed from the result of an 
//    XMLHttpRequest for the requested resource.
//    This requires the JSON object which is Native in IE8.  If you 
//    wish to use this method on a browser that does not have the
//    native JSON object, please download www.json.org/json2.js
// url: String value for the url
// method: Optional string value for the method. default is "GET"
// username: Optional string value for the username for Basic 
//     Authentication. default is 'null'
// password: Optional string value for the password for Basic
//     Authentication. default is 'null'
// headers: Optional Object containing name/value pairs for 
//     HTTP headers. default is 'null'
// query: Optional Object or String containing name/value pairs
//     to be added to the url as a querystring.  default is 'null'
// body: Optional Object or String containing the request
//     body for the request. A String is sent as-is while an Object
//     contains name/value pairs that are url form encoded. 
//     default is null
// returns: A Promise for the JSON value of the response

Promise.jsonp(url, query, callbackArg)
// Uses JSONP to request a JSON resource
// url: String value for the url
// query: Optional Object or String containing name/value pairs
//     to be added to the url as a querystring.  default is 'null'
// callbackArg: Optional string that specifies the name of
//     a querystring argument used to hold the name of
//     a callback function. default is "callback"

Promise.js is licensed under the MSR-LA for non-commercial use only. The license is included in license.txt in the zip.

TFS Walkthrough Series: Part 3 - Opening the Source Control Explorer
  1. Open Microsoft Visual Studio 2008
  2. Ensure you have an active connection to Team Foundation Server (see Walkthrough: Connecting to Team Foundation Server)
  3. In the "Team Explorer" task pane, expand the Team Project
  4. Double-click on the Source Control folder.

    Figure 8 - Source Control Explorer

TFS Walkthrough Series: Part 2 - Viewing Work Items
  1. Open Microsoft Visual Studio 2008
  2. Ensure you have an active connection to Team Foundation Server (see Walkthrough: Connecting to Team Foundation Server)
  3. In the "Team Explorer" task pane, expand the Team Project (in this example: TeamProject) to the following folder: TeamProject \ Work Items \ Team Queries

    Figure 6 - Work Item Queries

  4. Double-click on a Team Query. This will open the list of work items that match the query.

    Figure 7 - Example Work Item Query

  5. Review or make any necessary changes and click Save (from the File menu or the disk icon, or press Ctrl+S) to update the server.
TFS Walkthrough Series: Part 1 - Connecting to Team Foundation Server
  1. Open Microsoft Visual Studio 2008
  2. In Visual Studio 2008, click the menu: Tools / Connect to Team Foundation Server
  3. In the "Connect to Team Foundation Server" dialog, click the button: Servers…

    Figure 3 - Connecting to a Team Foundation Server

  4. In the "Add/Remove Team Foundation Server" dialog, click the button: Add…

    Figure 4 - Managing the list of Team Foundation Servers

  5. In the "Add Team Foundation Server" dialog, enter the server name and click the button: OK

    Figure 5 - Adding a Team Foundation Server

  6. In the "Add/Remove Team Foundation Server" dialog, click the button: Close
  7. In the "Connect to Team Foundation Server" dialog, check each of the projects you wish to view and click: OK
  8. The "Team Explorer" tool pane should open for you to browse the Team Project.

Team Foundation Server Walkthrough Series: Introduction

The following is a series of walkthroughs for Microsoft Visual Studio 2008 Team Foundation Server. They range from simply making a TFS connection to setting up build configurations and branching scenarios.

  • Connecting to Team Foundation Server
  • Viewing Work Items
  • Opening the Source Control Explorer
  • Creating Workspaces
  • Checking in Pending Changes
  • Creating a DEVELOPMENT branch
  • Creating a PRODUCTION branch
  • Forward Integration from MAIN to DEVELOPMENT
  • Forward Integration from MAIN to PRODUCTION
  • Reverse Integration from DEVELOPMENT to MAIN
  • Creating a Build Definition
  • Queuing a Build
  • Managing Build Quality
  • Fixing issues in a PRODUCTION release
  • Performing a Baseless Merge
  • Sample Peer Code Review Process using Shelvesets
TFS Quick Tips: #1 Adding a screenshot to a work item

The Team Explorer in Visual Studio provides a quick (and not exactly obvious) method for attaching a screenshot or other content to a Work Item.

Screenshots

To quickly add a screenshot do the following:

1. Press PrtScrn or Alt+PrntScrn (Captures only the currently active window)

2. Open a Work Item (Bug, Task, etc) in team explorer

3. Click “File Attachments”

4. Press Ctrl+V (Paste)

A file called “Screenshot.png” is automatically attached to the work item.

Copied Text

To quickly add any copied text as an attachment to a work item do the following:

1. Select the text to copy (or press Ctrl+A to select all text in the active input area like a page or document)

2. Open a Work Item (Bug, Task, etc) in team explorer

3. Click “File Attachments”

4. Press Ctrl+V (Paste)

A file called “PastedText.txt” is automatically attached to the work item.

Copied Files

You can also quickly add a file from your desktop as an attachment using the same steps as above:

1. Select the files to copy

2. Open a Work Item (Bug, Task, etc) in team explorer

3. Click “File Attachments”

4. Press Ctrl+V (Paste)

Each of the files selected is now attached to the work item.

Bonus Tip – Copy and Paste Text from an Alert

If you get an alert in a page like so:

clip_image002

Simply press Ctrl+C (Copy) to copy the contents of the dialog into the clipboard. To paste the text you can just use Ctrl+V:

---------------------------

Windows Internet Explorer

---------------------------

You can copy this

---------------------------

OK

---------------------------

"Wheel of Time" author James Oliver Rigney, Jr. aka "Robert Jordan" passes away at 58

From the official website:

New York Times #1 best-selling author Robert Jordan passed away on Sunday September 16th at 2:45 PM EST. His passing was announced by his cousin Wilson on his official blog, hosted here.
[http://www.dragonmount.com/]

I've been reading Robert Jordan's novels for a long time and have all of the available "Wheel of Time" books featured prominently on my bookshelves at home.  His fantasy writings were complex and emotional, weaving together the lives of many characters through dark and trying times.  He died before completing the 12th and final volume of the "Wheel of Time" series although currently it is believed that enough of the final volume has been penned to be able to complete the series, although the exact time frame for the final volume is still unknown. 

The story is on CNN and additional information is available at the following fan sites:

  • wotmania.com
  • TarValon.net
  • Theoryland.com
  • Visual Studio "Browse with..." multiple browsers

    Here's a tip if you're doing a lot of cross browser ASP.NET or AJAX development in Visual Studio:

    • Right click an .aspx file and choose "Browse with..."

      image
    • If you're like me you've probably added some additional browsers to your "Browse with" list like FireFox, Opera and Safari.  If you want to view the page in multiple browsers, CTRL+click each item:

      image
    • Hit "Browse" and all of the browsers will open for the specified page.
    • If you want to open multiple browsers every time, click "Set as Default":

      image
    • Now all of the browsers will open whenever you choose "Browse" from the context menu.
    DOM Event inconsistencies between browsers

    I've been working on some bugs in the Calendar extender from the ASP.NET AJAX Control Toolkit and have been resolving some of the focus/blur events.  I've been trying to make the calendar work consistently across all of the browsers and found some frustrating differences in event firing order between the browsers.

    Here are a list of some of the DOM events supported by the various browsers I tested:

     

    Table 1 - DOM Events by Browser

    Event DOM Level IE FF OP SF
    DOMActivate Level 2       *
    activate 2 *      
    DOMFocusIn Level 2     1 1
    focusin 2 *      
    focus Level 0 * 1 1 1
    DOMDeactivate Level 2        
    deactivate 2 *      
    DOMFocusOut Level 2     1 1
    focusout 2 *      
    blur Level 0 * 1 1 1
    mousedown Level 0 * * * *
    mouseup Level 0 * * * *
    click Level 0 * * * *

    IE: Internet Explorer 7.0
    FF: Firefox 2.0.0.6
    OP: Opera 9.23
    SF: Safari 3.03 Beta (Windows)
    *: Implemented on any element
    1: Implemented only on specific elements
    2: IE-only implementation

     

    Internet Explorer uses activate/focusin/focusout instead of DOMActivate/DOMFocusIn/DOMFocusOut for events.  Interestingly only IE and Safari support DOMActivate/activate on any element.  Another thing I saw was that IE is the only browser which fires focus/blur events for any element.  Other browsers can support the focus/blur events on any element that has a tab index however.

    The next version of Calendar needs to support the following activation scenarios depending on whether it is also associated with a button:

    • No Button
      • Show Popup when textbox receives focus
      • Show Popup when textbox is clicked
      • Hide Popup when textbox loses focus
      • Hide Popup when textbox receives ESC keypress
      • Hide Popup when a date is selected
    • Button
      • Show Popup when the button is clicked
      • Hide Popup when the button loses focus
      • Hide Popup when the button receives ESC keypress
      • Hide Popup when a date is selected

    The issue is that when the textbox or button loses focus the popup should hide unless the focus is being directed at the popup itself.  The problem is that only IE recognizes a focus event on the popup's DIV tag when the popup is not part of the tab order (and we don't want the popup in the tab order). I was curious the order of DOM events fired in each browser so I wrote a small test page and stepped through for each.  The results are as follows:

     

    Table 2 - DOM Event Order by browser

    IE FF OP SF
    1. // click on INPUT
    2. INPUT: mousedown
    3. INPUT: activate
    4. INPUT: focusin
    5. INPUT: focus
    6. INPUT: mouseup
    7. INPUT: click
    8. // click on DIV
    9. DIV: mousedown
    10. INPUT: deactivate
    11. INPUT: focusout
    12. DIV: activate
    13. DIV: focusin
    14. INPUT: blur
    15. DIV: focus
    16. DIV: mouseup
    17. DIV: click
    18. // click away
    19. DIV: deactivate
    20. DIV: focusout
    21. DIV: blur
    1. // click on INPUT
    2. INPUT: mousedown
    3. INPUT: focus
    4. INPUT: mouseup
    5. INPUT: click
    6. // click on DIV
    7. DIV: mousedown
    8. INPUT: blur
    9. DIV: mouseup
    10. DIV: click
    11. // click away
    1. // click on INPUT
    2. INPUT: focus
    3. INPUT: DOMFocusIn
    4. INPUT: mousedown
    5. INPUT: mouseup
    6. INPUT: click
    7. // click on DIV
    8. INPUT: blur
    9. INPUT: DOMFocusOut
    10. DIV: mousedown
    11. DIV: mouseup
    12. DIV: click
    13. // click away
    1. // click on INPUT
    2. INPUT: mousedown
    3. INPUT: focus
    4. INPUT: DOMFocusIn
    5. INPUT: mouseup
    6. INPUT: click
    7. INPUT: DOMActivate
    8. // click on DIV
    9. DIV: mousedown
    10. INPUT: blur
    11. INPUT: DOMFocusOut
    12. DIV: mouseup
    13. DIV: click
    14. DIV: DOMActivate
    15. // click away

     

    Immediately a few things become obvious. First, I obviously cannot rely on focus/blur on the other browsers. IE would allow me to capture focusin on the DIV before the blur fires on the INPUT but that's just IE. Second, on all browsers except Opera the mousedown event of the DIV fires before the blur event of the INPUT. There's no clean sequential way to handle the focus transition in all browsers.

    In the current version of Calendar we're making use of a "threading" class I created for the toolkit called DeferredOperation.  DeferredOperation wraps a delegate and executes it asynchronously using setTimout.  It has some built-in synchronization semantics and was used to handle the focus changes while the popup DIV was part of the tab order (which again, we don't really want). This introduces a subtle delay between activation requests and is problematic on Safari (we had to wait a full second before processing the deferred operation to let safari to catch up).

    What I've opted for now is to do the best I can in three out of four of the browsers.  I track mousedown/mouseup using a flag and in Internet Explorer, Firefox, and Safari and monitor the flag in the blur event of the textbox. To handle the Opera inconsistency I again use a DeferredOperation to asynchronously handle the blur event which fixes the issue in that browser.

    The final issue I discovered was that in all browsers, clicking on the popup causes the textbox (or button) to lose focus which again causes problems with the tab order.  On the new Calendar I reset the focus to the textbox (or button) on mouseup on the popup.

    Hopefully these changes should make the next toolkit release so keep an eye out for the next update.

    Visual Studio Macro to collapse function and prototype blocks

    I usually use the handy VS macro "Hide Selection" (Ctrl+M,H) to collapse function and prototype blocks when doing Ajax development on a large script file. While it may not be the most efficient implementation at current, it gets the job done.

    Open JsUtilities.vb Open JsUtilities.vb JsUtilities.vb VB File
    8.9 KB
    JsUtilities.vb

    All content is provided "AS IS" and confers no warranties or rights.

    Skinning model for Calendar and Tabs in Ajax Control Toolkit

    This article was originally posted on 2/2/2007. The original host of the article is no longer available.  Due to the high demand for the content I am reproducing it in its entirety.  Click below for the full article.

    Read 'Skinning model for Calendar and Tabs in Ajax Control Toolkit'...

    Where have I been?

    Finally back on the blogging scene.  A lot has happened in the last 6 months and now that things are settling down I finally have a chance to start putting some content on my blog.

    First, a little about me.  My name is Ron Buckton and I am a Senior Consultant for Microsoft Services in the Federal District, Public Sector.  What this means is that I work primarily with Public Sector government contracts for MCS.  I started with Microsoft back in May after a number of interviews and trips back and forth to Redmond, WA and Washington, DC.  Prior to joining Microsoft I was working for a Microsoft partner in Cleveland, OH where I previously did most of my blogging.

    Back in 2006 I joined the Ajax Control Toolkit as a contributor and among my contributions number the Calendar, Tabs, DropDown, and a number of other smaller extenders and numerous architectural features within the toolkit such as ScriptControl and ScriptUserControl.  I had a number of useful blog posts about the toolkit on my former blog but unfortunately the blog was taken down shortly after I left and I have yet to have the time to pull those posts out of my archives to republish here.

    A number of people on the ASP.Net forums have been asking for me to republish the content in some form, specifically the posts revolving around skinning the Calendar and Tabs controls using CSS.  I plan on having those posts cleaned up and published as articles on this blog before too much longer.

    Aside from the Toolkit and my original ASP.Net development focus I am now very heavily involved in mobile device development.  As a result you'll see a fair amount of mobility content in addition to some of my other favorite topics.

    Other than this introductory post I'll try to keep the signal very high and the content coming as frequently as I can. Here's what to expect in the coming weeks assuming I can find the time between a heavy workload, family, and other obligations:

    • Ajax
      • Skinning Calendar and Tabs (redux)
      • Upcoming Calendar and Tabs bugfixes and enhancements
      • My mile-long wishlist of control/extender ideas after I get my bug-count down
      • Ajax unit testing
    • Mobile
      • Mobile UI wizardry
      • BackgroundWorker for Compact Framework
      • Mobile unit testing with Visual Studio 2008
    • Workflow
    • WPF
    • Silverlight

    If any of this interests you, subscribe and comment. Until then, happy coding.

    Page view tracker