Welcome to MSDN Blogs Sign in | Join | Help

Building a Storefront using ASP.NET MVC

If you're interested in what's going on with the new ASP.NET MVC project, Rob Conery has just launched a new series of webcasts that document the process of him building out an MVC Storefront.  He's been iterating on this idea for a few weeks and it's coming together very nicely - I think you'll find it a nice change of pace from standard webcast fare.

Rob's ASP.NET MVC Storefront Series

Posted by sburke | 0 Comments

Tutorial: Writing a Templated Silverlight 2 Control

We're going to create this control:

image

It's called the ExpandoHeaderControl, and it does two basic things.  First, it has an area for a header, and a collapsible content area, with a ToggleButton to manage when the content is visible or not (e.g. collapsed).

For this tutorial, I'm trying to simplify concepts as much as possible. For more sophisticated, industrial-strength concepts, check out the full control source available for the Source Code for Silverlight 2 Beta 1 Controls.

I'm continually amazed by how much power Silveright 2 provides.  This control will require about 100 lines of code to write.  Really!  Let's get started.

Step 1: Defining your control's template parts. 

What are the parts of your control that the logic has to interact with?  These should be the the defined "parts" of your control.  There are two types of objects that your control cares about.  First, it's the elements that are going to drive the UI or layout.  And, second, are the transition Storyboard animations that will cause your control's UI to move from one state to the next.  Let's focus on the UI parts right now.

We want to make sure that we give designers the most flexibility possible.  So our code should be as general as possible.  If you look at the code in the Silverlight 2 Beta 1 Controls package, you'll see great examples of this.  The code running the control is very agnostic about what the UI is actually doing.

So for our control, we'll define a few parts:

  1. The RootElement, which is the container for all of the UI.  This is important because it's also where we'll stick the resources that have our Transitions.
  2. The HeaderElement, which is the element that contains the header content.
  3. The ContentElement, which is the element that will parent the main content in the collapsible area.
  4. The ButtonElement, which is the ToggleButton that will control the collapsed state.

Usually, when we start writing our control, the first step is to add these attributes to our control class:

   [TemplatePart(Name=ExpandoHeaderControl.RootElement, Type=typeof(FrameworkElement))]
   [TemplatePart(Name=ExpandoHeaderControl.HeaderElement, Type=typeof(ContentControl))]
   [TemplatePart(Name=ExpandoHeaderControl.ContentElement, Type=typeof(ContentControl))]
   [TemplatePart(Name=ExpandoHeaderControl.ButtonElement, Type=typeof(ToggleButton))]    
   public class ExpandoHeaderControl : ContentControl
   {        
       private const string RootElement = "RootElement";
       private const string HeaderElement = "HeaderElement";
       private const string ContentElement = "ContentElement";
       private const string ButtonElement = "ButtonElement";  

Note that we do two small, but important, things here.  First, we use static strings for the element names.  This is good practice because we'll use them again later.  Second, we specify what type of element the Template Part needs to be.  This should be the most general type that you need for a given element.  If you use specific types here (for example Grid instead of FrameworkElement for the root), it will be much more difficult to template your control.  In the case of the ButtonElement, we chose ToggleButton rather than Button so we can ensure that it has a specific state.  If we used just Button (or ButtonBase), we'd need to track this ourselves. 

Step 2: Define your control's transition animations

What are the animations that your control will need to transition from one state to the next?  Defining transitions is just the same as defining the parts - use the TemplatePart attribute.  The main difference here is that the type will always be Storyboard.

Let's add in the animation declarations:

[TemplatePart(Name=ExpandoHeaderControl.RootElement, Type=typeof(FrameworkElement))]
    [TemplatePart(Name=ExpandoHeaderControl.HeaderElement, Type=typeof(ContentControl))]
    [TemplatePart(Name=ExpandoHeaderControl.ContentElement, Type=typeof(ContentControl))]
    [TemplatePart(Name=ExpandoHeaderControl.ButtonElement, Type=typeof(ToggleButton))]    
    [TemplatePart(Name = ExpandoHeaderControl.OpenAnimation, Type = typeof(Storyboard))]
    [TemplatePart(Name = ExpandoHeaderControl.CloseAnimation, Type = typeof(Storyboard))]
    public class ExpandoHeaderControl : ContentControl
    {        
        private const string RootElement = "RootElement";
        private const string HeaderElement = "HeaderElement";
        private const string ContentElement = "ContentElement";
        private const string ButtonElement = "ButtonElement";        
        private const string OpenAnimation = "OpenAnimation";
        private const string CloseAnimation = "CloseAnimation";

Step 3:  Write your OnApplyTemplate logic

After a control is loaded, its UI is populated from it's template.  To do this, the override of OnApplyTemplate is called by the framework.   For the most part, this code always looks about the same. 

For each of your parts, you're likely to want to have a member variable to access the element later, if you'll need to manipulate it:

        private FrameworkElement _rootElement;
        private ToggleButton _buttonElement;
        private Storyboard _openAnimation, _closeAnimation;
        
        protected override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            // Fish out all of the template children and wire them up.
            //
            _rootElement = GetTemplateChild(ExpandoHeaderControl.RootElement) as FrameworkElement;

            if (_rootElement != null)
            {
                     
                _buttonElement = GetTemplateChild(ExpandoHeaderControl.ButtonElement) as ToggleButton;
                _openAnimation = _rootElement.Resources[ExpandoHeaderControl.OpenAnimation] as Storyboard;
                _closeAnimation = _rootElement.Resources[ExpandoHeaderControl.CloseAnimation] as Storyboard;
            }
        }

One thing to note is that we access parts and transitions differently.  Note how we access the transitions out of the root element's Resources collection. 

Step 4: Create any properties you'll want to bind to in your template

Silverlight and WPF have a feature called Template Binding.  Template Binding allows you to "pass through" settings from a control to the UI defined in the Template.  For example, imagine your control has a TextBlock in it.  You'd like to be able to set the font properties of that TextBlock in a way that doesn't force the user to understand how the template is put together.  Template Binding allows you to reference properties on the object being templated and set those values into the template, similar to data binding.

Our control has two content areas:  the header, and the content itself.   Since our control derives from ContentControl, it already has a property called "Content" that we can use for the main content area.  But we want another one called HeaderContent to specify that, so we'll create one.  For a property to be bindable, it has to be defined as a DependancyProperty.  Visual Studio 2008 has a built in snippet for defining these.  It's meant for WPF, but it's close enough.  Just type "propdp".

image

When you do this, fill in the various fields.  In this case, the property's type should be object, and it's called HeaderContent:

       // Content property for the header
       //
       public static readonly DependencyProperty HeaderContentProperty =
           DependencyProperty.Register("HeaderContent", typeof(object), typeof(ExpandoHeaderControl), null);

       public object HeaderContent
       {
           get { return (object)GetValue(HeaderContentProperty); }
           set { SetValue(HeaderContentProperty, value); }
       }

The snippet also creates the CLR property with accessors as well!

Step 5: Create a simple Template for Testing

Okay, now it's time to throw some UI in there.  We'll create a very simple template that does everything we need.

<Grid x:Name="RootElement">
        <Grid.Resources>
            <Storyboard x:Key="OpenAnimation">
                <DoubleAnimationUsingKeyFrames Storyboard.TargetName="ContentElement" Storyboard.TargetProperty="(UIElement.Opacity)">
                    <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1.0"/>
                </DoubleAnimationUsingKeyFrames>
            </Storyboard>
            
            <Storyboard x:Key="CloseAnimation">
                <DoubleAnimationUsingKeyFrames Storyboard.TargetName="ContentElement" Storyboard.TargetProperty="(UIElement.Opacity)">
                    <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="0"/>
                </DoubleAnimationUsingKeyFrames>
            </Storyboard>
            
        </Grid.Resources>
    <Grid.RowDefinitions>                                
        <RowDefinition Height="50"/>
        <RowDefinition Height="auto"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="30" />
    </Grid.ColumnDefinitions>
        <ContentControl  HorizontalAlignment="Left" Background="Red" Content="{TemplateBinding HeaderContent}"/>
        <ToggleButton Grid.Column="1" HorizontalAlignment="Right" x:Name="ButtonElement" Content="X"/>
    
    <ContentControl Grid.ColumnSpan="2" Grid.Row="1"  Grid.Column="0" x:Name="ContentElement" 
                    Content="{TemplateBinding Content}" Background="Blue"/>
    
    

</Grid>

Okay, there's our template.  When instantiated, it looks like this:

image

Told ya it was simple.  Let's take a look at what parts we have in the template and how they match up with the control.

In the template, we define the animations at the top as resources off of the RootElement.

Notice we reference them by Key, not by Name.

For this control, we've defined a 2 row, 2 column Grid for our layout.  In the top row, we have the HeaderElement and the ButtonElement.  In the second row, we have the ContentElement, with a RowSpan of 2 so it fills up the bottom.  The red area and the words "header" and "Content Area" come from the control's usage, not the control itself or the template (see below).  In the Template above, you'll see two instances of "TemplateBinding" like "{TemplateBinding HeaderContent}".  When the control is templated, it will substitute the value of the controls "HeaderContent" property in that position. 

In our main XAML page, we've declared this control like this:

<my:ExpandoHeaderControl Width="200" x:Name="Header" Style="{StaticResource SimpleTemplate}" HeaderContent="header">
                        
       <my:ExpandoHeaderControl.Content>
          <Grid>
             <Rectangle Fill="Red" Stretch="Fill"/>
             <TextBlock Text="Content Area" HorizontalAlignment="Center" VerticalAlignment="Center"/>
         </Grid>
       </my:ExpandoHeaderControl.Content>

</my:ExpandoHeaderControl>

You may be wondering where to put that template.

In most cases, the Template will either be inline with the control's declaration (as it's Template property value, example in Step 8 below) in the XAML. This is good for one-off templates but doesn't let you share a template across controls. The more common way is as a resource defined in App.XAML (as we've done here). then it's hooked us as a StaticResource into the controls style property, as you see above.

