About Me

About Adam Kinney My name is Adam Kinney and I work for Microsoft as a guy who loves UI platforms like Silverlight, WPF and Microsoft Surface. I often talk about these technologies from a developer and designer prespective. More about me...

Silverlight News

Making the ALT.NET Geek Code badge video

- tagged

Last week Scott Hanselman posted the ALT.NET Geek code page which allows you to choose different processes and tools that make you happy.  After making your selections you are given to identify yourself and your interests to others who can translate the code.

Scott's code reads as:

IOC(SM):IOC(CW):IOC(Nin):MOC(RM):MOC(MQ):MOC(TM):MOC(!?):TDD(NU):TDD(Xu):TDD(Mb):SCC(Svn):SCC(Git):SCC(TFS):SCC(Lame):SCC(Gmail):ORM(NH):ORM(SS):ORM(L2S):XPP(++):DDD(T+):DDD(+):DDD(!?):JSL(Jq):JSL(MS):JSL(p+):JSL(s+):CIS(CC):CIS(psh+):CIS(bat-):GoF(++)

This was code was in need of some serious visualization goodness and I was happy to oblige.  The following video explains the story of the ALT.NET Geek Code badge.

Download the Application Source

posted on Apr 29th, 2008 | Permalink | Comments (2)

SilverlightDevCamp Seattle - June 13-14th 2008

Two of my favorite events I attended last year were the SilverlightDevCamps in San Francisco and Chicago.  The DevCamp event is run in a community-driven barcamp style where conversation is open and free and results in intriguing dialogue and sessions. (my SF Recap, video from SF, photos from SF, photos from Chicago)

The time has come for a SilverlightDevCamp in Seattle and this one should be very interesting.  Its the first DevCamp since the release of the Silverlight 2 beta at MIX and the amount of features and functionality has about tripled in size since Silverlight 1.0.

Looking forward to an awesome event!


Oh and they are holding a contest for a SilverlightDevCamp Seattle logo.  Prizes include untold riches from the sponsors and recognition at the event of your design skills.  If only Seattle had some iconic totem or building to use as a starting point...

posted on Apr 22nd, 2008 | Permalink | Comments (0)

Building the Gamercard Part 3, Updating the UI

This is Part 3 in a series covering how the Xbox Gamercard application was made using Silverlight 2.  In this part I will cover how the interface is updated once the data is retrieved.

In the first part of the series I showed a few highlights of how the UI was created.  In the second part, I showed the call to the web service and how the results were parsed using LINQ to create a .NET object.  In this part, I will show how an instance of the custom XboxInfo class is used as the data source for Data Binding the UI Elements.

Data Binding

Returning to the parsing code from part 2, the web service results are returned as a string which is handed to a parsing method.  Within the method an XDocument is created that a LINQ query is run against that returns an XboxInfo class instance.  The DataContext is then set to the instance and this is where the Data binding magic begins.

XDocument xDoc = XDocument.Parse(xmlContent);
XboxInfo gamer = ...//LINQ query
this.DataContext = gamer;

In the above code, this is referring to an instance of my XboxGamerCard.Page class which is the UserControl used as the main or RootVisual control of the application.  This class is where the main UI is declared in XAML including the main Grid control from Part 1.  Rather than writing the familiar manual lnkGamerTag.Text = gamer.GamerTag type code we can use Data Binding to update the UI.

A simple example of the binding is shown below.  The Content property of the HyperlinkButton is set to a GamerTag property of the DataContext.  Once the DataContext of the UserControl is set, the Bindings are notified and the values are updated.  In the case of the Binding below, the DataContext is checked for a GamerTag property.  If it exists the value of the HyperlinkButton.Content property is set automatically to the GamerTag value.  If the DataContext GamerTag property were to change again, the Content property would be updated as well.  The target property and the data source are now connected via the Binding instance.

<HyperlinkButton Content="{Binding GamerTag}" ... />

Another interesting feature of Data Binding is converting values.  The screenshot of the Gamercard to the right shows four green lights indicating that the status of this Quemark person is online rather than offline, which is indicated by a single red light.

Using the Binding syntax below, the Canvas.Visibility property is bound to the StatusText property and a custom converter is set.   Notice in the syntax, that StatusText is preceded by PresenceInfo.  A property of the XboxInfo class, PresenceInfo is a custom class as well with a string property titled StatusText.  With the "." notation you can navigate the property chain.

