Export (0) Print
Expand All

A Fond Farewell

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 Tosh Meston
Microsoft Corporation

April 28, 2003

In this final episode of the Web Team Talking, we show off some cool stuff we've been working on and then we answer a few last questions in the good ol' Web Team in Short format.

We've all had a great time on this column. It has been a good long run, thank you for reading, and thanks for all your questions over the years! And for those of you who haven't hung on our every word, check out our archives; you'll find much of it is still very relevant and interesting. For you more adventurous readers, here are a couple of things we think you might find interesting that we didn't manage to squeeze in:

And here are a few of the cool technologies we've been having fun exploring.

SQLXML—Ask a SQL Server question and get an XML answer
Internet Explorer Web Controls: Treeview—Web navigation made easy
Web Team in Short

SQLXML

With Microsoft SQL Server 2000 it is possible to post a query directly to SQL Server and receive XML back using SQLXML, now up to Version 3.0. We'll show you a little example using the Northwind database. We'll just skim over the functionality to give you the idea and if you're eager for more, follow the links in the resource section for in-depth information.

Let the fun begin. The first step is to set up a virtual directory mapping for SQL. On the start menu under Microsoft SQL Server select the option Configure SQL XML support in IIS. Select your Web site and then select new -> virtual directory. We will need to fill in most of the tabs in this dialog, but we can start with the first three. You need to establish the virtual directory name, the user credentials, and the database to use on your system. We chose to use the virtual directory name nwind, pointed to the Northwind database and added a SQL user with permissions only for the Northwind database.