Step 6: Managing State Changes

Now we've got the UI working, but the button doesn't know what to do when we push it.   Let's go back to our code and fix that.

The way this is usually done is to create properties to represent the state of your control.  In our case, the main piece of state is whether-or-not the control is collapsed.  So we'll add a property "IsOpened" to manage that.

// Property that determines if the expando is opened.
//
public static readonly DependencyProperty IsOpenedProperty =
    DependencyProperty.Register("IsOpened", typeof(bool), typeof(ExpandoHeaderControl), new PropertyChangedCallback(NotifyIsOpenedChanged));

private static void NotifyIsOpenedChanged(DependencyObject dpObj, DependencyPropertyChangedEventArgs change)
{
    bool isOpened = (bool)change.NewValue;

    ((ExpandoHeaderControl)dpObj).IsOpened = isOpened;
}

public bool IsOpened
{
    get
    {
        if (_buttonElement != null)
        {
            return _buttonElement.IsChecked ?? false;
        }
        else
        {
            return (bool)GetValue(IsOpenedProperty);
        }
    }
    set
    {
        if (_buttonElement != null)
        {
            _buttonElement.IsChecked = value;
        }
        else
        {
            SetValue(IsOpenedProperty, value);
        }
        ChangeVisualState();
    }
}

This looks a bit complicated.  We want to be able to bind to this property, so we've declared it as a DependancyProperty.   And we want to make sure it mirrors the actual state of the button, so if we have one of those, we'll just use its checked state.   But if we don't have one from the template, we'll just fall back to our own value.

One other note - we're hooking the changed value here.  If someone changes this value, we want to know about it so that we can update our state.  When the DP changes, our static handler will be called.  From that, we figure out which control fired the change, and update the state as appropriate.

Now that we've added the property, we can add some code in OnApplyTemplate to hook up to the Button's Click event:

bool opened = IsOpened;

_buttonElement = GetTemplateChild(ExpandoHeaderControl.ButtonElement) as ToggleButton;

if (_buttonElement != null)
{
    // setup the buttons initial state.
    //
    _buttonElement.IsChecked = opened;
    _buttonElement.Click += new RoutedEventHandler(ToggleButtonElement_Click);
    
}

Finally, we want a unified place to manage the state animations.  For that we've created a method called ChangeVisualState which plays the right animations for each state change.