<Canvas Visibility="{Binding PresenceInfo.StatusText,
        
Converter={StaticResource StatusVisiblityConverter}}">...</Canvas>

The StatusText property is a string but the Visibility property is an enum of type System.Windows.Visibility.  By implementing the IValueConverter the custom converter class can be used to check a string value and return a Visibility enum value.  The code is simple to write and looks like the following:

public class StatusVisiblity : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        string statusText = (string)value;
        if (statusText == "Offline")
        {
            return Visibility.Collapsed;
        }
        else
        {
            return Visibility.Visible;
        }
    } 
}

One other interesting area is RecentGames, a property of the XboxInfo class of type List<XboxUserGameInfo>.  In order to show this collection, The RecentGames property is used as the datasource for an ItemsSource property of ItemsControl added to the page.  When bound, each item in the collection is shown using the the defined ItemTemplate where Binding are defined using the properties of the XboxUserGameInfo class.

<ItemsControl ItemsSource="{Binding RecentGames}" ...>
    ...
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Grid ...>
                <Rectangle ... />
                <Image Width="32" Height="32" Source="{Binding Game.Image32Url}">
                    ...
                </Image>
            </Grid>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Databinding can be very useful and is definitely worth looking more into.  Not only does it save the developer some lines of code, it can also be useful during design-time due to the Data Binding support within Expression Blend.  That sounds like another blog post in the works...

For more information on Data Binding check out Jesse Liberty's Data Binding Tutorial.

Refreshing the Data

The last piece I am going to talk about covers browser integration.  The Gamercard Script Builder page (pictured to the right), has a textbox where you enter your Gamertag and when you TAB out of it the embed script is added to the textarea below.  In addition the Gamercard application to the right is updated and you see a preview of what your card will look like.

Due to the awesome browser integration available in Silverlight 2 this was very simple to do.  The first step is to expose the RequestGamerXml method (explained in Part 2) to JavaScript which is done by adding an attribute to the public method and registering the class instance as a ScriptableObject.

[ScriptableMember]
public void RequestGamerXml(string gamerTag){...}

void Page_Loaded(object sender, RoutedEventArgs e)
{
    HtmlPage.RegisterScriptableObject("GamerCard", this);
}

Then in the JavaScript function call into that object and pass a variable to the exposed method.  slPlugIn is the id of the object tag used to instantiate the Silverlight application.

<script type="text/javascript">
function genScript(){
    ...
    document.getElementById("slPlugin").content.GamerCard.RequestGamerXml(tagEntered);
    ...
}
</script>

And that's it!  Now every time the Gamertag entered changes, the RequestGamerXml method is called and the DataContext is reset.  Thanks to the Bindings in place the interface Elements are updated to stay in synch with the data source.

For more information on browser integration try the Browser Integration Hands-on Lab from MIX08.

Conclusion

This was a fun project and the ideas keep on coming.  Expect to see more in the future from the Xbox Friends Watch Lab.