A SQL XML query can be executed using a SQL expression or stored procedure call, including parameters, in the URL: (http://localhost/nwind?sql=select ProductName from Products for xml auto&root=root). 'Allow sql=… or template=… queries in URL' must be enabled in the configuration tool for this sort of query to work. For complex SQL statements, or for using SQL XML in an internet environment, it might be better to use templates. A template encapsulates the query in an XML document. Here is an extension of the URL query we made above:

<ROOT xmlns:sql="urn:schemas-microsoft-com:xml-sql" sql:xsl="tree.xsl">
<sql:query>
SELECT Categories.CategoryName, Products.ProductName
FROM Categories INNER JOIN Products ON Categories.CategoryID = Products.CategoryID
ORDER BY Categories.CategoryName,Products.ProductName
for xml auto
</sql:query>
</ROOT>

The XML returned by this example looks like:

<ROOT xmlns:sql="urn:schemas-microsoft-com:xml-sql">
   <Categories CategoryName="Beverages">
      <Products ProductName="Chai"/>
      <Products ProductName="Chang"/>
   </Categories>
</ROOT>

Notice that we can include a reference to the XSL sheet to use to format the data returned. We also need to add a virtual directory to the template directory in the configuration tool before we can access this template. On our test system, the URL to access that template was http://localhost/nwind/queries/ProductsbyCategory.xml. This is a much cleaner and more secure syntax than putting the sql statement in the URL.

There are lots of cool things about using SQLXML, like using XPath queries on your relational data, updating the database with XML, xdr, client-side or server-side processing, and caching. For additional information, have a look at:

Internet Explorer Web Controls: Treeview

With ASP.NET there has been the addition of some interesting Server Controls to the Web developer's palette. We decided to look at the tree view control. Tree views are a common and simple way to display navigation information and most Web developers have been asked for one in a project at one time or another. In a regularly changing site, it helps to have a dynamically generated menu, so let's put our SQLXML example to good use! First of all, the Treeview control requires a specific XML layout, so we need to translate our product xml into the following:

<TREENODES>
    <TreeNode TEXT="Beverages" NavigateURL="Beverages.htm" TARGET="main">
    <TreeNode TEXT="Chai" NavigateURL="Chai.htm" TARGET="main" />
    <TreeNode TEXT="Chang" NavigateURL="Chang.htm" TARGET="main" />
</TREENODES>

Remember that XSL sheet we referenced in the SQLXML template above? Well, here is our tree.xsl for translating the XML we got from SQL server, above, to that needed by the treeview control:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version='1.0'>

  <xsl:template match="/">
    <TREENODES>
       <xsl:apply-templates />
    </TREENODES>
  </xsl:template>

  <xsl:template match="Categories">
     <xsl:element name="TreeNode">
        
        <xsl:attribute name="Text" >
           <xsl:value-of select="@CategoryName"/>
        </xsl:attribute>
        <xsl:attribute name="NavigateURL" >
           <xsl:value-of select="@CategoryName"/>.htm
        </xsl:attribute>
        <xsl:attribute name="TARGET" >ContentWindow</xsl:attribute>

        <xsl:apply-templates />
           
      </xsl:element>
  </xsl:template>

  <xsl:template match="Products">
      <xsl:element name="TreeNode">
         <xsl:attribute name="Text" >
            <xsl:value-of select="@ProductName"/></xsl:attribute>
         <xsl:attribute name="NavigateURL" >
             <xsl:value-of select="@ProductName"/>.htm</xsl:attribute>
         <xsl:attribute name="Target" >ContentWindow</xsl:attribute>
      </xsl:element>
  </xsl:template>

  <xsl:template match="ROOT">
       <xsl:apply-templates />
  </xsl:template>

</xsl:stylesheet>

Now, here comes the fun part. We just need to connect the pieces together to get our dynamic navigation tree. Save the following into navigate.aspx:

<%@ import namespace="Microsoft.Web.UI.WebControls" %>
<%@ Register TagPrefix="mytree" 
Namespace="Microsoft.Web.UI.WebControls" 
Assembly="Microsoft.Web.UI.WebControls, Version=1.0.2.226, 
Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>
<BODY>
<FORM RUNAT="server">
  <mytree:treeview id="tocTree" runat="server" target="ContentWindow"
   DefaultStyle="font-family:arial;font-size:8pt"
   HoverStyle="background-color:green;"
   SelectedStyle="background-color:orange;font-weight:bold"
   TreeNodeSrc="http://localhost/nwind/queries/ProductsbyCategory.xml">
 </mytree:treeview>
</FORM>
</BODY>

Notice the target and style attributes in the code above. Figure 1 is a picture of what we get in the browser when we navigate to our nifty tree.

Bb250485.webteam03032003-fig01(en-us,VS.85).gif

Figure 1. Dynamic navigation tree

It is also possible to specify your own images to display next to different levels of the tree. Check the Treeview reference on MSDN Library for all this and more.

Now, did you notice that we just created a spiffy dynamic menu with nothing more than a little SQL command and some XSL? Throw those into the cache and woosh—look at that performance! Sometimes we just love being Web developers!

Web Team in Short

RSSing to New Things

Q: Shun Endo writes, "I read Dare Obasanjo's MSDN article Building a Desktop News Aggregator about RSS and I was wondering how I could incorporate an RSS news feed into my Web page so I can give my users news content that might interest them."

A: That was an excellent introduction to RSS (Really Simple Syndication) with working code. The code that Dare presents can easily be moved to a .NET Web application. We were thinking that it would be nice to offload the request for the RSS feed to the browser to save our server the extra processing. To do this, we came up with a simple element behavior that uses the XMLHTTP component to issue the GET request through script on the client, parse the returned RSS XML, and display the news links in a table.

Our example requests a feed of news about the Seattle SuperSonics, but you can browse the sites mentioned in Dare's article (News Is Free or Syndic8) for more feeds.

Below is our sample HTML file:

<HTML xmlns:ms>
<HEAD>
<?IMPORT namespace="ms" implementation="rss.htc">
</HEAD>
<BODY>
   <ms:rss feedURL="http://p.moreover.com/cgi-local/page?feed=175&o=rss"/>
</BODY>
</HTML>

As you can see, our element <ms:rss> does all the work. We just include this in a Eeb page sent to a browser that supports element behaviors (Internet Explorer 5.5 or later), specifying the feedURL attribute and the list of news items is displayed.

Here is the source for rss.htc:

<PUBLIC:COMPONENT tagName="rss">
<PUBLIC:DEFAULTS 
   canHaveHTML=true
   viewLinkContent=true
/>   
<PUBLIC:ATTACH event="ondocumentready" onEvent="onDocumentReady()" />
</PUBLIC:COMPONENT>
<SCRIPT LANGUAGE="JScript">
var m_oHttp;

function onDocumentReady()
{
   m_oHttp = new ActiveXObject("Microsoft.xmlhttp");
   m_oHttp.open("GET", this.element.feedURL, true); 
   m_oHttp.onreadystatechange = onHttpReady;
   m_oHttp.send();
}

function onHttpReady()
{
   if ((m_oHttp.readyState != 4) || (m_oHttp.status != 200) || 
(m_oHttp.responseXML == null))
      return;

   var oNodes = m_oHttp.responseXML.selectNodes("rss/channel/item");

   if ((oNodes == null) || (oNodes.length == null))
      return;

   for (var i = 0; i < oNodes.length; i++)
   {
      var oTR = document.createElement("TR");
      var oTC = document.createElement("TD");
      var oA = document.createElement("A");

      oA.href = oNodes[i].selectSingleNode("link").text
      oA.innerText = oNodes[i].selectSingleNode("title").text;
         
      oTC.appendChild(oA);
      oTR.appendChild(oTC);
      tblRSS.children[0].appendChild(oTR);
   }
}
</SCRIPT>
<TABLE ID="tblRSS"></TABLE>

The script in our HTC is pretty simple. We take the URL specified in the feedURL attribute and issue an HTTP GET request using the XMLHTTP component on your system. We specify true as the third parameter of the open method to indicate that we want to issue an asynchronous request, and we set the onreadystatechange event handler to the function onHttpReady so that onHttpReady is called back. This function checks the readyState property and HTTP response code value before looping over the item nodes in the returned RSS XML and inserting the links into the table.

It should be noted, however, that there is a bifurcation in the RSS standard. Our example here should work with RSS 2.0 and 0.9x, but not 1.0. One notable difference is that the root element of an RSS 1.0 document is the <RDF> tag and our script expects a root node of <RSS>. For more detail about the differences between RSS 0.9x, 1.0 and 2.0, see Mark Pilgrim's article at http://www.xml.com/pub/a/2002/12/18/dive-into-xml.html.

Building a Combo-Box

Q: Katie Sakuma asks, "Is there any way to mimic the behavior of a combo box in DHTML?"

A: One of the great benefits of writing for the Web Team Talking series is that we get to hear from real world developers. We get to read your wish lists and find out what is affecting the builders of the World Wide Web. Requests for HTML combo-boxes are one feature that we see all the time. So much so, that we sometimes wonder why there has not been an intrinsic HTML combo-box element added to the W3C's HTML specification. However, with the Internet Explorer element behavior technology, one can easily construct one.

We've built a combo-box element behavior as an example below, using a textbox, button and popup object. However, a quick search on Google turns up a couple controls that were impressive.

We should note that HTML elements inside element behaviors do not participate in a POST, so if you wish to POST the contents of this combo-box you need to write script to put the contents of the value property into a hidden control or another form element. ThisMicrosoft Knowledge Base Article details a workaround: http://support.microsoft.com/default.aspx?scid=kb;en-us;256228.

Test.htm

<HTML xmlns:ms>
<BODY STYLE="font-family:Tahoma;font-size:.75em;">
<?IMPORT NAMESPACE="ms" implementation="combobox.htc">
<ms:combobox ID="combobox1" WIDTH="200" ITEMS="oranges;apples;pears;cranberries" />
<BR>
<BR>
<BUTTON ONCLICK="alert(combobox1.value);">Get ComboBox Value</BUTTON>
</BODY>
</HTML>

Combobox.htc

<PUBLIC:COMPONENT tagName="combobox">
<PUBLIC:DEFAULTS 
   canHaveHTML=true
   contentEditable=false
   tabStop=true
   viewLinkContent=true
   viewMasterTab=false
/>   
<PUBLIC:PROPERTY ID="propItems" NAME="items" PUT="putItems"/>
<PUBLIC:PROPERTY ID="propValue" NAME="value" GET="getValue"/>
<PUBLIC:ATTACH event="ondocumentready" handler="onDocumentReady"/>
</PUBLIC:COMPONENT>
<STYLE>
.btn
{
   font-family: Arial,MS Sans Serif;
   color: gray;
   font-weight: bold;
   vertical-align:top;
}
</STYLE>
<SCRIPT LANGUAGE="JScript">
var g_nHeight = 0;
var g_sMenu = "<TABLE ID='tblMenu' STYLE='border:1px solid black;
background-color:window;font-size:100%;width:100%;
border-collapse:collapse;cursor:default;'></TABLE>";
var g_oMenu = null;
var g_rgItems = null;
function onDocumentReady()
{
   // Size button
   g_nHeight = txt1.offsetHeight;
   btn1.style.width = g_nHeight;
   btn1.style.height = g_nHeight;

   // Size element
   if (element.width) 
   {
      spn1.style.width = element.width;
      txt1.style.width = spn1.offsetWidth - btn1.offsetWidth;
   }

   // Create popup
   g_oMenu = window.createPopup();
   g_oMenu.document.body.innerHTML = g_sMenu;
   g_oMenu.document.body.style.fontFamily = document.body.currentStyle.fontFamily;
   g_oMenu.document.body.style.fontSize = document.body.currentStyle.fontSize;
   txt1.style.fontFamily = document.body.currentStyle.fontFamily;
   txt1.style.fontSize = "100%";

   addItems()
}
function onClickButton()
{
   g_oMenu.show(0, 0, 0 ,0);
   var nHeight = g_oMenu.document.body.scrollHeight;
   g_oMenu.show(0, g_nHeight - 1, spn1.offsetWidth - 1, nHeight, element);
}
function addItems()
{
   if (!g_oMenu) return;

   // Add Items to the popup menu
   g_oMenu.document.body.innerHTML = g_sMenu;
   for (var i = 0; i < g_rgItems.length; i++)
   {
      if (g_rgItems[i] == "") break;
      var oTR = g_oMenu.document.getElementById("tblMenu").insertRow();
      var oTD = oTR.insertCell();
      oTD.attachEvent("onclick", new Function("chooseItem(\"" + i + "\")") );
      oTD.attachEvent("onmouseover", new Function("selectItem(" + i + ")") );
      oTD.attachEvent("onmouseout", new Function("unselectItem(" + i + ")") );
      oTD.innerText = g_rgItems[i];
   }
}
function selectItem(nIndex)
{
   var oMnuTbl = g_oMenu.document.getElementById("tblMenu");
   oMnuTbl.rows[nIndex].style.backgroundColor = "highlight";
   oMnuTbl.rows[nIndex].style.color = "highlighttext";
}
function unselectItem(nIndex)
{
   var oMnuTbl = g_oMenu.document.getElementById("tblMenu");
   oMnuTbl.rows[nIndex].style.backgroundColor = "";
   oMnuTbl.rows[nIndex].style.color = "";
}
function chooseItem(nIndex)
{
   var oMnuTbl = g_oMenu.document.getElementById("tblMenu");
   oMnuTbl.rows[nIndex].style.backgroundColor = "";
   oMnuTbl.rows[nIndex].style.color = "";
   txt1.value = oMnuTbl.rows[nIndex].cells[0].innerText;
   g_oMenu.hide();
}
function putItems(sVal)
{
   g_rgItems = new Array();
   g_rgItems = sVal.split(";");
   addItems();
}
function getValue()
{
   return txt1.value;
}
</SCRIPT>
<NOBR ID="spn1"><INPUT TYPE="text" ID="txt1" tabIndex=0><BUTTON ID="btn1" 
ONCLICK="onClickButton()" CLASS="btn" tabIndex=0 HIDEFOCUS=true>v</BUTTON></NOBR>

Font Faces in a Crowd

Q: Jim Spaloss inquires, "I am in the process of building a simple HTML-based rich edit control using a designMode document. In order to keep the toolbar in sync with the selected text in the control, I would like to find a way to retrieve the font name of the currently selected element. I have tried using queryCommandState("FontName"), but I can't seem to figure out how to retrieve the font name when all I get back is a Boolean."

A: You're on the right track, Jim, with the FontName command ID. But instead of using the queryCommandState method, use document.queryCommandValue("FontName"). This method returns to the font of the currently selected text in your editor.

Minor Versions

Q: Zsolt Toroczkay says, "How can I determine if Internet Explorer 6.0 SP1 is installed from script?"

A: You can use navigator.appMinorVersion. This returns a string that looks something like ";SP1;Q328970;Q324929;". You can check for "SP1" in there. Of course, you need to check the appVersion property to make sure you have "MSIE 6.0" and not an SP1 of another browser.

What You See Is What You Get

Q: Another inquiring mind states, "I am developing a page that makes heavy use of DHTML. My scripts change the DOM extensively such that what is rendered in the browser is very different from the HTML source. I would like to find a way to view the rendered contents of the browser as HTML in order to help me debug. Can you help me?

A: Nice question. This is a common enough problem that we've seen several internal tools built to show us the HTML as it is displayed in the browser at runtime. However, the best solution we've come across to date is also the simplest, and we're glad to pass on the word.

Type this into the Internet Explorer address bar:

javascript:'<XMP>'+document.documentElement.outerHTML+'</XMP>';

As you see, this command takes the outerHTML property of the documentElement object and displays it inside an <XMP> node. The XMP element displays sub nodes as text, not as rendered HTML. This displays the current HTML corresponding to the document presently loaded inside Internet Explorer.

Cartesian Problem

Q: Jeremy Gray writes "I have a function that adds several effects to objects in a Web page based on their DIV ID. My only problem is that the function needs x,y coordinates to place the effect. So, is there any way to find the coordinates of an object (like a table or an image) when my page loads?"

A: The method getBoundingClientRect should be what you are looking for. Calling this method on an element will return a TextRectangle object. The TextRectangle has four properties (top, bottom, left, right), which return to you the coordinates of the object.

<HTML>
<HEAD>
<SCRIPT>
function window.onload()
{
   var rect = div1.getBoundingClientRect();
   alert("top: " + rect.top + "\nbottom: " + rect.bottom + "\nleft: " + 
rect.left + "\nright: " + rect.right);
}
</SCRIPT>
</HEAD>
<BODY>
<DIV ID="div1" STYLE="background-color:red;width:100px;height:100px;"></DIV>
</BODY>
</HTML>

Mail Headers in System.Web.Mail

Q: Do you know if there is a way to capture the Unique MessageID generated by the mail server using the .NET Framework implementation? With CDO, I can access it after the send via oMsg.Fields("urn:schemas:mailheader:message-id").

A: With the .NET Framework version 1.1 available here, you will find a Fields collection on the MailMessage class that you can use to obtain this and other e-mail header fields.

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.

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.


Tosh Meston is a Web developer on the Outlook Web Access team. He comes to Microsoft with a background in physics and spends his free time reading and perfecting his three-point shot on the basketball court.

The Web Team's Greatest Hits

List of Web Team Topics


  
Show:
© 2015 Microsoft