This code may look funny, but to make sure we handle the case of an interrupted transition, we should start the "to" state StoryBoard before stopping the "From" one.

void ToggleButtonElement_Click(object sender, RoutedEventArgs e)
{
    // just update our state.
    //
    ChangeVisualState();
}
private void ChangeVisualState()
{
    // manage the animations base on the current state.
    //                        
    Storyboard toState = IsOpened ? _openAnimation : _closeAnimation;
    Storyboard fromState = !IsOpened ? _openAnimation : _closeAnimation;

    if (toState != null)
    {
        
        if (!_templateLoaded)
        {
            toState.SkipToFill();                    
        }
        else
        {
            toState.Begin();
        }
    }

    if (fromState != null)
    {
        fromState.Stop();
    }
}

We now have a working control!

Step 7: Specifying your default template

You'll notice that all of the SL 2 Beta1 controls have a nice default look and feel to them.  How is this accomplished?  Well, it takes a small amount of trickery.

The way you do this is by defining a XAML file called "generic.xaml" in your project and specifying it as a resource:

image

The Silveright Runtime will look for this resource and hook up templates for each specified type.  Your generic.xaml should look like this:

<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:HeaderControl;assembly=HeaderControl">

<Style TargetType="local:ExpandoHeaderControl">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:ExpandoHeaderControl">
                    <!-- template xml -->
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

Step 8: Making it look nice!

I've been slowly going through Blend Kindergarten over the last few weeks.  I finally managed to get the hang of making nice glassy looking UI and was able to create a template to match.  If you'd like to learn this stuff, I highly recommend the tutorials over at the Liquid Boy blog.  They're simple and quickly taught me enough to be dangerous to myself and others.

I spent quite a bit of time mocking up the UI you see at the top of this post, and I think it looks pretty good.  Notice that I also templated the ToggleButton (as a nested template inside of the ExpandoHeaderControl Template in generic.xaml.

<ToggleButton Margin="4" Grid.Column="1" x:Name="ButtonElement" IsChecked="true">
    <ToggleButton.Template>
        <ControlTemplate TargetType="ToggleButton">
            <!-- glassy round Toggle Button Template XAML -->

        </ControlTemplate>
    </ToggleButton.Template>                                
</ToggleButton>

After I got the hang of this, it became a lot of fun.  The glassy look on the toggle button, and the funny little arrow didn't take too long to put together really.  For someone who doesn't have an ounce of design sense, I'm quite proud of myself.

Finally, here's how the control is declared on the page:

<my:ExpandoHeaderControl x:Name="sample" HeaderContent="Choose Color">
            
        <my:ExpandoHeaderControl.Content>
             <ListBox x:Name="lb" Height="100">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <Grid Margin="2" Width="180">
                            <Rectangle Stretch="Fill" Fill="{Binding}"/>
                            <ContentPresenter Content="{Binding}"/>
                        </Grid>
                    </DataTemplate>                                
                </ListBox.ItemTemplate>
             </ListBox>                       
        </my:ExpandoHeaderControl.Content>

    </my:ExpandoHeaderControl>

Note the "{Binding}" syntax in the data template. Usually you specify a property in there like {Binding Name}, but in this case we're just using the item value itself, as in this case we're binding to an array of strings.

Again, here's the final, working product (if you're on IE/FF on Windows at least, not sure about Safari on Mac - shoehorning this into the blog has issues :))

 

Attached is the full project.  Enjoy!

Posted by sburke | 14 Comments
Filed under:

Attachment(s): HeaderControl.zip

Silverlight 2 Beta 1 Controls Available, including Source and Unit Tests

Get Silverlight 2 Beta 1

If you were watching the Keynote from MIX08, you know that Silverlight 2 Beta 1 is now available for download.

The package includes three components:

1) The Silverlight 2 Beta 1 Runtime - installs the basic Silverlight runtime components

2) The Silverlight 2 SDK - includes - installs tools for building Silverlight applications including the controls

3) Microsoft Silverlight Tools Beta 1 for Visual Studio 2008 - installs a project system for Silverlight that works on Visual Studio 2008, including Silverlight XAML Intellisense, Expression Blend interop, and a design-time view of the Silverlight app that you're building.

Get the Source for the Controls

A few months back, we pulled together a team to build out this list of controls.  As part of this effort, we wanted to make sure we delivered something that would really help customers with using the Beta 1 bits. 

So we decided to do to things:

1) Provide source code for the components so users can use it as a sample, or to make changes to the components themselves.

2) Within that project, also ship the Unit Tests that our developers used when writing the components.

You can also download the Silverlight 2 Beta 1 Controls project package now.  The code in this package exactly matches the controls included with the Silverlight 2 Beta 1 SDK download (for example, TextBox, Image, etc. are implemented directly in the runtime).

Simply download the package and unzip it somewhere on your machine using the self-extracting zip.  Once you've done that, launch the project using the "MixControls.sln" solution in VS 2008.

When you launch the solution, you'll see six projects:

Controls - contains basic controls like Button, Checkbox, etc.

Controls.Extended - contains controls like Calendar, Slider, etc.

Controls.Data - contains the DataGrid control

Controls.Test - Unit tests for the Controls project

Controls.Extended.Test - Unit tests for the Controls.Extended project

Controls.Data.Test - Unit tests for the Controls.Data project

Controls Project Test Projects, Controls.Data, Controls.Extended

As expected, all the source code for the controls is in the project.

image

Testing Silverlight Control Code

You can then run the unit tests for any of the projects by launching it's corresponding "Test" project.  Our test harness runs inside of the browser and runs the unit tests.  There are a lot of them -- almost 1500 for the Controls assembly alone! -- and we've gotten very good code coverage.  In the case of the Controls.Test project, for example, we know we're getting over 80% code coverage from our unit tests.  This facility has allowed us to have very few regressions in our control development work, as well as quickly spot any possible issues in the underlying Silverlight runtime when we pick up daily builds. 

Silverlight Control Unit Tests

As you'd expect, the unit tests are basic but provide some functionality that you need for testing in an animated environment like Silverlight.  For example, if the template for a component defines animations, you need to wait for those to run and for the component to be added to the visual tree before checking the state.  You can't do this synchronously, and you can't access the UI thread from another thread.  So we came up with a unique easy to use system for this that handles all that for you, but still lets you write tests that are in one method and are easy to maintain.

