Archives

Archives / 2005 / September
  • On Vacation the Next Two Weeks

    I just wanted to post a quick note to say that I'll be on vacation (without any access to email) the next two weeks visiting scenic places in Italy.  If you post an email or comment to me through my blog I will get back to you when I return (just wanted to let you know I wasn't ignoring you <g>).

  • Microsoft Expression “Quartz Web Designer”

    This morning at the PDC Microsoft announced the new Expression Designer line of products.  Included as part of the Expression suite is the new product currently codenamed Microsoft Expression “Quartz Web Designer”.

     

    It provides a really rich designer focused web authoring tool that has full support for ASP.NET 2.0 – including rich support for ASP.NET Controls, Data-binding, Templates, Master Pages, Themes, Localization, etc.  It will be a great tool for designers to use when working on ASP.NET 2.0 solutions (developers use VS, designers use Expression – with the ability for both tools to edit the same web projects together). 

     

    You can learn more about it here: http://www.microsoft.com/products/expression/en/web_designer/default.aspx. If you are at the PDC there is a session this afternoon that will include a 50 minute demo of it that will really show off its capabilities. 

     

    In the Visual Studio Orcas timeframe we will also be upgrading the VS Whidbey WYSIWYG web designer to use the Quartz design surface so that we can take advantage of its richer CSS and layout editing support.  If you attend the PDC session this afternoon you’ll also see a demo of a VS Orcas build showing this richer support off. 

     

    Fun times ahead,

     

    Scott

  • Atlas Keynote Walkthrough

    I mentioned a little earlier that I’d post the Atlas sample code and overall talk flow from the keynote demo I was part of earlier today at the PDC.  Here it is below:

     

    Step 1: The Web Service We Use

     

    In the talk Anders used the new LINQ features in C# 3.0 to query for process information, then join it in memory with data from a database, and then transform it into XML.  Don then showed exposing it via Indigo as first a SOAP end-point, then an RSS end-point, and then finally as a JSON end-point that I could consume.

     

    Unfortunately I don’t have a snap-shot of the above code still with me (it is on another machine right now), so can’t show what we really used during the keynote.  Here is a simplified .asmx version, though, that shows what the signature of the service looks like The sample below is calling System.Diagnostics directly instead of using the cool new LINQ features, but hopefully provides a little insight into the service signature.  Note that there is no Atlas specific decoration or meta-data that needs to be applied to the web-service – it is just a standard .asmx file (Atlas then installs an HttpModule that can handle transforming the input/output of the service to a JSON wire protocol as appropriate): 

     

    LapService.asmx

     

    <%@ WebService Language="C#" Class="LapService" %>

     

    using System;

    using System.Collections;

    using System.Collections.Generic;

    using System.ComponentModel;

    using System.IO;

    using System.Web;

    using System.Web.Services;

    using System.Web.Services.Protocols;

     

    public class LapService : System.Web.Services.WebService

    {

        [WebMethod]

        public ProcessData[] GetProcesses() {

            return this.GetAllProcesses().ToArray();

        }

     

        [WebMethod]

        public ProcessData[] MatchProcesses(string expression)

        {

            List<ProcessData> data = this.GetAllProcesses();

            if (String.IsNullOrEmpty(expression))

            {

                return data.ToArray();

            }

     

            string[] words = expression.Split(' ');

            return data.FindAll(new Predicate<ProcessData>(delegate(ProcessData process)

            {

                return System.Text.RegularExpressions.Regex.IsMatch(process.Name, expression);

            })).ToArray();

        }

     

        private List<ProcessData> GetAllProcesses()

        {

            List<ProcessData> data = new List<ProcessData>();

     

            System.Diagnostics.Process[] processes = System.Diagnostics.Process.GetProcesses();

            foreach (System.Diagnostics.Process process in processes)

            {

                data.Add(new ProcessData(process.ProcessName, process.WorkingSet64, process.Threads.Count));

            }

     

            return data;

        }

    }

     

    ProcessData.cs

     

    using System;

    using System.ComponentModel;

     

    public class ProcessData

    {

        private string _name;

        private long _workingSet;

        private int _threadCount;

     

        public string Name {

            get { return _name; }

            set { _name = value; }

        }

     

        public long WorkingSet {

            get { return _workingSet; }

            set { _workingSet = value; }

        }

     

        public long WorkingSetInMB {

            get { return _workingSet / (1024 * 1000); }

            private set { }

        }

     

        public int ThreadCount {

            get { return _threadCount; }

            set { _threadCount = value; }

        }

     

        public ProcessData() { }

     

        public ProcessData(string name, long workingSet, int threadCount) {

            _name = name;

            _workingSet = workingSet;

            _threadCount = threadCount;

        }

    }

     

    Step2 : Simple Invocation of the Web Service

     

    The first step with Atlas we did on stage was to take a simple html page with no server-side code and show binding to the remote LapService end-point with it.  The final code for this segment looked like the page below. 

     

    Note how it is easy to add a JSON proxy to the page – just add two script references to the page (one that points to the AtlasCore.js library and one that points to the .asmx with the /js flag specified).  You can then just call methods on the remote service – and setup a call-back event handler that will be invoked when the response is returned.  You can then work with the data using the same object model (except in Jscript) that was used on the server.  All code in the below sample runs on the client. 

     

    Default.aspx:

     

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

     

    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

    <head>

      <title>Process Viewer</title>

      <link type="text/css" rel="stylesheet" href="simple.css" />

     

      <script src="ScriptLibrary/AtlasCore.js" type="text/javascript"></script>

      <script src="LapService.asmx/js" type="text/javascript"></script>

     

      <script language="javascript" type="text/javascript">

     

            function Button1_onclick() {

                var text1 = document.getElementById("Text1");

                LapService.MatchProcesses(text1.value, onSearchComplete);

            }

           

            function onSearchComplete(results) {

                var searchResults = document.getElementById("searchResults");

               

                for (var i=0; i<results.get_length(); i++) {

                  searchResults.innerHTML += results[i].Name + "<br>";

                }

            }

      </script>

     

    </head>

    <body>

      <form id="form1" runat="server">

     

        <div id="logo">

          Process Explorer

        </div>

       

        <div id="header">

          Search:

          <input id="Text1" type="text" />

          <input id="Button1" type="button" value="Search" onclick="Button1_onclick();" />

        </div>

       

        <div id="content">

       

          <div class="left">

            <span id="searchResults"></span>

          </div>

         

        </div>

      </form>

    </body>

    </html>

     

     

    Step 3: Using an Atlas ListView Control

     

    [In the keynote demo we actually merged this step and the next one together – but for this post I’ll actually show what we originally planned (before we ran into a time crunch and had to compress them together).]

     

    In this step we’ll take our simple invocation further and use one of the new Atlas controls called ListView to customize the presentation of the data further (and avoid having to hard-code it into javascript like the sample above). 

     

    Atlas provides a Javascript client library that provides a Listview control that I can declare and use with javascript on the client.  Alternatively, I can use the ASP.NET server control model to nicely encapsulate this and handle automatically sending out the appropriate client markup (which is what I’ll do below).

     

    Note that all Atlas-enabled ASP.NET Server Controls support both a client-side and server-side object model – so I can write code against them both from client Javascript and my server code-behind file.  In the below example I’m taking the same Results data that we retrieved above and instead of doing a for-loop over it I’m binding it on the client to the ListView (by calling searchResults.control.set_data(results).  Note also that all style design is done using standard CSS.

     

    Default.aspx:

     

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

     

    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

    <head>

      <title>Process Viewer</title>

      <link type="text/css" rel="stylesheet" href="simple.css" />

     

      <atlas:ScriptManager runat="server" />

      <script src="LapService.asmx/js" type="text/javascript"></script>

     

      <script language="javascript" type="text/javascript">

     

            function Button1_onclick() {

                var text1 = document.getElementById("Text1");

                LapService.MatchProcesses(text1.value, onSearchComplete);

            }

           

            function onSearchComplete(results) {

                var searchResults = document.getElementById("searchResults");

                searchResults.control.set_data(results);

            }

      </script>

     

    </head>

    <body>

      <form id="form1" runat="server">

     

        <div id="logo">

          Process Explorer

        </div>

       

        <div id="header">

          Search:

          <input id="Text1" type="text" />

          <input id="Button1" type="button" value="Search" onclick="Button1_onclick();" />

        </div>

       

        <div id="content">

          <div class="left">

         

            <atlas:ListView id="searchResults" runat="server" ItemTemplateControlID="row" CssClass="listView"

              ItemCssClass="item" AlternatingItemCssClass="alternatingItem">

              <LayoutTemplate>

                <ul runat="server">

                  <li id="row" runat="server">

                    <atlas:Label id="name" runat="server">

                      <Bindings>

                        <atlas:Binding DataPath="Name" Property="text" />

                      </Bindings>

                    </atlas:Label>

                    <atlas:Label runat="server" CssClass="bar">

                      <Bindings>

                        <atlas:Binding DataPath="WorkingSetInMB" Property="style" PropertyKey="width" />

                      </Bindings>

                    </atlas:Label>

                    <atlas:Label runat="server">

                      <Bindings>

                        <atlas:Binding DataPath="WorkingSetInMB" Property="text" />

                      </Bindings>

                    </atlas:Label>MB

                  </li>

                </ul>

              </LayoutTemplate>

            </atlas:ListView>

           

          </div>

           

        </div>

      </form>

    </body>

    </html>

     

     

    Step 4: Implementing Drag/Drop

     

    The next step is to implement a master/detail drill-down view, where we let visitors to the site drag/drop processes from our Listview into an ItemView control to drill into its details (entirely on the client without requiring any post-backs or page refreshes).  Doing this is pretty easy with Atlas – drag/drop sourcing and targeting can be done declaratively or programmatically (note the <behaviors> tag to see how this is done below).  Note that I also switching the CSS stylesheet to "color.css" (from simple.css") just to make it look a little better.

     

    Default.aspx:

     

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

     

    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

    <head>

      <title>Process Viewer</title>

      <link type="text/css" rel="stylesheet" href="color.css" />

     

      <atlas:ScriptManager runat="server" />

      <script src="LapService.asmx/js" type="text/javascript"></script>

     

      <script language="javascript" type="text/javascript">

     

            function Button1_onclick() {

                var text1 = document.getElementById("Text1");

                LapService.MatchProcesses(text1.value, onSearchComplete);

            }

           

            function onSearchComplete(results) {

                var searchResults = document.getElementById("searchResults");

                searchResults.control.set_data(results);

            }

      </script>

     

    </head>

    <body>

      <form id="form1" runat="server">

     

        <div id="logo">

          Process Explorer

        </div>

       

        <div id="header">

          Search:

          <input id="Text1" type="text" />

          <input id="Button1" type="button" value="Search" onclick="Button1_onclick();" />

        </div>

       

        <div id="content">

             

          <!-- Listview -->

          <div class="left">

     

            <atlas:ListView id="searchResults" runat="server" ItemTemplateControlID="row" CssClass="listView"

              ItemCssClass="item" AlternatingItemCssClass="alternatingItem">

              <Behaviors>

                <atlas:DragDropList runat="server" DataType="Process" />

              </Behaviors>

              <LayoutTemplate>

                <ul id="ul1" runat="server">

                  <li id="row" runat="server">

                    <atlas:Label id="name" runat="server">

                      <Behaviors>

                        <atlas:DraggableListItem runat="server" Handle="name">

                          <Bindings>

                            <atlas:Binding Property="data" />

                          </Bindings>

                        </atlas:DraggableListItem>

                      </Behaviors>

                      <Bindings>

                        <atlas:Binding DataPath="Name" Property="text" />

                      </Bindings>

                    </atlas:Label>

                    <atlas:Label id="Label1" runat="server" CssClass="bar">

                      <Bindings>

                        <atlas:Binding DataPath="WorkingSetInMB" Property="style" PropertyKey="width" />

                      </Bindings>

                    </atlas:Label>

                    <atlas:Label id="Label2" runat="server">

                      <Bindings>

                        <atlas:Binding DataPath="WorkingSetInMB" Property="text" />

                      </Bindings>

                    </atlas:Label>MB

                  </li>

                </ul>

              </LayoutTemplate>

            </atlas:ListView>

          </div>

                 

          <!-- Item Details -->

          <div class="right">

         

            <atlas:ItemView id="details" runat="server">

              <Behaviors>

                <atlas:DataSourceDropTarget runat="server" Append="false" AcceptedDataTypes="Process" />

              </Behaviors>

              <ItemTemplate>

                <atlas:Label runat="server" CssClass="name">

                  <Bindings>

                    <atlas:Binding DataPath="Name" Property="text" />

                  </Bindings>

                </atlas:Label>

                <span class="detailRow">

                  Working set:

                  <atlas:Label runat="server">

                    <Bindings>

                      <atlas:Binding DataPath="WorkingSetInMB" Property="text" />

                    </Bindings>

                  </atlas:Label>

                </span>

                <span class="detailRow">

                  Thread count:

                  <atlas:Label runat="server">

                    <Bindings>

                      <atlas:Binding DataPath="ThreadCount" Property="text" />

                    </Bindings>

                  </atlas:Label>

                </span>

              </ItemTemplate>

            </atlas:ItemView>

           

          </div>

        </div>

           

      </form>

    </body>

    </html>

     

     

    Step 5: Implementing VirtualEarth

     

    This step was completely gratuitous but fun.  Since any “real” Ajax app has some-type of map integration <g>, we decided to add support for mapping the location of our server processes on a map.

     

    Since there is no way to get real longitude or latitude information from a running process, we just hard-coded in the coordinates.  But the name of the process was data-bound and live (and if we did have longitude and latitude information we could obviously have data-bound that too).

     

    Here is what the code looked like with the virtual earth inside the bottom of the ItemView (note that no other code changes were required).  I’m using an older push-pin image so the background image is not transparent (it was for the demo) – but it should give you an idea of what it looked like:

     

    Default.aspx:

     

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

     

    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

    <head>

      <title>Process Viewer</title>

      <link type="text/css" rel="stylesheet" href="color.css" />

     

      <atlas:ScriptManager runat="server" />

      <script src="LapService.asmx/js" type="text/javascript"></script>

     

      <script language="javascript" type="text/javascript">

     

            function Button1_onclick() {

                var text1 = document.getElementById("Text1");

                LapService.MatchProcesses(text1.value, onSearchComplete);

            }

           

            function onSearchComplete(results) {

                var searchResults = document.getElementById("searchResults");

                searchResults.control.set_data(results);

            }

      </script>

     

    </head>

    <body>

      <form id="form1" runat="server">

     

        <div id="logo">

          Process Explorer

        </div>

       

        <div id="header">

          Search:

          <input id="Text1" type="text" />

          <input id="Button1" type="button" value="Search" onclick="Button1_onclick();" />

        </div>

       

        <div id="content">

             

          <!-- Listview -->

          <div class="left">

     

            <atlas:ListView id="searchResults" runat="server" ItemTemplateControlID="row" CssClass="listView"

              ItemCssClass="item" AlternatingItemCssClass="alternatingItem">

              <Behaviors>

                <atlas:DragDropList runat="server" DataType="Process" />

              </Behaviors>

              <LayoutTemplate>

                <ul id="ul1" runat="server">

                  <li id="row" runat="server">

                    <atlas:Label id="name" runat="server">

                      <Behaviors>

                        <atlas:DraggableListItem runat="server" Handle="name">

                          <Bindings>

                            <atlas:Binding Property="data" />

                          </Bindings>

                        </atlas:DraggableListItem>

                      </Behaviors>

                      <Bindings>

                        <atlas:Binding DataPath="Name" Property="text" />

                      </Bindings>

                    </atlas:Label>

                    <atlas:Label id="Label1" runat="server" CssClass="bar">

                      <Bindings>

                        <atlas:Binding DataPath="WorkingSetInMB" Property="style" PropertyKey="width" />

                      </Bindings>

                    </atlas:Label>

                    <atlas:Label id="Label2" runat="server">

                      <Bindings>

                        <atlas:Binding DataPath="WorkingSetInMB" Property="text" />

                      </Bindings>

                    </atlas:Label>MB

                  </li>

                </ul>

              </LayoutTemplate>

            </atlas:ListView>

          </div>

           

          </div>

         

          <!-- Item Details -->

          <div class="right">

         

            <atlas:ItemView id="details" runat="server">

              <Behaviors>

                <atlas:DataSourceDropTarget runat="server" Append="false" AcceptedDataTypes="Process" />

              </Behaviors>

              <ItemTemplate>

                <atlas:Label runat="server" CssClass="name">

                  <Bindings>

                    <atlas:Binding DataPath="Name" Property="text" />

                  </Bindings>

                </atlas:Label>

                <span class="detailRow">

                  Working set:

                  <atlas:Label runat="server">

                    <Bindings>

                      <atlas:Binding DataPath="WorkingSetInMB" Property="text" />

                    </Bindings>

                  </atlas:Label>

                </span>

                <span class="detailRow">

                  Thread count:

                  <atlas:Label runat="server">

                    <Bindings>

                      <atlas:Binding DataPath="ThreadCount" Property="text" />

                    </Bindings>

                  </atlas:Label>

                </span>

               

                <!-- Virtual Earth Control -->

                <atlas:VirtualEarthMap id="map" runat="server" ZoomLevel="17" Latitude="34.042653"

                  Longitude="-118.269779" PushpinImageUrl="~/TravelImages/pushpin.gif"

                  PushpinCssClass="pushpin" PushpinImageWidth="12" PushpinImageHeight="20" PushpinActivation="Click"

                  PopupPositioningMode="TopLeft" MapStyle="Hybrid">

                  <PopupTemplate>

                    <atlas:Label runat="server">

                      <Bindings>

                        <atlas:Binding DataContext="details" DataPath="dataContext.Name" Property="text" />

                      </Bindings>

                    </atlas:Label>

                  </PopupTemplate>

                  <Pushpins>

                    <atlas:Pushpin Value="pp" Latitude="34.042653" Longitude="-118.269779" />

                  </Pushpins>

                </atlas:VirtualEarthMap>

              </ItemTemplate>

            </atlas:ItemView>

           

          </div>

        </div>

           

      </form>

    </body>

    </html>

     

     

    Step 6: Showing it on a Mac

     

    The final step we showed was switching to a Mac running Safari and hitting the same page above using it.  Safari browsers get the exact same user experience with full Ajax support.

     

    Hopefully this provides a rough sense of what the demo was like.

     

    - Scott

  • Atlas Unleashed...

    I just finished up giving my keynote demo with Anders, Don and Chris about an hour ago.  Coding on the fly in front of 8,000 people for 45 minutes on a single machine with no cutaways or backups is fairly adrenaline rushing. :-)  I think it went pretty well and it was a lot of fun to-do.  A little later today I’m going to try and blog up the code we wrote for Atlas, as well as the screenshots so people can see what we did (update: here it is: http://weblogs.asp.net/scottgu/archive/2005/09/14/425131.aspx)

     

    As a lot of people have noticed, the Atlas project went live on the web earlier this morning at: http://atlas.asp.net.  You can download it and start playing with it on top of ASP.NET 2.0 and VS 2005 Beta2.  You can find out a whole bunch more about Atlas on the website – I just wanted to call out a few extra project principles we had in mind when we started working on it:

     

    -- Atlas doesn’t change or modify any core ASP.NET, .NET Framework or other binaries on your system.  One of our goals has been to make it really easy to try out and take advantage of, and not burden people with the need to upgrade their core ASP.NET + VS bits to a yet a new beta immediately after Whidbey ships.  As we get further along we’ll integrate things even more into ASP.NET + VS – but for right now we are trying to keep things fast and flexible for people.

     

    -- You don’t need hosters or administrators to install Atlas on a server for you to use it.  You can instead just copy the Microsoft.Web.Atlas.dll binary into your projects’ \bin directory and copy the Atlas \ScriptLibrary directory of .js files into your project and you are good to go.  We also have a project template you can take advantage of within VS that you can use to just pick an ASP.NET Atlas Web Project when you are creating a new web-site – and it will copy all of this into your project for you.  Just xcopy this up onto a remote server running ASP.NET 2.0 and you are good to go.

     

    -- Atlas is designed to be cross-browser. This first technology preview adds Ajax support to IE, FireFox and Safari browser clients.  Our plan is to test and further expand browser support even more with subsequent builds.  Note that all styling for Atlas based controls are done purely through CSS.

     

    -- Atlas will be built using a transparent development model.  Our plan with Atlas is to eschew the traditional two-betas and then RTM model that we often use with big projects at Microsoft, and instead move to a model where we ship very often (at least once every month and often more frequently then that), get feedback as early as possible, share our ideas out in the open (even when they are not 100% baked), and above all use the broader developer community to make sure we build the right technology.  All of the client-library code is shipped as standard .js files that you can read and look over to see what we are doing.  Note that we have deliberately not removed white-space, obfuscated, or compressed them down – our goal right now is to preserve developer readability (note: when compressed the entire Atlas client library is around a 32k download and cacheable on the client -- although typical scenarios will usually use only a subset of the .js files, so it will be even smaller than that).  Please give us your feedback on the forums, tell us what you like and don’t like, and how we can build the right product you want to use. 

     

    -- Atlas is still very early.  It is nowhere near beta quality yet – think of it more as an early Alpha.  Some features in this tech-preview literally were implemented only a few days ago.  So you will see bugs, rough spot areas, design questions we are still debating about, things that don’t work like they should, etc.  This is one of the aspects of shipping frequently and trying to get feedback super early in the process.  As Whidbey finishes up, our QA team will start working on Atlas in earnest and you'll start to see quality steadily go up.

     

    I hope you'll have a lot of fun using it – we are having a lot of fun building it. :-)

      

    Thanks,

     

    Scott

     

    P.S. Here is a simple Atlas demo I put together for fun that uses one of the new Atlas server controls (note: the Atlas control has a client-side object model I can also program and databind against it using client-side javascript)….

     

    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="test.aspx.cs" Inherits="test" %>

     

    <!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>Untitled Page</title>
       
        <atlas:ScriptManager runat="server" />
        <link type="text/css" rel="stylesheet" href="scott.css" />
       
    </head>
    <body>
        <form id="form1" runat="server">
        <div>
       
            <atlas:VirtualEarthMap ID="map" runat="server" ZoomLevel="17" Latitude="34.042653"
              Longitude="-118.269779" PushpinImageUrl="Me.gif" PushpinCssClass="pushpin" PushpinImageWidth="12"
              PushpinImageHeight="20" PushpinActivation="Click" PopupPositioningMode="TopLeft" MapStyle="Hybrid">
                 <Pushpins>
                      <atlas:Pushpin Value="Me" Latitude="34.042653" Longitude="-118.269779" />
                  </Pushpins>
                  <PopupTemplate>
                      <h3>ScottGu here...</h3>
                  </PopupTemplate>     
            </atlas:VirtualEarthMap>
      
        </div>
        </form>
    </body>
    </html>

     

    Here is the output when the page is first loaded:

     

    and then while clicked on the client....


    Gratuitous and silly.... but also fun. ;-)

     

  • Defining Custom Item Templates in Web Projects with VS 2005

    One of the cool new features in VS 2005 that I was playing with a little today is the ability to export and then re-use project item templates.  This enables developers to easily define common templates that they can re-use over and over when adding new pages/classes/controls into web projects or class libraries, and avoid repetitively typing standard content or code.

     

    Below is a simple tutorial on how you can use this with web projects to automate creating a standard page template that inherits from a common page base class (in this example: “ScottGu.Test.MyBasePage”), is defined within a common code namespace (in this example: "ScottGu.Test"), and which has a common CSS stylesheet and Javascript script reference pre-set (note: you should probably use the new ASP.NET 2.0 Masterpage feature for the common CSS and Javascript file – but I thought I’d show it here nonetheless).

     

    Note: I’m pretty sure this didn’t work with Beta2 builds of VS 2005 – but it is supported with the final release and probably the more recent August CTP builds.

     

    Step 1: Define the page you want to become a template

     

    The below two files are a sample page I added into a web project.  Note that the page derives from a base-page called “MyBasePage”, lives in the “ScottGu.Test” namespace, has two custom namespaces defined with using blocks in the code-behind, and has a stylesheet and client-side javascript file already added to it.

     

    The only change I’ve made from the standard page markup is that I’ve replaced the class name of the code-behind file to have the marker name of “$safeitemname$”, and updated the Inherits attribute of the .aspx file to also point to the “$safeitemname$” class.  VS 2005 will then use this marker to set an appropriate classname based on the filename selected in the Add New Item dialog below (if you don't set a marker, then VS 2005 will default to just using the same classname as what was defined in the template).

     

    Default.aspx

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

     

    <%@ Page Language="C#" AutoEventWireup="true" CodeFileBaseClass="ScottGu.Test.MyBasePage" CodeFile="Default.aspx.cs" Inherits="ScottGu.Test.$safeitemname$ " %>

     

    <!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>My Sample Page</title>

        <link href="Common.css" rel="stylesheet" type="text/css" />

        <script src="ScriptLibrary/AtlasCore.js" type="text/javascript"></script>

    </head>

    <body>

        <form id="form1" runat="server">

        <div>

            <h1>My Template Page</h1>

        </div>

        </form>

    </body>

    </html>

     

    Default.cs.aspx

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

     

    using System;

    using System.Data;

    using System.Configuration;

    using System.Collections;

    using System.Web;

    using System.Web.Security;

    using System.Web.UI;

    using System.Web.UI.WebControls;

    using System.Web.UI.WebControls.WebParts;

    using System.Web.UI.HtmlControls;

     

    using MyCustomNamespace;

    using MyCustomNamespace2;

     

    namespace ScottGu.Test

    {

        public partial class $safeitemname$: MyBasePage

        {

            protected void Page_Load(object sender, EventArgs e)

            {

     

            }

        }

    }

     

    Step 2: Export the Page as a Template

     

    To export and re-use the above page, go to the File menu in VS and select the “Export Template” menu item.  This will then walk you through the below wizard, where you can choose which project and then which file in the project you want to use as a template:

     

     

     

    Once you select an item, you can then provide a name for what this template will show up as in the “Add New Item” dialog, an optional icon for the dialog, as well as a help text string.  In this example I am going to create a new template I’ll call “MyCustomTemplatePage”.

     

     

    VS will then generate a .zip file containing the item you selected and optionally add it automatically to your current Add New Item dialog (note: this dialog is populated from the C:\Documents and Settings\UserName\My Documents\Visual Studio 2005\Templates\ItemTemplates directory):

     

     

    Step 3: Create a new Page using the Template

     

    You can now create new items in your project based on the template you defined (or which you picked up from someone else or downloaded online). 

     

    To-do this, just choose the usual New File or “Add New Item” menu item in your project to pull up the “Add New Item” dialog.  This dialog will include all the standard VS 2005 file items provided by the particular project type you are working on, as well as expose a new “My Templates” section beneath the standard items.  Included in the “My Templates” section are all the item templates you've either created yourself (see step 2 above) or imported from others.

     

    For example, I can now select the “MyCustomTemplatePage” item, and name the page I want to create (based on the template) “UsingTheTemplate.aspx”. 

     

     

    When I select OK, VS 2005 will then add two new files into my project – UsingTheTemplate.aspx and UsingTheTemplate.aspx.cs.  These files are based on the template we defined earlier and look like this:

     

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

    UsingTheTemplate.aspx

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

     

    <%@ Page Language="C#" AutoEventWireup="true" CodeFileBaseClass="ScottGu.Test.MyBasePage" CodeFile="UsingTheTemplate.aspx.cs" Inherits="ScottGu.Test.UsingTheTemplate " %>

     

    <!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>My Sample Page</title>

        <link href="Common.css" rel="stylesheet" type="text/css" />

        <script src="ScriptLibrary/AtlasCore.js" type="text/javascript"></script>

    </head>

    <body>

        <form id="form1" runat="server">

        <div>

            <h1>My Template Page</h1>

        </div>

        </form>

    </body>

    </html>

     

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

    UsingTheTemplate.aspx.cs

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

     

    using System;

    using System.Data;

    using System.Configuration;

    using System.Collections;

    using System.Web;

    using System.Web.Security;

    using System.Web.UI;

    using System.Web.UI.WebControls;

    using System.Web.UI.WebControls.WebParts;

    using System.Web.UI.HtmlControls;

     

    using MyCustomNamespace;

    using MyCustomNamespace2;

     

    namespace ScottGu.Test

    {

        public partial class UsingTheTemplate : MyBasePage

        {

            protected void Page_Load(object sender, EventArgs e)

            {

     

            }

        }

    }

     

    Note that they look just like my original template – the only difference is that the classname has been automatically set by VS 2005 to match the filename I entered in the Add New Item dialog, and the CodeFile attribute in the .aspx has automatically been updated to point to the new code-behind filename.

     

    I can now quickly work on the page without the hassle of setting my common settings up.

     

    Hope this proves useful and saves you some time,

     

    Scott

  • PDC Soon

    I've seen a number of people post their "I'm off to the PDC" posts today and yesterday, so I thought I'd do my obligatory one too. :-)

  • Building Custom Build Providers with ASP.NET 2.0 and VS 2005

    One of the cool new features in ASP.NET 2.0 is support for what we call "build providers".  These are providers that can plug into the ASP.NET compilation system and provide custom compilation support for file-types.  ASP.NET ships with a number of built-in providers in the box -- including support for .wsdl and .xsd files.  These providers can automatically generate the appropriate proxy or dataset class for you without you having to manually generate code and keep it in sync with the declarative format (I believe they also generate as partial types -- so you can add your own object model and methods to extend them).