Other parts in this series are available:

  • Building the Gamercard Part 1, Composing the UI
  • Building the Gamercard Part 2, Retrieving the Data

     

    my diigo links silverlight+databinding, silverlight

  • posted on Apr 17th, 2008 | Permalink | Comments (0)

    Building the Gamercard Part 2, Retrieving the Data

    This is Part 2 in a series covering how the Xbox Gamercard application was made using Silverlight 2.  In this part I will cover how the data is retrieved.

    First off, the Xbox Friends Watch series of experiments would not be possible without the existence of the Xbox Gamertag Data Service.  The service provides both SOAP and REST based access and I'll being showing you how the REST-based service was used within the application.

    Get the data

    When the application loads it looks within the InitParams parameters for a gamertag Key.  The value is then passed to a RequestGamerXML function that creates a WebClient instance and queues and asynchronous request to the Gamertag Data service.

    public void RequestGamerXml(string gamerTag)
    {
        string xboxUrl = string.Format("http://duncanmackenzie.net/services/GetXboxInfo.aspx?GamerTag={0}", gamerTag);
        WebClient xboxService = new WebClient();
        xboxService.DownloadStringCompleted += new DownloadStringCompletedEventHandler(xboxService_DownloadStringCompleted);
        xboxService.DownloadStringAsync(new Uri(xboxUrl));
    }

    The completed event handler is then called once the request is complete, providing the opportunity to handle errors and pass the result string to a parsing function.

    private void xboxService_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
    {
        if (e.Error == null)
        {
            loadGamer(e.Result);
        }
        else{...}
    }

    The WebClient class is perfect for simple requests like this, but for more advanced network requests check otu the HttpWebRequest class.

    For more information on Networking check out Karen Corby's two networking part series (Part 1, Part 2) and Laurent Bugnion's Downloading Zipped files

    From XML to objects

    Now that the data has been returned from the REST service as string, the values need to be parsed for display.  Luckily there just happens to be a new simple and fun way to do this using LINQ.

    LINQ, new in Silverlight 2, is a huge topic in and of itself and there are many videos, screencasts and blog posts about it.  In the context of the Gamercard application it is interesting because you can perform powerful language-integrated queries, sets and transforms.  Specifically turning the string results into an XboxInfo class with all of its property values set and sub classes created and populated as well.

    First take a look at the XML response: http://duncanmackenzie.net/services/GetXboxInfo.aspx?GamerTag=festive%20turkey

    Looking at the XML you can get an idea of the backing classes needed to serialize and deserialize that data.  The classes I've created have typed properties but otherwise very closely match to the XML result's schema.

    The XML string is used to create a new XDocument instance which can then be queried against to return an XboxInfo instance:

    XDocument xDoc = XDocument.Parse(xmlContent);

    XboxInfo gamer = (from XboxInfo in xDoc.Descendants("XboxInfo")
                                      select new XboxInfo
                                      {
                                          PresenceInfo = new PresenceInfo()
                                          {
                                              Info = (string)XboxInfo.Element("PresenceInfo").Element("Info"),
                                              Info2 = (string)XboxInfo.Element("PresenceInfo").Element("Info2"),
                                              Online = (string)XboxInfo.Element("PresenceInfo").Element("Online"),
                                              StatusText = (string)XboxInfo.Element("PresenceInfo").Element("StatusText")
                                          },
                                          GamerTag = (string)XboxInfo.Element("Gamertag"),
                                          ProfileUrl = (string)XboxInfo.Element("ProfileUrl"),
                                          TileUrl = (string)XboxInfo.Element("TileUrl"),
                                          GamerScore = (string)XboxInfo.Element("GamerScore"),
                                          Zone = (string)XboxInfo.Element("Zone"),
                                          RecentGames = (from XboxUserGameInfo in XboxInfo.Element("RecentGames").Descendants("XboxUserGameInfo")
                                                     select new XboxUserGameInfo
                                                     {
                                                         Game = new Game()
                                                         {
                                                             Name = (string)XboxUserGameInfo.Element("Game").Element("Name"),
                                                             Image32Url = (string)XboxUserGameInfo.Element("Game").Element("Image32Url"),
                                                             Image64Url = (string)XboxUserGameInfo.Element("Game").Element("Image64Url"),                                                        
                                                             TotalAchievements = (string)XboxUserGameInfo.Element("Game").Element("TotalAchievements"),
                                                             TotalGamerScore = (string)XboxUserGameInfo.Element("Game").Element("TotalGamerScore")
                                                         },
                                                         Achievements = (string)XboxUserGameInfo.Element("Achievements"),
                                                         GamerScore = (string)XboxUserGameInfo.Element("GamerScore"),
                                                         DetailsURL = (string)XboxUserGameInfo.Element("DetailsURL"),
                                                         LastPlayed = (DateTime)XboxUserGameInfo.Element("LastPlayed")
                                                     }).Take(5).ToList<XboxUserGameInfo>()
                                  }).First();

    That may look like a mouthful but if you compare the XML results and the query you can see the relationship between the two.  The end result a fully populated XboxInfo object that can be used to update the interface.

    For more information on LINQ read the overview on MSDN and Scott Guthrie's tutorial Using LINQ with ASP.NET

    Other parts in this series are available:

  • Building the Gamercard Part 1, Composing the UI
  • Building the Gamercard Part 3, Updating the UI

     

    my diigo links silverlight+networking, silverlight

  • posted on Apr 17th, 2008 | Permalink | Comments (0)

    Building the Gamercard Part 1, Composing the UI

    I recently published an Xbox Gamercard based on Silverlight 2 Beta 1.  In that post a talked a little bit about the XAP model which now allows you to embed Silverlight applications via the object.  In Silverlight 1.0 application instantiation was handled via JavaScript and HTML with loose files.  Now with the XAP model we have a single file and object tag support which allows for simpler embedding of an application.  Especially by a non-developer web user, which is the main audience for the Gamercard application.

    In this three part series I will highlight a few of the new features in Silverlight 2 and how the application was built. In this first part, I'll cover how the interface for the application was composed.

    First stop the Grid

    Ah, the mighty Grid, master of layout panels.  The Grid is the embodiment of what you always wanted from the HTML table. That is, of course, back in the  old days when you were still using the table for layout.

    The screenshot to the right shows the Grid used for the Gamercard with the ShowGridLines property turned on.  GridLines can be very useful as an aid when in design mode, whether you're working in Blend or Visual Studio.  The Grid is the main container of all the elements seen on the screen.

    Like the HTML table, the Grid is made up of Columns and Rows and elements can span multiple Columns or Rows using ColumnSpan and RowSpan, respectively.

    Unlike the HTML table there is no table cell concept.  Each Element  defines its location using attached properties provided by the Grid class instead.

    The TextBlock at the bottom of the grid with the text value of "Shadowrun (G 885/1000)" is defined in XAML as:

    <TextBlock Grid.Row="4" Grid.ColumnSpan="2" ...  />

    This places the TextBlock in the first column (0) which is the default value of Grid.Column and in the fourth Row based on the value set.  It also spans both columns to make room for longer text that would not fit in the first column which has a width of 76 pixels.

    Columns and Rows are defined and their values are set using the ColumnDefinitions and RowDefinitions. In the case of the Gamercard, the height and width of each column and row is set to a specific value except for the last one which is set using the star (*) syntax.

    The star denotes proportional sizing, which means here the last Row and Column take up the remaining space in the Grid not yet claimed by other Columns or Rows.

    Proportional sizing can also be used across Columns or Rows.  For example, the width of the first column could be set to "*" and the second set to "2*".  If the Grid was 150 pixels wide, the first column would end up being 50 pixels wide and the second column would be 100 pixels wide.

    For more on Grids check out Jesse Liberty's Controls Tutorial or his Grids and Stack panels video.

    Composing Controls

    Another great feature of Silverlight is the ability to compose controls.  In the Gamercard application, a HyperlinkButton control is used to link to the Gamertag profile page.  Rather than simply using text for the link, in this case I wanted to use the the Profile Image which was easily accomplished using the XAML below:

    <HyperlinkButton ....>
        <HyperlinkButton.ToolTip>
            <ToolTip Content="View Profile" />
        </HyperlinkButton.ToolTip>
        <Canvas>
            <Rectangle Width="64" Height="64" RadiusX="3" RadiusY="3" Fill="#333" />
            <Image Width="64" Height="64" ...>
                <Image.Clip>
                    <RectangleGeometry RadiusX="3" RadiusY="3" Rect="1,1,62,62" />
                </Image.Clip>
            </Image>
        </Canvas>
    </HyperlinkButton>

    The first child element is the attached property for ToolTip, similar to the Grid.Row property mentioned above.  Although not a part of the HyperlinkButton's composition, this shows a simple version of implementing the Content property.  The same property is available on the HyperLinkButton control which could be set to a string value, which is how we would define a simple text link.

    As mentioned though, we want to customize the Template of the Content property which is done implicitly when child nodes that are not attached properties or added within the Control.  Content can only hold a single child, which was why the Canvas element is added first as a simple container.  After that a Rectangle and an Image control is added to provide the custom look.  And due to the composing functionality, the Hyperlink links to the profile page whether you click the Rectangle or the Image.

    The extra attributes on Rectangle and Image provide the round corners as well as clipping the raster image so it appears that it has transparent round corners.  I found the clipping method to be very useful when customizing images provided by an external service.

    ...pausing to think about more mashup possibilities...

    For more on Composing Controls check out Jesse Liberty's Controls Tutorial.  And if you have not watched it yet please check out Karen Corby's Controls presentation at MIX08.  The slides and sample code is available and she does a great job showing off how to compose, style and skin controls.

    Other parts in this series are available:

     

    my diigo links XAP, silverlight+controls, silverlight

    posted on Apr 17th, 2008 | Permalink | Comments (2)

    PDX Webfoot and then Olympia

    This Saturday I will be attending and speaking at the PDX Webfoot event.

    Webfoot is a web-focused event for all creatures, techie and non-techie alike. Bring the whole family for a day of education, entertainment, and fun!

    If you take a look at the schedule, you'll see that we'll be talking about topics like Safe Internet Browsing, IE8, Flex and Silverlight.  We should have a pretty interesting crowd as the different groups involved include the CodeTrip crew, PADNUG, PDXUX and Ryan Miller form the Portland Adobe Developer User Group.

    After the event in Portland I will be riding the CodeTrip bus to help out with the Olympia event, which is the final stop before the tour ends at the MVP Summit back in Seattle and near much closer to where my car will be.

    (And for those of you from the PDXUX group, I'll make sure I am well -fed and well-rested before Saturday.)

    posted on Apr 7th, 2008 | Permalink | Comments (0)

    Silverlight Sidebar Gadgets on Windows Vista 64-bit edition

    Silverlight-based Sidebar gadgets

    At this time (version 2 Beta 1), in the life of Silverlight there is no 64-bit support.  I'm not certain if or when 64-bit support will appear, but I don't think Vista 64-bit users should go without Silverlight. And the good news is they don't have to.

    In most cases, running Silverlight applications should not be an issue.  IE7 and Firefox run in 32-bit mode by default allowing Silverlight to run as expected.

    A different scenario which needs some attention is Sidebar Gadgets that use Silverlight.  The Windows Sidebar application (sidebar.exe) runs by default in 64-bit mode and in order to use a Silverlight-based gadget you need to run sidebar.exe in 32-bit mode.

    You can do this simply by closing the default Sidebar and navigating to C:\Program Files (x86)\Windows Sidebar and double-clicking sidebar.exe.  Now you can successfully run the new Channel 9 gadget or Xbox Friends Watch in your sidebar and enjoy knowing what the latest C9 video or or when your Xbox friends are online.

    If you're happy with the results, you can make sure the 32-bit version of Sidebar is added to your system startup by following the step below:

    1. If you are still running the 64-bit Sidebar, right-click the Sidebar and select Properties.
    2. Uncheck the Start Sidebar when Windows starts option and hit OK
    3. Open C:\Program Files (x86)\Windows Sidebar in Windows Explorer
    4. Right-click sidebar.exe and select Copy
    5. Right-click the Windows Orb and select Explore
      Windows Orb
    6. In Windows Explorer navigate to the folder entitled ..Programs/Startup
    7. Right-click in the folder and select Paste Shortcut
    posted on Apr 4th, 2008 | Permalink | Comments (3)

    Xbox Friends Watch - releasing v1 of the new Gamercard

    Xaps

    One of my favorite new features of Silverlight 2 is the new Application model.  Moving on from version 1.0, where JavaScript instantiation was the only way to start your app and you were restricted to the same origin as the web page, enter the Xap.  The new packaging model, not only bundles your assets into one file enabling simpler deployment, it also enables application instantiation via the object tag.  Plus, the Xap can live on a separate server than the web page, which enables the creation of embeddable applications with Silverlight.

    <applause!>

    - More information about Xaps and the new Application Model

    Xbox Friends Watch Gamercard

    In celebration of this functionality, I've expanded on the Xbox Friends Watch line and added a new GamercardXbox.com currently offers a gamercard which renders a fairly static set of html and images which is embedded via an iframe.  Using Silverlight for its interactivity, data stack and portability and the Xbox Gamertag Data Service I thought I would be able to enhance the experience as well as expose the status information provided by the Data Service.

    Below is the result of my experiment and using the Gamercard page you can create a script to add the Gamercard to your site.

    Get Microsoft Silverlight

    - Add the Gamercard to your page

    Next Steps

    My next post will include some of the technical details covering how the Gamercard was created and some of the techniques I used to work with Silverlight 2.

    If you're interested in following the saga of the Xbox Friends Watch components you can subscribe to my XBF feed, follow XBF on Twitter or follow XBF on Facebook.

    posted on Apr 2nd, 2008 | Permalink | Comments (5)

    Silverlight rehab session recorded last week

    There are a few of us here, who really, really like Silverlight.  In some cases a little bit too much.  They filmed one of our group therapy sessions last week to raise awareness.

    I hope this video can help bring comfort to some of you, knowing that you're not alone.


    Silverlight Rehab - You're not alone

    Stay in the light!

    posted on Mar 31st, 2008 | Permalink | Comments (4)

    Xbox Friends Watch - v1.2 has been posted

    Download 1.2 now

    This is a very small update that is the same as version 1.1, except that a fix has been added to allow the sidebar gadget to run if you have installed the Silverlight 2 Beta 1 runtime.

    There was a small bug in path resolution that the team is aware of and will hopefully be able to fix in future releases.

    When setting the source parameter during application initialization the workaround involves making the path absolute using the gadget protocol.

    Old code:

    source: "xaml/SideBar.xaml"

    New code:

    source: "x-gadget:///xaml/SideBar.xaml"

    I know this is not the promised version 2 that many of you are looking for, but getting past this bug is a step in the right direction.

    Thanks to Donovan for finding the solution!

    posted on Mar 26th, 2008 | Permalink | Comments (5)

    Interviewed at MIX, audio now available

    In case you haven't heard, we meaning Microsoft threw a little party in Vegas this month called MIX.  You should really come visit MIX sometime maybe next year, say around March 18-20th in 2009?

    We like to have 72 hour conversations and talk about all the things we have going on for the web.  This year we announced Silverlight 2 Beta 1.

    Man I was excited, and maybe after listening to these two interviews a bit overly excited.  On Wednesday I received two emails within 20 minutes of each other notifying me that my interviews had been posted.

    First, I was interviewed by Erik Mork from the Sparkling Client podcast.  Sparkling Client is podcast they have recently started, but judging by the production quality and the content, I feel they are off to a good start.  My interview is available as Show 6: interview with Adam Kinney of Microsoft.

    Erik and Monica do nice job leading into the content and my favorite interviewee-caught-in-the-headlights quote is when I say "algorithms to be figured out".  Other than algorithms we discuss Silverlight 2 and the direction that we are headed with it.

    The goal of Sparkling Client is to be concise 15-minute podcast that entertains as well as informs their audience about Silverlight and the rich web to come.  You can find more shows on their site SparklingClient.com.

    The Thirsty Developer is another podcast that I interviewed for at MIX.  Dave Bost and Larry Clarkin two Microsoft evangelists from the Midwest produce this show which has a nice "hanging out in Chicago" feel.

    Dave interviewed me about Silverlight 2 and we cover what it is and what some of the great new features are.

    I listened to this one and found a few errors that I should clear up.

    • Templates - when discussing styling and skinning, I meant to discuss templates but ended up discussing the ContentPresenter and its ability to composite arbitrary controls.  This is not changing the template of the control but rather changing the content.  Jose Fajardo has an interesting post covering skinning and styling which is a bit more accurate.
    • Installation - I just wanted to strengthen the point that the installation process is still as customizable as before in 1.0.  Hard Rock has a nice custom screen and FaceOut does too and they include a timer to start the application as soon as Silverlight is installed.  Kudos for the timer!

    It was a very fun process to be a part of and I thank both parties for their kind words.

    posted on Mar 21st, 2008 | Permalink | Comments (0)

    Presenting Silverlight 2 at the PDXUX meeting next week

     

    Next week I will be presenting an overview of Silverlight 2 at the March PDXUX meeting in Hillsboro, OR, US.

    The PDXUX defines itself as:

    This is a group for both developers and designers, something unique in Portland as all other groups target either one or the other. The "UX" in the name stands for "User Experience", something which both designers and developers must care about and work on together in order to succeed. The primary goals of this group include: unity, design, platform, reality.

    I've always wanted to attend a Special interest Group like this, so why not start by speaking at one.  There is certainly plenty to talk about with the recent release of the Silverlight 2 Beta.  I've been holding in Silverlight 2 goodness, I'll probably just jumble my words together in excitement.

    To make up for the possible overly-excited state, I will be bringing copies of the First Look at Microsoft Silverlight 2 book that was included with the MIX08 Swag Bag.  Hopefully enough for everyone, so if you plan to come make sure you let Kelly know.

    posted on Mar 14th, 2008 | Permalink | Comments (0)