Here's an example:

 

image

CreateAsyncTest takes the component, adds it to the Silveright surface, waits for it's Loaded event to fire, then runs the delegate function you've provided (in this case we're using the C# 3.0 Lambda syntax).  It's very clean and simple and allows very rapid writing and execution of tests.

In the controls themselves, you'll also see lots of things to help you write your own control.  In each controls project, you'll find a file called generic.xaml.  This is where you will find the default templates for all of our controls.  You can either customize this in place, or (preferably) use it as a starting point for creating your own templates.

To use these controls in your Silverlight project instead of the default, just add the DLL to your references as you would with any other .NET project and you're set.

More Resources

We're working on getting more sample apps up to help you do usage and templating of controls, but the online docs really are great, including videos and traditional MSDN content.  For example, check out this topic on templating a Button.  Keep an eye on Silverlight.net for more example apps.

I've found the code in these controls invaluable when building Silverlight applications.  I think you will too!

Posted by sburke | 25 Comments
Filed under: ,

MIX08 in Vegas this week

I'll be headed down to Las Vegas this week to attend the MIX08 show.  This is the third year of MIX and it's by far my favorite Microsoft event, and not just because it's in Vegas.  Like the last two years, this show should provide plenty of great surprises.

I'll be participating in a Panel at 1:30 on Wedneday titled "Opportunities and Challenges in Mashing Up the Web" if you want to find me to chat about Silverlight, AJAX, or anything else.  I'm also looking to do some networking to find some leads on filling an opportunity for a senior developer on my team, so if you know someone who might be a good fit, definitely let me know.

See you in Vegas!

[Update: Fixed the job URL above]

Posted by sburke | 2 Comments
Filed under:

Updated AJAX Control Toolkit release

As you know, the team has been very busy working on controls for the upcoming Silverlight 2 Beta 1 release.  Working on a new platform, with new tools, doing things that have never been done before is more than enough to keep us busy.

But we wanted to make sure we kept some focus on our AJAX Control Toolkit users.  As such, we pulled together some top-requested bug fixes as well as all the fixes we've recently gotten from our users, and have released the 20229 version of the Toolkit.  Thanks to all of our users that have been submitting patches - keep it up!

More details available from Kirti and David about what went into this release.

Posted by sburke | 5 Comments

Getting to know Silverlight 2 Beta 1

What we've been up to in DevDiv...

Today, Scott put up a mammoth post introducing the upcoming Silverlight 2 Beta 1. His post includes an 8 (yes, 8!) part walk through building out an application using it.  I really have to hand it to Scott on this one, and I'm not just kissing up because he's my boss.  He's been talking about this post he's been working on all week, and I went over a little bit of it with him on Monday.  But when I saw it live this morning, my jaw just about hit the floor.  To say the guy is prolific is an understatement.  Just earlier this week, he put up another big roadmap post.

I've been spending this week playing with the overall Silverlight 2 Beta 1 experience, which is coming along very nicely.   The integration between Blend and the Visual Studio tools is really, really nice.   I lack any UI design skills, but I'm starting to be able to build basic apps that look okay, and that's a big step forward to me.  I'm having fun.

image

What my team has been up to...

Like many new, cool, things, my team has been involved with this as well.  As a way to set ourselves up for delivering top-notch Silverlight controls in the future, we agreed to take on the task of building out some of the basic Silverlight controls, including Button, CheckBox, and ListBox.  The controls work great, are easily templatable, and will be very familiar to any WPF users out there.  My team is working with some other teams to deliver a broad suite of controls in Beta1 including DataGrid, Calendar, ToolTip, Hyperlink, and GridSplitter, among others.  Given where we are at in the product cycle, the available control Toolbox is coming along nicely.

But that's why we've been so quiet - it's been very busy.  But the model we're using - having the Silverlight Team build the runtime and a separate team build controls has really driven a lot of great feedback across the teams and resulted in a some real design and quality wins. 

Looking forward to getting this stuff out into your hands!

Posted by sburke | 10 Comments
Filed under:

.NET Framework Source & Symbols available for Windows Server 2008 & Windows Vista SP1

The build of the .NET Framework that we recently released source for was 2.0.50727.1433, which was the VS2008/.NET 3.5 RTM build.  A small change was made to the Framework after that, and this new version, 2.0.50727.1434, was then delivered to Windows Server 2008 and Windows Vista SP1. 

Since the source and symbols have to exactly match what's running on a developer's machine, this meant that customers running these new OS releases lost the ability to access the Reference Source for the parts of the .NET Framework that we've released.  The new symbols and source have now been deployed for the following:

  • mscorlib.dll
  • system.dll
  • system.data.dll
  • system.drawing.dll
  • system.web.dll
  • system.windows.forms.dll

The rest of the released source/symbols weren't updated.

Posted by sburke | 5 Comments

You can't domisticate a server!

Someone just pointed me at this "Stay At Home Servers" website.  Y'all know I'm a big Windows Home Server fan. 

Pretty solid geek humor, check it out!

Posted by sburke | 2 Comments

How to disable optimizations when debugging Reference Source

When you debug code in the .NET Framework using the newly available Reference Source functionality in VS 2008, you may notice that many variables are not available for inspection.

image

This is because you're debugging against retail-optimized code.  In many cases, since you can still step through, this is something that's manageable.

But what if you really need to get a better idea of what's going on?  Fortunately, there is a way.

What you need to do is to tell the CLR not to load the pre-JIT (aka NGEN) images.  Here is how to do it. 

First, create a CMD file that sets an environment variable, then launches Visual Studio.  I called mine "NoOptDevEnv.cmd", and it's contents are as follows:

set COMPLUS_ZapDisable=1
cd /d "%ProgramFiles%\Microsoft Visual Studio 9.0\Common7\ide\"
start devenv.exe
exit

I put this cmd file on my desktop.   When I want to disable optimizations, and only when I want to do this, I launch VS from this command. 

Once in my Visual Studio project, do the following steps:

1) Right click on your project file and choose "Properties"

2) Choose the "Debug" tab and uncheck "Enable the Visual Studio Hosting Process"

3) Launch your application in the debugger.

