Let Us Go Then, You and I
As of December 2011, this topic has been archived. As a result, it is no longer actively maintained. For more information, see Archived Content. For information, recommendations, and guidance regarding the current version of Internet Explorer, see Internet Explorer Developer Center.
Jay Allen, Mark Davis, Heidi Housten, and Dan Mohr
Microsoft Corporation
November 6, 2001
It's been a strange month for the Web Team, folks. On the plus side, Microsoft's bodacious new operating system, Windows XP, finally hit retail shelves! We've been running our corporate and home systems on XP since RC1 (release candidate), and we can't recommend it highly enough; its enhanced UI and superb Plug-and-Play capability alone justify the upgrade.
On the minus side (unless you live in Arizona or New York), the Seattle Mariners' pennant race hopes were put to rest. We were so stunned that a member of our team—who shall, unlike his five children, remain nameless—blew a synaptic block and began channeling his freshman English literature courses. The result is a slightly purple tinge to this month's article titles, which we hope you'll find amusing, if not informative. Bonus points to anyone who can name the classic 20th-century poem from which the titles were borrowed. (We'll trust you not to cheat—contrary to popular belief, we really don't know whether you've used MSN Search lately.)
Fortunately, the Mariners' loss allows us to return to our regularly scheduled shenanigans—crafting DHTML wait screens, speeding up development with ASP.NET Web Controls and data binding, and adding pop-up menus to Internet Explorer with a little C++ magic (though you'll have to forgive the Sodo Mojo that crept into this last sample).
Contents
Lingered Upon the Pools—conjuring wait screens with DHTML
A Magic Lantern—adding custom pop-up menus to the Internet Explorer toolbars
The Works and Days of Hands—simplify your life with the ASP.NET DataGrid control
Lingered Upon the Pools
Dear Web Team:
I want to do something I have seen done in other Web pages where: (1) a form or request is submitted to the Web server; (2) the Web server sends back a Web page with a message "please wait while the request is being processed...this may take several seconds"; (3) the server does some processing; and (4) the final result is returned. I have been unsuccessful in reproducing this on my own, although I expect it is a simple solution. Any assistance would be very helpful.
Thanks,
Brian Grobbel
The Web Team Replies:
There's an adage about software performance that makes the obvious point that "the fastest thing to do is nothing at all." However, for those of us whose pages have work to do, we need to examine other options. One of the best techniques to utilize is asynchronous client-server communications. This increases your perceived performance by allowing the user to interact with the browser while your code is waiting for data from the server. Normal event handlers in script operate on a single thread that can block the Internet Explorer UI work as long as synchronous calls are made in the script code. To avoid this, you can use window.setTimeout as has been discussed in previous Web Team Talking columns. For client-server communications, you have a number of options including the download behavior, the XMLHttpRequest object, and the new webService behavior. These all allow for asynchronous data retrieval. Now, what if we want to have the data retrieval be asynchronous so the UI remains responsive, but at the same time keep the user from carrying out any further actions to affect the state of the code? Methinks a sample is in order!
<HTML>
<HEAD>
<TITLE>Fake Modal Dialog Sample</TITLE>
</HEAD>
<BODY STYLE="FONT-SIZE: 10pt; FONT-FAMILY: Verdana, Arial, Helvetica">
<SCRIPT LANGUAGE="JScript">
var NUMBER_OF_REPETITIONS = 40;
var nRepetitions = 0;
var g_oTimer = null;
function startLongProcess()
{
divProgressDialog.style.display = "";
resizeModal();
btnCancel.focus();
// Add a resize handler for the window
window.onresize = resizeModal;
// Add a warning in case anyone tries to navigate away or refresh the page
window.onbeforeunload = showWarning;
//
// Here's where you would normally kick off a long asynchronous process
// like a file download or a remote database operation. Here, we use
// our "long process" to simulate this process.
//
continueLongProcess();
}
function updateProgress(nNewPercent)
{
// Update our pseudo progress bar
divProgressInner.style.width = (parseInt(divProgressOuter.style.width)
* nNewPercent / 100)+ "px";
}
function stopLongProcess()
{
if (g_oTimer != null)
{
// Clear the timer so we don't get called back an extra time
window.clearTimeout(g_oTimer);
g_oTimer = null;
}
// Hide the fake modal DIV
divModal.style.width = "0px";
divModal.style.height = "0px";
divProgressDialog.style.display = "none";
// Remove our event handlers
window.onresize = null;
window.onbeforeunload = null;
nRepetitions = 0;
}
function continueLongProcess()
{
if (nRepetitions < NUMBER_OF_REPETITIONS)
{
// Set the timeout somewhere between 0 and .25 seconds
var nTimeoutLength = Math.random() * 250;
updateProgress(100 * nRepetitions / NUMBER_OF_REPETITIONS);
g_oTimer = window.setTimeout("continueLongProcess();", nTimeoutLength);
nRepetitions++;
}
else
{
stopLongProcess();
}
}
function showWarning()
{
//Warn users before they refresh the page or navigate away
return "Navigating to a different page or refreshing the window could
cause you to lose precious data.\n\nAre you *absolutely* certain you want
to do this?";
}
function resizeModal()
{
// Resize the DIV which fakes the modality of the dialog DIV
divModal.style.width = document.body.scrollWidth;
divModal.style.height = document.body.scrollHeight;
// Re-center the dialog DIV
divProgressDialog.style.left = ((document.body.offsetWidth -
divProgressDialog.offsetWidth) / 2);
divProgressDialog.style.top = ((document.body.offsetHeight -
divProgressDialog.offsetHeight) / 2);
}
</SCRIPT>
<INPUT TYPE="BUTTON" VALUE="Click Me!" ONLCICK="startLongProcess();">
<!-- BEGIN PROGRESS DIALOG -->
<DIV STYLE="BORDER: buttonhighlight 2px outset; FONT-SIZE: 8pt; Z-INDEX:
4; FONT-FAMILY: Tahoma; POSITION: absolute; BACKGROUND-COLOR: buttonface;
DISPLAY: none; WIDTH: 350px; CURSOR: default" ID="divProgressDialog"
onselectstart="window.event.returnValue=false;">
<DIV STYLE="PADDING: 3px; FONT-WEIGHT: bolder; COLOR: captiontext;
BORDER-BOTTOM: white 2px groove; BACKGROUND-COLOR: activecaption">
Downloading Requested Document
</DIV>
<DIV STYLE="PADDING: 5px">
Please wait while I download the document you requested.
</DIV>
<DIV STYLE="PADDING: 5px">
This may take several seconds.
</DIV>
<DIV STYLE="PADDING: 5px">
<DIV ID="divProgressOuter" STYLE="BORDER: 1px solid threedshadow;
WIDTH: 336px; HEIGHT: 15px">
<DIV ID="divProgressInner" STYLE="COLOR: white; TEXT-ALIGN:
center; BACKGROUND-COLOR: infobackground; MARGIN: 0px; WIDTH: 0px; HEIGHT:
13px;"></DIV>
</DIV>
</DIV>
<DIV STYLE="BORDER-TOP: white 2px groove; PADDING-BOTTOM: 5px; PADDING-TOP: 3px;
BACKGROUND-COLOR: buttonface; TEXT-ALIGN: center">
<INPUT STYLE="FONT-FAMILY: Tahoma; FONT-SIZE: 8pt" TYPE="button"
ID="btnCancel" onclick="stopLongProcess();" VALUE="Cancel">
</DIV>
</DIV>
<!-- END PROGRESS DIALOG -->
<!-- BEGIN FAKE MODAL DIV-->
<DIV ID="divModal"
STYLE="BACKGROUND-COLOR: white; FILTER: alpha(opacity=75); LEFT: 0px; POSITION:
absolute; TOP: 0px; Z-INDEX: 3"
onclick="window.event.cancelBubble=true; window.event.returnValue=false;">
</DIV>
<!-- END FAKE MODAL DIV -->
</HTML>
This code uses some nifty DHTML tricks (such as the large semi-opaque DIV) to essentially disable interaction with the contents of the page while carrying out its work. The pseudo-dialog box keeps the user appraised of the current progress and allows them to cancel the operation, just in case they're sick of waiting. Two other things of note—if our UI contained select boxes, we would need to set their display property to none while doing our work because they can paint over the top of our DIV (for the details see Knowledge Base article Q177378). Also, you cannot prevent users from refreshing the page or navigating away from a page (both of which would severely affect our long operation). You can present them with a pop-up dialog warning them before they take either of these actions. This is accomplished using a dynamic onbeforeunload handler.
A Magic Lantern
Dear Web Team:
I would like to add a pop-up menu to a custom toolbar button for IE (like the Mail button). I know this can be done, but I'm not sure where to start. Can you give me a hand?
Thanks,
Tom
The Web Team Replies:
Before we get too deep, if you haven't got the basics covered, you'll want to check out the Adding Toolbar Buttons documentation from MSDN, as well as the Toolbar Button Style Guide. So, in the spirit of the fall classic, what better way to illustrate adding a pop-up menu to a toolbar button than through baseball? Let's pretend our Mariners didn't just lose the American League Championship Series to the Yankees (we'd really like to just keep on pretending this didn't happen) and we want to our favorite players' stats a click or two away at all times. To this end, we're going to create a toolbar button with a pop-up menu full of player names. Clicking on these names will navigate the browser to the correct stats page. (We're aware that we could add these links to our Favorites menu, but that would take three clicks and we're really busy people!)
First, we create a new ATL DLL project in Visual C++®. Then, we add an ATL object with a dual interface that implements IObjectWithSite (using the default implementation, IObjectWithSiteImpl). Then, we add support for another interface IOleCommandTarget (we'll have to implement this one ourselves). This interface is necessary to support user interaction; without it, we're just another pretty face. At this point, we would also edit the .rgs file that ATL uses to register the component to follow the guidelines above. Icons for the button should also be added as resources. Now we get to the fun stuff. We'll override the default implementation of IObjectWithSite::SetSite and have our version store a pointer to the top-level browser window—we'll need it to navigate later.
The next step is to fully implement the methods for the IOleCommandTarget interface. Luckily, there are only two methods—QueryStatus and Exec. QueryStatus is used by the Internet Explorer toolbar to ask our button about its current state. Our implementation will simply reply that all commands are supported and enabled. The heavy lifting is done in Exec. Exec is fired when the user actually clicks on our button. In our implementation, we're going to do the following:
- Create our pop-up menu and submenus using the Win32 APIs.
- Get the cursor position and iterate through the toolbar buttons to determine our index.
- Use the special toolbar control messages to get more information about our button.
- Temporarily make our button appear pressed down (like the mail button).
- Display our pop-up menu and track the user's selection.
- Un-press our button.
- Navigate the browser to the appropriate player page as determined by the user's selection.
Here's the main chunk of code from our button implementation:
STDMETHODIMP CButtonObj::SetSite(IUnknown* pUnkSite)
{
// If pUnkSite is not NULL, a new site is being set.
if (pUnkSite)
{
IServiceProvider* pISP = NULL;
if (m_pWB)
{
m_pWB->Release();
m_pWB = NULL;
}
if (SUCCEEDED(pUnkSite->QueryInterface(IID_IServiceProvider, (void **)&pISP)))
{
if (FAILED(pISP->QueryService(IID_IWebBrowserApp, IID_IWebBrowser2,
(void **) &m_pWB)))
{
// Just in case
m_pWB = NULL;
}
pISP->Release();
pISP = NULL;
}
}
else
{
m_pWB->Release();
m_pWB = NULL;
}
return S_OK;
}
STDMETHODIMP CButtonObj::QueryStatus( const GUID* pguidCmdGroup, ULONG cCmds,
OLECMD* prgCmds, OLECMDTEXT* pCmdText )
{
HRESULT hr = OLECMDERR_E_UNKNOWNGROUP;
if (pguidCmdGroup && IsEqualGUID(*pguidCmdGroup, CLSID_ToolbarExtButtons))
{
for (ULONG i = 0; i < cCmds; i++)
{
// By default, we'll support all commands
prgCmds[i].cmdf = OLECMDF_ENABLED | OLECMDF_SUPPORTED;
// If we wanted to latch the button down, we could do this:
// prgCmds[i].cmdf |= OLECMDF_LATCHED;
}
hr = S_OK;
}
return hr;
}
STDMETHODIMP CButtonObj::Exec( const GUID* pguidCmdGroup, DWORD nCmdID,
DWORD nCmdexecopt, VARIANTARG* pvaIn, VARIANTARG* pvaOut )
{
HRESULT hr = S_OK;
if (pguidCmdGroup == NULL)
{
switch (nCmdID)
{
case 0:
// Called from toolbar button
{
HMENU hMenu, hSubMenuPitchers, hSubMenuFielders;
HWND hWndToolbar;
WCHAR wszURL[512];
LRESULT lResult;
TBBUTTONINFO tbbi;
POINT pt;
RECT rect = {0,0,0,0};
long nOpt = 0;
long nButtonCount = 0;
int nTargetButton = 0;
int i = 0;
tbbi.cbSize = sizeof(tbbi);
tbbi.dwMask = TBIF_COMMAND | TBIF_BYINDEX;
// Create our popup menus/submenus
hMenu = CreatePopupMenu();
hSubMenuPitchers = CreatePopupMenu();
AppendMenu(hSubMenuPitchers, MF_STRING, 119469, "Jamie Moyer");
AppendMenu(hSubMenuPitchers, MF_STRING, 119704, "Jeff Nelson");
AppendMenu(hSubMenuPitchers, MF_STRING, 121125, "Arthur Rhodes");
AppendMenu(hSubMenuPitchers, MF_STRING, 277408, "Kazuhiro Sasaki");
AppendMenu(hSubMenuPitchers, MF_STRING, 121986, "Aaron Sele");
AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT_PTR)hSubMenuPitchers, "Pitchers");
hSubMenuFielders = CreatePopupMenu();
AppendMenu(hSubMenuFielders, MF_STRING, 110816, "David Bell");
AppendMenu(hSubMenuFielders, MF_STRING, 111214, "Bret Boone");
AppendMenu(hSubMenuFielders, MF_STRING, 111904, "Mike Cameron");
AppendMenu(hSubMenuFielders, MF_STRING, 136722, "Carlos Guillen");
AppendMenu(hSubMenuFielders, MF_STRING, 118808, "Mark McLemore");
AppendMenu(hSubMenuFielders, MF_STRING, 119976, "John Olerud");
AppendMenu(hSubMenuFielders, MF_STRING, 400085, "Ichiro Suzuki");
AppendMenu(hSubMenuFielders, MF_STRING, 124383, "Dan Wilson");
AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT_PTR)hSubMenuFielders, "Fielders");
GetCursorPos(&pt);
hWndToolbar = WindowFromPoint(pt);
nButtonCount = (long)SendMessage(hWndToolbar, TB_BUTTONCOUNT, 0, 0);
ScreenToClient(hWndToolbar, &pt);
for (i=0; i < nButtonCount; i++)
{
// We're going to try to figure out which button we are
// by seeing which one the cursor is inside
lResult = SendMessage(hWndToolbar, TB_GETITEMRECT, (WPARAM)i, (LPARAM)&rect );
if (PtInRect(&rect, pt))
{
// Found it
nTargetButton = i;
// Break out of the loop
i = nButtonCount;
}
}
// Get the button command index
SendMessage(hWndToolbar, TB_GETBUTTONINFO, (WPARAM)nTargetButton, (LPARAM)&tbbi);
// Make the button appear pressed
SendMessage(hWndToolbar, TB_CHECKBUTTON, (WPARAM)tbbi.idCommand, (LPARAM)TRUE);
// Translate our coordinates so the menu shows up in the right place
pt.x = rect.left;
pt.y = rect.bottom;
ClientToScreen(hWndToolbar, &pt);
// Get the user selection
nOpt = TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hWndToolbar, NULL);
DestroyMenu(hSubMenuPitchers);
DestroyMenu(hSubMenuFielders);
DestroyMenu(hMenu);
// Make the button not appear pressed
SendMessage(hWndToolbar, TB_CHECKBUTTON, (WPARAM)tbbi.idCommand, (LPARAM)FALSE);
if (nOpt != 0 && m_pWB != NULL)
{
// Navigate the browser!
VARIANT vEmpty;
vEmpty.vt = VT_EMPTY;
VARIANT vURL;
swprintf(wszURL, L"http://www.myfakestatssite.com/player.asp?id=%d", nOpt);
vURL.vt = VT_BSTR;
vURL.bstrVal = SysAllocString(wszURL);
m_pWB->Navigate2(&vURL, &vEmpty, &vEmpty, &vEmpty, &vEmpty);
SysFreeString(vURL.bstrVal);
}
break;
}
default:
hr = OLECMDERR_E_NOTSUPPORTED;
break;
}
}
else
{
hr = OLECMDERR_E_UNKNOWNGROUP;
}
return hr;
}
The Works and Days of Hands
Dear Web Team:
I love how the ASP.NET DataGrid Web Control is so easy to use, but I'd like to allow the user to select a row by clicking any column, rather than having a specific selection column as shown in many samples.
Nigel
The Web Team replies:
One of the great benefits of using Microsoft ASP.NET and Microsoft Visual Studio® .NET is that you can quickly develop a Web application to perform tasks that would have taken many lines of code using ASP and DHTML. Using ASP.NET Web Forms and Controls to develop your next Web application will enable you to forget much of the server/client, cross-browser, and language-specific nature of Web development and focus on what you do best—deliver great features to your customers. Of course, when anything is made easier for you, there will be times when these advantages may not always deliver the exact solution that you require. This is when you'll want to take a look under the hood of ASP.NET Web Forms and Controls.
The DataGrid is an ASP.NET Web Control that can be used on a Web Form. The DataGrid provides a view of your data that is output as a standard HTML table. You simply specify the source of the data and the control styles. You can specify the columns to be displayed and the commands, such as select, sort and edit, you want to support. You can do all this in just a few lines of code. (Note: runat="server" indicates that this is a server-side control). Simply drag and drop the DataGrid control on to a Web Form if you're using Visual Studio .NET. Here is a simple declaration of a DataGrid as it appears in a Web Form (.ASPX) file:
<form id="Form1" method="post" runat="server"> <asp:DataGrid ID="mygrid" Runat="server" /> </form>
The following code is all that is required in the C# code behind the Web Form, in addition to the code provided by Visual Studio .NET, to display the titles table of everyone's favorite database.
using System.Data.SqlClient;
protected DataGrid mygrid;
private void Page_Load(object sender, System.EventArgs e)
{
if ( ! IsPostBack )
LoadGrid();
}
private void LoadGrid()
{
SqlConnection sc = new SqlConnection("server=(local);database=pubs;
Integrated Security=SSPI");
DataSet ds = new DataSet();
SqlDataAdapter da = new SqlDataAdapter( "", sc );
da.SelectCommand.CommandText = "select * from titles";
// Retrieve the data set
da.Fill( ds );
// Specify the data source
mygrid.DataSource = ds;
// Bind the data to the grid
mygrid.DataBind();
}
Providing row selection is as easy as specifying a button column that supports the select command:
<form id="Form1" method="post" runat="server">
<asp:DataGrid ID="mygrid" Runat="server">
<SelectedItemStyle BackColor="Cornsilk" />
<Columns>
<asp:ButtonColumn CommandName="Select" Text="Select"/>
</Columns>
</asp:DataGrid>
</form>
Note that the AutoGenerateColumns property is true by default so that, in addition to the selected column, there will be one column for each of our data set fields. The user can click on the first column to select the row. The SelectedItemStyle tag allows us to specify the style of the selected row.
To allow the user to select a row by clicking on any column, you can provide client script in your ASPX file that will be called whenever the table is clicked:
<!-- Hook up grid events -->
<SCRIPT LANGUAGE="jscript" FOR="mygrid" EVENT="onclick">
selectRow( "mygrid" );
</SCRIPT>
<SCRIPT LANGUAGE=jscript>
function selectRow( name )
{
// Get row that was clicked
var oRow = window.event.srcElement.parentElement;
// Get rows in table
var oRows = oRow.parentElement.parentElement.rows;
if ( oRows )
{
var nRows = oRows.length-1;
// Find the clicked row
for ( var i=1; i<nRows; i++ )
{
if ( oRows[i] == oRow )
{ // Found - post an event to the server
__doPostBack( name + ":_ctl" + (i+1) + ":_ctl0", "" );
return;
}
}
}
return;
}
</SCRIPT>
The client script locates the row that was clicked and simulates selection of the row by posting a click event for the first column back to the server. You can now set the Visible property of the ButtonColumn to false to hide this column. Note that when you hide the selection column in the above sample, the __doPostBack function is not available because there are no other controls on the page.
I've written the example script to work on Microsoft Internet Explorer 4.0 or higher. You could build an ASP.NET user control that makes use of the HttpBrowserCapabilities properties available from the ASP.NET Request object to determine what client script is required. Alternatively, you could rewrite this script to work on multiple browsers.
The Web Team in Short
Which a Minute Will Reverse
Q. Mark Davison wants to tell if the values on a form have changed from their default values before submitting it.
A. The magic property you want, Mark, is defaultValue. Set an onchange event handler for your element and use window.event.srcElement to obtain a reference to the firing element from within the event handler. If you decide to commit the user's new input, you can assign it to defaultValue to make it the new initial value.
function change() {
var currElem = window.event.srcElement;
window.alert(currElem.defaultValue);
// Perform validation on new value here.
Var isValid = PerformValidation(currElem.value);
if (isValid) {
currElem.defaultValue = currElem.value;
} else {
currElem.value = currElem.defaultValue;
}
}
And Indeed There Will Be Time
Q. Rani Gopakumar wants to know how to display the current system time on the client using ASP and VBScript.
A. To return the Date and Time relative to the server, use the Now function. You can extract the time part using the FormatDateTime function:
<% Response.Write "System time: " & FormatDateTime(Now(), vbLongTime) & "<br>" %>
Like a Patient Etherised Upon a TABLE
Q: G. Aguilar wants to know why he can't make his floating table appear above a SELECT element.
A: Knowledge Base article Q177378 speaks it to the people. The SELECT element is a windowed control, and windowed controls always appear over windowless controls—and, as of Internet Explorer 5.5, every other object in the Document Object Model besides SELECT is windowless. The only workaround is to craft your own windowless SELECT object using DHTML Behaviors, an ATL Full Control, or a Visual Basic UserControl with the Windowless property set to True.
I Shall Tell You All
Q: Alan Canning wants to know how to generate an e-mail on the client with prepared content in the Subject and Body.
A: Use the mailto: protocol with the Subject and Body variables initialized a la a URL QueryString. Then, assign the URL you created to the document.location object:
<SCRIPT>
function mail() {
var mailUrl = "mailto:webteam@microsoft.com?subject=Hello,
world&Body=This is my message to the Web Team.";
document.location = mailUrl;
}
</SCRIPT>
<BUTTON onclick="mail();">
Prepare mail message
</BUTTON>
Oh, Do Not Ask "What Is It?"
Q: David Taylor wants to know why the BrowserType object under IIS5 does not detect Internet Explorer 6.0 correctly.
A: BrowserType uses the browscap.ini file as its browser database. You'll need to update your browscap.ini (located in %SYSTEMDIR%\inetsrv) to include Internet Explorer 6 using the following User-Agent strings:
[Mozilla/4.0 (compatible; MSIE 6.*; Windows 95*)] [Mozilla/4.0 (compatible; MSIE 6.*; Windows 98*)] [Mozilla/4.0 (compatible; MSIE 6.*; Windows NT*)] [Mozilla/4.0 (compatible; MSIE 6.*)]
You can get an updated version of browscap.ini from Windows XP Professional, which will have hit shelves by the time you read this (go, Windows team!) or off of Windows Server 2003 (stay tuned). Unfortunately, the Windows XP Pro version shipped with a slight bug. The beta attribute for Internet Explorer 6 is set to true, when it should be false. Feel free to correct this oversight.
An Overwhelming Question
Q: Tom Glover and Kurt Petrucci want to know how to open the Print Preview dialog from client-side script.
A: We could find no good way to do this directly. Your best bet would be an ActiveX® control that retrieved the IWebBrowser2 interface of its host frame (see Knowledge Base article Q172763) and call its ExecWB() method with a command identifier of IDM_PRINTPREVIEW. Alternatively, you could host a WebBrowser control inside of the HTML page and call ExecWB() against the host (though, as Knowledge Base article Q183048 will tell you, we don't recommend this for various reasons).
The Web Team
Mark Davis is a software design engineer on the Internet Explorer SDK team. Mark originates from England and is currently training to climb the major summits in the Northwest.
Heidi Housten works as a Consultant with Microsoft Consulting Services in Sweden after spending some time in Developer Support and MSDN. It is only a rumor that she moved there to escape the drizzle of Seattle; she really went for the traditional crayfish parties in August.
Dan Mohr, an engineer with Microsoft Developer Support's Internet Client team, spends his free minutes recording bands in his basement, programming his Commodore 64, and extolling the virtues of late '70s punk rock.
Jay Allen, a Support Engineer for the Internet Client team in Microsoft Developer Support, longs for the integration of Notepad and Emacs Lisp. What little time is not consumed by his four children is usually spent reading math books, studying Japanese and programming in Haskell.