Now, you'll see full local and member variable information:

image

Finally, you may be asking yourself "What is the Visual Studio Hosting Process (aka VSHost), and what happens if I turn it off?"  And this is a good question.

For the most part, disabling VSHost won't have any major impacts, but it will disable two features that you may be using.

First, you will not be able to do "Debug In Zone", which allows you to debug a process in the context of a security zone such as "Internet" or "Intranet".  That won't work without VSHost.

Second, Design Time Expression Evaluation for class libraries will also not work.  What that means, for example, is that if you are developing a Class Library, you won't be able to execute code from it in the Immediate Window while under the debugger.

In general, I recommend re-enabling VSHost (undo step 2 above) when you are finished with your debugging.

Hope that helps!

Posted by sburke | 8 Comments
Filed under:

Configuring Visual Studio to Debug .NET Framework Source Code

It's finally here - the launch of the .NET Reference Source project.  This post (hopefully!) contains everything you need to know.  Over the past few weeks, we ran a pilot of this feature and collected lots of great data that helped us work through some issues and understand where people were likely to have problems. 

First, though, if you have any problems, please make sure you've followed all of the steps exactly as described.  If you're still having problems, please check the FAQ/Troubleshooting section at the bottom.  If that doesn't work, post a comment below and I'll look into it.

BASIC SETUP

Note this functionality is not available on the Express versions of the Visual Studio 2008 products.

1) Install the Visual Studio 2008 QFE.  This Hotfix just updates a DLL that's part of the Visual Studio debugger that fetches the source files, more details on the download page.

 UPDATE:  If you get an error installing the Hotfix , try inserting your VS 2008 DVD and then running the Hotfix EXE again.  We're looking into the root cause - it's related to having a prior version of VS 2008 (e.g. Beta 2) installed on the machine.  But this workaround should allow the Hotfix to install properly.

 UPDATE (1/18): There were some problems with the QFE link above that have been addressed, sorry for the inconvenience, it's fixed now.

2) Start Visual Studio 2008 and bring up Tools > Options > Debugging > General.  If you are running under the Visual Basic Profile, you will need to check the box on the lower left of the Options Dialog marked "Show All Settings" before continuing (other profiles won't have this option).

Set the following two settings:

  • Turn OFF the "Enable Just My Code" setting
  • Turn ON the "Enable Source Server Support" setting

Your settings should be as below:

image

3) Next, bring up the "Symbols" Page and set the symbols download URL and a cache location.  Specifically, set the three settings below:

    • Set the symbol file location to be: http://referencesource.microsoft.com/symbols
    • Set a cache location.  Make sure this is a location that your account has read/write access to.  A good option for this is to place this path somewhere under your user hive (e.g. c:\users\sburke\symbols)
    • Enable the "Search the above locations only when symbols are loaded manually" option.

When you're finished, the settings should look like the image below:

image

Setup is done!  That's it, really!

DEBUGGING INTO FRAMEWORK SOURCE

For this simple example, we'll start with a blank C# Windows Application project, but it will work the same with a VB, Web, or WPF project.  To walk through this, go ahead and create that project.

Set a breakpoint in Form_Load:

image 

Now run your project to hit that breakpoint and go to your Call Stack window (CTRL+ALT+C).  In the Call Stack, right click a frame that starts with System.Windows.Forms.dll, and choose "Load Symbols".  This will load the symbols for the System.Windows.Forms assembly, which are about 10 megabytes, so the speed of the download will vary according to your connection speed.  Note that Visual Studio may be unresponsive during this time.   However, this download is a one-time cost for each assembly.  The symbols (PDB) file will be cached on your machine, in the directory specified in the steps above. 

Loading Symbols Manually

This will load the symbols for the DLL from the server, and you'll see some information in the status bar to reflect this.  Note that when this completes, the call frames will turn black and line numbers will be available.  Note you'll need to do the Right Click -> Load Symbols step each time you launch a debugging session (but, again, the symbols will now be cached locally so they won't have to download).  For more information on this, see the ADVANCED USERS section below.

image

You have now loaded the symbols for the Windows Forms DLL and can begin viewing the code.  You can view code in any way that you normally would in a debugging session.  In this case you can either Step In to the line of code above, or you can double-click one of the frames in the Call Stack Window.   For this case, we'll step in (F11).

The first time you step into code, we'll be presented with the EULA for accessing the source code.  Please take the time to read this EULA.  If you agree to the terms of the EULA, hit ACCEPT, and the source will then be downloaded.

That's it! You're now debugging .NET Framework Source!

Debugging Form.cs in Windows Forms

Now, for each assembly that you'd like to debug into just repeat the steps above (note you'll only see the EULA once, not for each file).

There are times when the Assembly you'd like to debug into isn't on the call stack, for example in the code below:

image

Before you step in to Graphics.DrawRectangle, you need to get the symbols for System.Drawing.Dll loaded.  To do this, we use the Modules Window (CTRL+ALT+U).  This lists all of the modules (DLLs) loaded by the debuggee.  Just find System.Drawing.DLL in this list, right click, and choose Load Symbols.

Load via Modules Window

Note that once a symbol file is loaded, the path to the symbol file shows up in the "Symbol File" column.

You can now step into the Graphics.DrawRectangle code above using F11!  In this case, note, you'll have to step through the PaintEventArgs.Graphics property code first.

ADVANCED USERS

Normally, each time you launch a debugging session, Visual Studio attempt to download symbols for each DLL that loads into the debuggee process.  As part of this process, it asks each path specified in the Debugging Symbols dialog for the corresponding PDB.  Some projects load a LOT of DLLs which won't have symbols available, so this process can significantly impact debugger startup time as this probing occurs.  It is mainly for this reason we've recommended manual symbol loading in the steps above; we don't want using this feature to degrade the debugging experience across-the-board.

There is, however, a way to allow automatic symbol loading (which avoids the "Load Symbols" step) in a way that minimizes performance impact.   This is for more advanced users because it requires regular trips back to this Debugging Symbols Dialog.  Note you can quickly get to this dialog by choosing the "Symbol Settings..." item on the right click menus pictured in steps above form the Call Stack or Modules windows.

The key is to get all of the symbols for a given project type downloaded and cached locally, then turn off the automatic symbol downloads.  This will prevent the pings at debugger startup as well.

To do this, configure your setup as above with the following difference: Uncheck the "Search from the above locations..." item on the dialog.

Now, launch your project in the debugger.  This will cause all of the symbols available for the DLLs in your process to be downloaded at on-demand as the DLLs are loaded into the process.  Depending on your connection speed, this could take a while (it's generally about 50MB of symbols), so it's a good idea to hit F5 then go do something else for a while.  Again, these symbols are cached so this is a one-time cost.  Visual Studio will likely be unresponsive during this download process.

image

Once that process has completed, stop the debugger, and UNCHECK the the Reference Source Server symbol location, and hit OK:

image

Now when you launch the debugger, symbols will load automatically and you'll be be able to step in and through call stacks normally.  Note if you switch to a different project type (that has different assemblies), or have assemblies that are loaded later by your project, you can just repeat these steps to get any assemblies you don't have cached locally). 

FAQ/TROUBLESHOOTING

1) Which assemblies are currently available for symbol/source loading:
  • Mscorlib.DLL
  • System.DLL
  • System.Data.DLL
  • System.Drawing.DLL
  • System.Web.DLL
  • System.Web.Extensions.DLL
  • System.Windows.Forms.DLL
  • System.XML.DLL
  • WPF (UIAutomation*.dll, System.Windows.DLL, System.Printing.DLL, System.Speech.DLL, WindowsBase.DLL, WindowsFormsIntegration.DLL, Presentation*.dll, some others)
  • Microsoft.VisualBasic.DLL
2) When I choose "Load Symbols" I get an Open File dialog asking for the PDB.

This can be caused by one of four situations:

  • You didn't configure the symbol location URL properly from basic setup above.  Ensure it's http://referencesource.microsoft.com/symbols.
  • You chose a cache symbols directory that your user account doesn't have write permissions for.
  • You attempted to Load Symbols for a DLL that's not available in the list above
  • You have a different version of the .NET Framework on your machine - this can happen, for example, if you're running a Windows Server 2008 Beta.  To check this, go to the Modules Window in Visual Studio (CTRL+ALT+U) and ensure that the version number of your mscorlib.dll is 2.0.50727.1433, as below.  If not, make sure you install the RTM Version of the .NET Framework 3.5.

  • Check your "C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE" (or wherever you installed VS) for a file called symsrv.no.  If that file exists, rename it to symsrv.yes, and restart Visual Studio. 
3) When I try to open or step into a source code file, I get a dialog that says "Source is not available for this location" or I get an Open File dialog for the file.

First, see FAQ item (2) above to ensure the symbols for the DLL were loaded successfully.  You can verify this by looking in the Modules Window under the "Symbols Status" column.

If the Symbols Status is "Symbols loaded.", check the following.

  • If you have configured Microsoft Symbol Server in the past, you may have already downloaded symbols for this DLL that don't contain source information.  Try specifying a different cache path or deleting your existing cache path, and then repeating the Load Symbols command.   See FAQ #4 for more information on this.
  • Ensure you have checked the "Enable Source Server" item on the Tools -> Options -> Debugging -> General page
  • Ensure that your account has read/write access to your cache path
  • If you have _NT_SYMBOL_PATH set, it will override these settings.  Details here.
4) I also use Microsoft Symbol Server to download symbols.  What's the difference? Can these two programs co-exist?

Microsoft Symbol Server provides symbols without any source information in them.  That information has been removed (sometimes referred to as "stripped") before publishing.  The symbols provided on the Reference Source Server are full symbols with debugging information.

The key to using both is to have your Reference Source path above the Symbol Server Path so that those symbols are searched/found first.  As described in the ADVANCED USERS section above, you'll likely want to launch your debugger once with this configuration to get all the symbols downloaded, then uncheck both of these paths to avoid debugging launch slowdowns in the future.  Also note that this may conflict in the future as more DLLs are added to the Reference Source Project.  Meaning, if you've already downloaded the Symbol Server symbol, you'll need to delete that or change your cache path to get the Reference Source one (Visual Studio has no way of knowing which is which).

image

One final note here, if you have the Microsoft Symbol Server configured via _NT_SYMBOL_PATH, you'll need to add the Reference Source path above to that path as well - _NT_SYMBOL_PATH overrides the above settings.

5) Does this work with 64-bit?

Yes, we've also provided 64-bit versions of the PDBs.  Note some DLLs work on multiple architectures, so not all of them need a separate 64-bit PDB.

6) How do I set breakpoints in Framework code?

Visual Studio requires the code to exactly match what is expected by the PDB.  The source publishing process, however, makes some small updates to the code, such as pushing a standard copyright banner to the end of the source file.  This changes the signature (CRC) of the code file.  However, it's still easy to set a breakpoint. 

Just set your breakpoint as normal (you'll see it fails with a little warning icon), then right click the breakpoint and choose "Location..."

image

Then check the "Allow Source to be Different" box, hit OK.

image

Now the breakpoint will set successfully.

If you find yourself doing this often, you can also disable source matching in general by disabling Tools->Options->Debugging: “Require source files to exactly match the original version.”

7) Why don't some features like "Go To Definition" work?

Browse database information is separate from symbol (PDB) information within the debugger, so that information is maintained by the project system when a project is compiled and is not present within the symbol files.  So, unfortunately, that ability is not available here.

8) Why are some member or local variables unavailable?  Why can't I step into certain functions or lines of code?

The .NET Framework bits that you have installed on your machine are “retail” bits, which means they are optimized for size and performance.  Part of this optimization process removes certain information from the process when it is no longer needed.  Debugging retail assemblies reflects this.  However, most debugging information is still present in the session, and setting breakpoints earlier in a function often allows it to be visible.   Another aspect of retail builds is that some small methods may be “inlined” which means you will not be able to step into them or set breakpoints in them.  But for the most part you can step and debug as normal.

9) Why does it take so long to download some source files?

Some source files are very large - nearly 1MB - and unfortunately many of these are the common ones to download.  However, most are significantly smaller and download quickly.  Source files must be downloaded each time you restart Visual Studio.  They are not persistently cached like symbols are.

10) Can I just download all of the code at once?

Not currently, but we are currently working on enabling this functionality in the future.

11) When I debug into VB code, for example in Microsoft.VisualBasic.dll, there is a C# style comment at the bottom, is this a bug?

Not really - we do run a post processing step when publishing the code and it adds a standard copyright banner at the bottom.  The processor, at this time, isn't able to support multiple comment formats.  Having it in a non-VB format doesn't affect the debugging functionality in any way.

12) I got to a source file and all that downloaded was a blank file?

This is something we've seen intermittently but have not been able to diagnose.  If you see this, usually the workaround is to just restart VS, which will force the file to reload.  If you observe this behavior, please use the "Email" link on the left to email me the name of the file that failed and about what time the failure occurred.

13) What happens if I download a Hotfix or a Service Pack?  Will I be able to get source for that?

We've built a system that allows us to publish any number of versions of source and symbols for a given product.  We haven't made any firm decisions on how often we'll publish source and are open to customer feedback on those issues.  For example, it's clear that publishing source for each Service Pack makes sense, but it's unclear if we'll be able to do so for each Hotfix.  Again, we're looking forward to feedback here.

In the meantime, also note that symbol files may no longer match a module if a framework DLL has been updated via a Hotfix. In those cases, the modules window will indicate that the symbols could not be downloaded for that module. Assuming a new symbol file was published, it can be downloaded and cached using “Load Symbols”.

14) Can I point a web browser at the symbols URL and download the symbols directly?

No, you'll get an HTTP 400 (Bad Request) response.

SUPPORT

If you have any other questions, please visit our new MSDN Forum on the topic: Reference Source Server Discussion.

Thanks!

Posted by sburke | 289 Comments
Filed under:

Setting up a Windows Home Server

I've been on a Windows Home Server kick for a while. 

I originally set it up on an old laptop I got - which wasn't doing what I wanted so I started from scratch and ordered a dual-core Dell notebook (Vostro 1400) it showed up last week.  This kind of turned into a saga, hopefully I've documented some of the issues here.  Originally I bought a low-end Toshiba from BestBuy to do this.  Bad move - it actually wouldn't run XP or WHS.  Back it went.

Here's what I want a single machine to do in my house:

1) Be on all the time

2) Back up all the other machines when they're on (I want them sleeping most of the time)

3) Host a Internet-facing web site

4) Host a bunch of shares for my photos, music, data, etc.

5) Be a print server

6) Be able to run the Zune client so that I can use the Zune wireless sync to pick up podcasts, etc.

7) Have low power consumption.

8) Run Windows Media Connect so I can stream DRM'd music to my ROKU SoundBridge's over the wireless.

So that's my requirements list, in no particular order. 

In fact, power consumption is a biggie for me.   The key to this is to use a laptop instead of a desktop machine for this.  It's ideal for a home server machine.  Built in UPS - and a very long life one at that.  Built in keyboard, monitor, and mouse.  Lots of USB ports.  Most desktop machines, when sitting idle, pull about 125w.  The Dell laptop I just got, with the shut, pulls 19w.  Huge difference. 

When did my first run at this, I got most of the way there.  WHS but itself does everything but except (6) above more on this later.  And I don't really like the same system doing (3) and (4) together.  That's just asking for trouble.  Having the website running in its own machine is a nice extra layer of security.

(8) Means that the same system has to be running the client that downloads any DRM'd music (e.g. subscriptions, which is number 6), which is an added wrinkle.  I can't download it somewhere else then copy it to the server.

Also, the Zune client doesn't run on WHS.  I want this thing running all the time so my Zune4 can sync with it. 

What I realized is that I could install Virtual Server 2005 R2 on the machine and then mount a Windows Vista image on there to handle some of this other stuff.  This was a cool idea because it so nicely handled a bunch of scenarios.  For example,  if I have the VPC running off it's own USB drive (which is a good idea for performance reasons), if I need to move my web site onto another machine, I can do so in minutes.  Virtual Server and Virtual PC speak the same language, so any machine will work provided I take the 2 minutes to install Virtual PC on it.  By giving the VPC a static IP address, the router will take care of the rest.

I got the laptop ordered and set about building up the VPC image on my laptop.  This is also nice to be able to do - rather than needing the server, I could chip away at setting up the VPC image to my hearts content and then just copying it over when I'm ready.  In case you haven't noticed I'm getting pretty hot on Virtualization in general and have been finding all kinds of ways to use it in my day-to-day life.  But that's another story.

So last week I get the machine delivered.  I've got the VPC ready, so I think it'll be an easy four step process:

1) Install WHS

2) Setup backups

3) Mount VPC

4) Celebrate the glory

Well, it's not going to be that easy.

First problem: Upon setup reboot (after it copies the files), WHS install Blue Screens

Good times:

STOP: 0x 0000007B (0x F789EA94, 0x C0000034, 0x 00000000, 0x 00000000)

I look everywhere trying to figure this out, with no luck.  I learned this was "INACCESSIBLE_BOOT_DEVICE" but the blue screen didn't say that.  But I gleaned from that that there wasn't a driver for the machine's drive built into WHS setup.  I tried locating a driver and then finally was looking in the BIOS when I discovered the issue: by default, the hard disk access is set to AHCI, which setup doesn't understand.  In the BIOS I tried changing this to ATA, and presto, it worked.   I haven't tried changing it back, and probably won't.

The rest of setup went pretty smoothly.  I got WHS installed and running, copied the data from the old server, and installed Virtual Server 2005 R2 SP1...so far so good.

I got the VPC image copied over and loaded up on the laptop.  I gave it 1100 MB of RAM to work with.  At this point, I pointed Windows Media Player at the music share on the server box and let it catalog all that music for sharing.

Just to recap, now I've got a VPC image running IIS7 and a website, and also serving music that resides on a different machine.  So this VPC is pretty low investment, its just a broker.  It's also running SQL Server Express to manage the website.   This all works when running off my laptop.  I think I'm good to go.

Second problem: Getting the VPC to show up as a Windows Media device

So I make sure I can connect to this thing and that the performance is good enough.  I just have it share out the default music that comes with Windows Vista.  But I can't get the device to be visible to the ROKUs or to the other PCs in the house.  I see the VPC as a normal machine, no problem.  And the web server is running fine (that was easy...), but no media sharing uPnP icon is showing up.

This one took a bunch of debugging and was pretty painful really.  I tried everything with permissions and firewalls and configuration I could find, without luck.

Once thing I did notice was that when I'd try to restart the "Windows Media Network Sharing Service" (Windows Media Connect), it would hang at "Stopping" and I'd have to kill the process.  Hmmm...

Finally after a lot of searching I came across this post which suggested deleting the DRM folder that holds the DRM keys.  Of course you'd need to reacquire any licenses, but at this point that was fine.  This folder is under your user profile (<profile>\AppData\Local\Microsoft\DRM), and you can rename it if you want to be able to put it back.  I just killed all the WMP*.exe processes, deleted the folder, then restarted the VPC.

Presto.

I'm not sure how this got so gummed up, and it really, really, sucks that there was NO clue for me (event log, etc) that this was the problem.  Not cool, I'll need to find someone to email on this one.  Even though it was in a VPC, this wasn't a freaky scenario.  I'm also not sure what I did to break this between setting the VPC up on my laptop and moving it over, but I think it had something to do with WMP setting up the library.  Just a guess.

Third Problem: Getting Media Sharing to Share Network Content

At this point sharing is working great for local content.  Not so much for the stuff that I'd put into the library from the server.  With some digging I came across this Windows Media Sharing FAQ, that says "You can use Windows Media Player 11 to share media files that are located on other computers on your home network. However, additional steps are required so that these remote files will play on the devices you are sharing them with.".  Okay, cool.

Unfortunately this FAQ is super confusing about exactly what you have to do.  It lists five steps.  But you need to do the FIRST ONE and just ONE of the next FOUR.  That's not clear.  And all of these steps should happen on the client (VPC in this case) machine.  That's also confusing in the FAQ.

First, set the registry key to enable slurping off a UNC share:

image

The next four options boil down to two things:

1) Set the Windows Media Sharing service to run under the account that has permissions to the share, or

2) Grant anonymous access to the share so rights aren't an issue.

I didn't want to do (2) since it's not secure and with the new security stuff in Windows, it seems to not work reliably to make a folder REALLY public anyway.  So I went with (1).  Basically you just tell the service to run under the context of a given account (in this case an account that WHS knows about), and then give that same account some special registry permissions.  I'm not sure what this is about, but it's local to the WMP nodes in HKLM, so whatever. 

So in the FAQ, do the first bullet and the second one in the steps list.

image

image

After setting all of this up, all the music started showing up for the ROKUs and streaming well.  Very nice, perf seems to be fine, etc.  I've gotten some occasional drop outs, so I'll need to debug that futher.   But usually it works fine.

Fourth Problem: Getting the VPC to Start Automatically

When the WHS boots or restarts (e.g. Windows Update causes this from time to time), I want the VPC to automatically start.  Since it's running a web site, that's kind of important, yeah?

There is an option under Virtual Machine / Edit Configuration in the Virtual PC. 

image

It's called "General Properties" and it has an option for starting the Virtual Machine on machine startup.  But it's disabled!

image

This took quite a bit of futzing to figure out.  Turns out it's lame - you need to shut down the virtual machine (not save state, actually select Turn Off) and then this option is enabled.  Sigh...after you know this the error message makes sense.  Maybe I'm just stupid.

Fifth Problem: Autologin to the VPC

Okay, this is an easy one.  But still.  Since I need to launch the Zune Client for wireless sync, which doesn't run as a service (hint, hint Zune team), I need the VPC to log into a session by default. This is easy via the Autologon tool.  This encrypts your password in the registry and automatically logs on the account.  Put the Zune Client in the Startup Programs group, presto, it's done.

image

Was all that worth it?

Now that it's all working, this is a great setup.  No real problems and everything is good from perf to reliability.  It's pretty much hands off now and I've accomplished all of the goals I've set above.   Definitely far too much work getting this set up, and I'm sure I'll keep tweaking on it but right now, everything is working great.

 

image

Posted by sburke | 8 Comments

VS2008/.NET 3.5 Ships...sooooo...where's the source?

By now I'm sure you've heard that Visual Studio 2008 & the .NET Framework 3.5 shipped on Monday.   Good news.

Many of you who have been able to download the release are probably anxious to be able to start stepping through the .NET Reference Source that we announced back in September.  The short answer is we're not quite ready yet. But soon, very soon.

The back-end for the system that manages and hosts the source had just recently been finished up, and being Thanksgiving week, we decided against turning it on then having everyone go out of the office for the rest of the week.  If something went wrong, it would be very difficult to manage without the right people on site.   That's the main factor.  We also identified a performance issue very late VS2008 release process as we did broader testing of the end-to-end system.  The issue certainly wasn't worth disrupting the VS2008 ship process, so we're also working on releasing a hotfix for that issue shortly as well (it's currently in testing).

Everything else is ready to go, so we're looking to turn this on very soon (we're talking weeks here) so you guys can dig into it.  I think you'll all be really happy with how it all hangs together!

Posted by sburke | 6 Comments

Growing the family

It's a boy!  No, no, no not that. 

If you're a SubSonic fan, you may have seen Rob Conery's blog today with the announcement that he's accepted an offer to join my team.  I've been working with Rob on some projects over the past few months and it's been nothing but goodness.  I'm super excited about this.

ScottGu and I have been talking about ways to continue to build community momentum around our tools and platform, and a project like SubSonic is just a great, great example of that.  Given our existing relationship with Rob, plus the new ASP.NET MVC stuff, formalizing something here made a lot of sense.  Moreover, this is a very natural addition to the other transparency stuff that I've been driving over the past few years - the open source AJAX Control Toolkit, the .NET Reference Source project, and another external open source project.

To be clear Microsoft did NOT buy, annex, co-opt, or otherwise take over SubSonic.  SubSonic is still what it is, and a big part of Rob's job is going to be to make sure it continues to grow and provide a great platform for building data-centric apps.  Rob still has complete authority over SubSonic, it's his baby.  He'll also be pitching in to help find ways to deliver value for ASP.NET in general.  He's got great ideas already, we're fortunate to harness his passion and instincts in these areas.  Plus he can tag-team with Phil Haack, so look out world.

Head on over to Rob's blog for more det