Accessing the ASP.NET Authentication, Profile and Role Service in Silverlight

In ASP.NET 2.0, we introduced a very powerful set of application services in ASP.NET (Membership, Roles and profile).  In 3.5 we created a client library for accessing them from Ajax and .NET Clients and exposed them via WCF web services.    For more information on the base level ASP.NET appservices that this walk through is based on, please see Stefan Schackow excellent book Professional ASP.NET 2.0 Security, Membership, and Role Management.

In this tutorial I will walk you through how to access the WCF application services from a directly from the Silverlight client.  This works super well if you have a site that is already using the ASP.NET application services and you just need to access them from a Silverlight client.    (Special thanks to Helen for a good chunk of this implantation)

Here is what I plan to show:

1. Login\Logout
2. Save personalization settings
3. Enable custom UI based on a user's role (for example, manager or employee)
4. A custom log-in control to make the UI a bit cleaner

image

You can download the completed sample solution

 

 

Part I: Login\Logout

In VS, do File\New select the Silverlight solution.  Let's call it "ApplicationServicesDemo".

image

We will need both the client side Silverlight project and the ASP.NET serverside project. 

image

 

Let's configure our system with the test users.  To do this we will use the ASP.NET Configuration Manager.  In VS, under the Website menu, select "ASP.NET Configuration". Use this application to add a couple of users.  I created two employees:

ID:manager
password:manager!
and
ID:employee
password:employee!

image

 

To expose the ASP.NET Authentication system, let's add a new WCF service.  Because we are just going to point this at the default one that ships with ASP.NET, we don't need any code behind, so the easiest thing to do is to add a new Text File.  In the ASP.NET website, Add New Item, select Text File  and call it "AuthenticationService.svc"

image

Add this one line as the contents of the file.  This wires it up to the implementation that ships as part of ASP.NET.

<%@ ServiceHost Language="C#" Service="System.Web.ApplicationServices.AuthenticationService" %>


Now in Web.config, we need to add the WCF magic to turn the service on.

  <system.serviceModel>
    <services>
      <!-- this enables the WCF AuthenticationService endpoint -->
      <service name="System.Web.ApplicationServices.AuthenticationService"
               behaviorConfiguration="AuthenticationServiceTypeBehaviors">
        <endpoint contract="System.Web.ApplicationServices.AuthenticationService"
                  binding="basicHttpBinding" bindingConfiguration="userHttp"
                  bindingNamespace="http://asp.net/ApplicationServices/v200"/>
      </service>

    </services>
    <bindings>
      <basicHttpBinding>
        <binding name="userHttp">
          <!-- this is for demo only. Https/Transport security is recommended -->
          <security mode="None"/>
        </binding>
      </basicHttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="AuthenticationServiceTypeBehaviors">
          <serviceMetadata httpGetEnabled="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <!-- this is needed since this service is only supported with HTTP protocol -->
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
  </system.serviceModel>

Now, still in Web.config, we need to enable forms authentication.  Under the <system.web> change the authentication mode from "Windows" to "Forms".

<authentication mode="Forms" />

 

One last change to web.config, we need to enable authentication to be exposed via the web service.This is done by adding a System.Web.Extensions section.

  <system.web.extensions>
    <scripting>
      <webServices>
        <authenticationService enabled="true" requireSSL="false"/>
      </webServices>
    </scripting>
  </system.web.extensions>

 


Now, to consume this authentication service in Silverlight, let's open the page.xaml file and add some initial UI. Just buttons to log "employee" and "manager"  in and a textblock to show some status. 

    <Grid x:Name="LayoutRoot" Background="White">
        <StackPanel>
            <Button x:Name="employeeLogIn" 
                    Width="100" Height="50" 
                    Content="Log In Employee" 
                    Click="employeeLogIn_Click"></Button>
            <Button x:Name="managerLogIn" 
                    Width="100" Height="50" 
                    Content="Log In Manager" 
                    Click="managerLogIn_Click"></Button>
            <TextBlock x:Name="statusText"></TextBlock>
        </StackPanel>
    </Grid>


Now, let's add a reference to the service we just created

Right click on the Silverlight project and select Add Service Reference

image

Click Discover and set the namespace to "AuthenticationService"

image

If you get an error at this point, it is likely something wrong with your AuthenticationService.svc or the web config, go back and double check those. 

 

Now, let's write a little code to call that service to log us in.  First add the right using statement

using ApplicationServicesDemo.AuthenticationServices;

Then, in employeeLogIn_Click method write the code to call the service to log the employee in.  For now, we will hard code the name in password, but by the end we will be prompting the user to get this data.

First we create a the web services client class, then we call the login method asynchronously.  Remember all network calls in Silverlight are async, otherwise we'd lock up the whole browser.  Finally we sign up for the callback.

private void employeeLogIn_Click(object sender, RoutedEventArgs e)
{
    AuthenticationServiceClient client = new AuthenticationServiceClient();
    client.LoginAsync("employee", "employee!", "", true, "employee");
    client.LoginCompleted += new EventHandler<LoginCompletedEventArgs>(client_LoginCompleted);
}

In the callback, for now, let's just set our status.

void client_LoginCompleted(object sender, LoginCompletedEventArgs e)
{
    if (e.Error != null) statusText.Text = e.Error.ToString();
    else statusText.Text = e.UserState + " logged In result:" + e.Result;
}

Run it!  You should see a good status.  Try changing the password and ID, and see the status change to false.  It is working.

image

Now do the same thing for manager and you are set!

 

private void managerLogIn_Click(object sender, RoutedEventArgs e)
{
    AuthenticationServiceClient client = new AuthenticationServiceClient();
    client.LoginCompleted += new EventHandler<LoginCompletedEventArgs>(client_LoginCompleted);
    client.LoginAsync("manager", "manager!", "", true, "manager");
}

Part 2: Save Personalization Settings

In this part I will show how to leverage the ASP.NET profile system to store and retrieve data tied to a particular user.  The beautiful thing about this is there is no explicit database configuration required.

First, let's go back to the ASP.NET server project and add the ProfileService.svc.  The easiest way to do this might be to copy the AuthenticationService.svc change the file name and then change the contents as show below. 

<%@ ServiceHost Language="C#" Service="System.Web.ApplicationServices.ProfileService" %>

Now, we need to enable the service via WCF configuration in web.config.  This looks just like the config for the prfofile service.  In the system.serviceModel\services section add a new node.

      <!-- this enables the WCF ProfileService endpoint -->
      <service name="System.Web.ApplicationServices.ProfileService"
               behaviorConfiguration="ProfileServiceTypeBehaviors">
        <endpoint contract="System.Web.ApplicationServices.ProfileService"
                  binding="basicHttpBinding" bindingConfiguration="userHttp"
                  bindingNamespace="http://asp.net/ApplicationServices/v200"/>
      </service>

and in the system.serviceModel\behaviors\serviceBehaviors add a new node

        <behavior name="ProfileServiceTypeBehaviors">
          <serviceMetadata httpGetEnabled="true"/>
        </behavior>


Now we need to configure the profile system.  Again, in web.config add a section listing all the profile properties.  In the System.web section, add the following node.

    <profile>
      <properties>
        <add name="Color" type="string" defaultValue="Red" />
      </properties>
    </profile>

 

Now we need to enable profile service to be accessed from the webservice. To do this, add the following node to the system.web.extensions\scripting\webservices section.

        <profileService enabled="true" 
                        readAccessProperties="Color" 
                        writeAccessProperties="Color"/>

Now, let's go to the Silverlight client and add a reference to this service.  This is the same as we did authentication service. Add Service Reference, Discover and select the profileService. 

image

Now, we need to add a little UI to the page.xaml in order to give us a way to view and edit the personalized setting.

            <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
                <TextBlock>Favorite Color: </TextBlock>
                <TextBox x:Name="colorNameBox" Width="100" Height="25"></TextBox>
                <Button x:Name="submitButton" Width="50" Height="25" 
                        Content="submit" Click="submitButton_Click">
</
Button> </StackPanel>

Now, let's extend loginComplete to retrieve all the profile properties for the user that just logged in.   Again, we need to do that asynchronously so we don't block the browser.

void client_LoginCompleted(object sender, LoginCompletedEventArgs e)
{
    if (e.Error != null) statusText.Text = e.Error.ToString();
    else
    {
        statusText.Text = e.UserState + " logged In result:" + e.Result;
        ProfileServiceClient client = new ProfileServiceClient();
        client.GetAllPropertiesForCurrentUserAsync(false);
        client.GetAllPropertiesForCurrentUserCompleted += new EventHandler<GetAllPropertiesForCurrentUserCompletedEventArgs>(client_GetAllPropertiesForCurrentUserCompleted);
    }
}

When we get the property values back, we just set the LayoutRoot to have that background.

void client_GetAllPropertiesForCurrentUserCompleted(object sender, GetAllPropertiesForCurrentUserCompletedEventArgs e)
{
    if (e.Error == null)
    {
        colorNameBox.Text = e.Result["Color"];
        ChangeBackgroundColor(e.Result["Color"]);
    }
}

private void ChangeBackgroundColor(string colorName)
{
    SolidColorBrush brush = new SolidColorBrush();
    switch (colorName.ToLower())
    {
        case "black":
            brush.Color = Colors.Black;
            break;
        case "blue":
            brush.Color = Colors.Blue;
            break;
        case "brown":
            brush.Color = Colors.Brown;
            break;
        case "green":
            brush.Color = Colors.Green;
            break;
        case "orange":
            brush.Color = Colors.Orange;
            break;
        case "purple":
            brush.Color = Colors.Purple;
            break;
        case "yellow":
            brush.Color = Colors.Yellow;
            break;
        case "red":
            brush.Color = Colors.Red;
            break;
        case "white":
        default:
            brush.Color = Colors.White;
            break;
    }
    LayoutRoot.Background = brush;
}

Finally, when the submit button is pressed we need to set the value on the server.. for completeness, I show waiting until the result comes back from the server before setting the background locally. 

private void submitButton_Click(object sender, RoutedEventArgs e)
{
    ProfileServiceClient client = new ProfileServiceClient();
    Dictionary<string, object> properites = new Dictionary<string,object>();
    properites.Add("Color",colorNameBox.Text);
    client.SetPropertiesForCurrentUserAsync(properites, false, properites);
    client.SetPropertiesForCurrentUserCompleted += new EventHandler<SetPropertiesForCurrentUserCompletedEventArgs>(client_SetPropertiesForCurrentUserCompleted);
}

void client_SetPropertiesForCurrentUserCompleted(object sender, SetPropertiesForCurrentUserCompletedEventArgs e)
{
    Dictionary<string, object> properites = e.UserState as Dictionary<string, object>;
    ChangeBackgroundColor((string)properites["Color"]);
}

 

The end result looks good!  Notice the employee and the manager can each have different values for color. 

image

image

 

Part 3. Enable Custom UI Based on a User's Role

In this section, we want to customize the UI to display differently based on the users role. 

To start off, let's go back to the websites management tool (WebSite\ASP.NET Configuration) and setup a "Management" role and add our "manager" user in that role.

image

image

Now we follow the same three step pattern as all the other services. 

First, we add the service, this time called RoleService.svc with the following contents

<%@ ServiceHost Language="C#" Service="System.Web.ApplicationServices.RoleService" %>

Then add enable this service via WCF in web.config:

      <!-- this enables the WCF RoleService endpoint -->
      <service name="System.Web.ApplicationServices.RoleService"
               behaviorConfiguration="RoleServiceTypeBehaviors">
        <endpoint contract="System.Web.ApplicationServices.RoleService"
                  binding="basicHttpBinding" bindingConfiguration="userHttp"
                  bindingNamespace="http://asp.net/ApplicationServices/v200"/>
      </service>
and
        <behavior name="RoleServiceTypeBehaviors">
          <serviceMetadata httpGetEnabled="true"/>
        </behavior>
      </serviceBehaviors>

Then we need to enable the roles service for this site.  In web.config, under system.web add:

<roleManager enabled="true"/>

Final change to web.config, we expose it via web services in web.config under System.web.extensions\scripting\webServices add:

        <roleService enabled="true"/>

 

Now, we just need to consume this on the client.  Add Service Reference\Discover, select the role service

image

Now, let's add some UI tweaks.  In this case I am going to add an image that will change based on the role of the user logged in.   So to do that, I add a image tag to the page.xaml

            <Image x:Name="roleImage" Source="notLoggedIn.jpg" Width="200"></Image>

Then, I need to go in and add to what happens with the user logs in... We need to go and see what roles they are in.  So we add the following code to client_LoginCompleted(). 

if (e.Result == true) { // if log in successful
    RoleServiceClient roleClient = new RoleServiceClient();
    roleClient.GetRolesForCurrentUserAsync();
    roleClient.GetRolesForCurrentUserCompleted += new EventHandler<GetRolesForCurrentUserCompletedEventArgs>(roleClient_GetRolesForCurrentUserCompleted);
}

and when the callback comes back, we chose the right image based on the roles of the user that logged in.

void roleClient_GetRolesForCurrentUserCompleted(object sender, GetRolesForCurrentUserCompletedEventArgs e)
{
    if (e.Result.Contains("Management"))
    {
        roleImage.Source = new BitmapImage(new Uri("bossRole.jpg", UriKind.Relative));
    }
    else
    {
        roleImage.Source = new BitmapImage(new Uri("employee.jpg", UriKind.Relative));
    }
}
 

Now you can run it and see the results!

 

Not logged in

image

Management role:

image

Logged in, but not in a management role:

image

 

Part 4. A Custom log-in control

A developer on my team built a very cool little log in control that makes it easier to log in.  So let's replace the lame test buttons we have been using with this new log-in control.

Add the LoginControl project to your solution

image

Add a project reference from the silverlight project to the LogIn controls project

image

Add the xmlns:my tag for the login control

image

Then, remove the manager login and employee login buttons and replace them with

            <my:Login x:Name="loginControl" 
                      LoginClick="loginControl_LoginClick"
                      LogoutClick="loginControl_LogoutClick"
                      ></my:Login>

The implementing of loginClick is very easy.  It uses the login_Complete we have already written above.

private void loginControl_LoginClick(object sender, RoutedEventArgs e)
{
    AuthenticationServiceClient client = new AuthenticationServiceClient();
    client.LoginCompleted += new EventHandler<LoginCompletedEventArgs>(client_LoginCompleted);
    client.LoginAsync(loginControl.UserName, loginControl.Password, "", true, loginControl.UserName);
}

Notice we also add logout for completion. 

private void Login_LogoutClick(object sender, RoutedEventArgs e)
{
    ServiceReference1.AuthenticationServiceClient client = new ServiceReference1.AuthenticationServiceClient();
    client.LogoutAsync();
    client.LogoutCompleted += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(client_LogoutCompleted);
}

void client_LogoutCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
    loginControl.IsLoggedIn = false;
    statusText.Text = "Logged out";
    ChangeBackgroundColor("white");
    roleImage.Source = new BitmapImage(new Uri("notLoggedIn.jpg", UriKind.Relative));
    colorNameBox.Text = "";
loginControl.ClearPassword(); }

 

Now we are done!  you can log in and out as any user.  have fun!

 

image

image

 

You can download the completed sample solution

Posted 03 May 08 04:40 by BradA | 21 Comments   
Check out popfly game creator

The Popfly guys are at it again.  They just launched a very cool game creator tool that lets normal people create great Silverlight based games.  The RIA development experience is very slick and I can't wait to see the games people create!

Check it out at Popfly.com and John's post.

 

 

popflybreakout

Posted 01 May 08 09:05 by BradA | 2 Comments   
Filed under
Job Openings Working on the .NET Framework Product Team

The single biggest factor for success on any project I have been apart of in the last 10 years at Microsoft is the people.  In a business like ours where intellectual capital is the biggest asset, hiring and growing people is an extremely important task. 

I have gathered some details on a few positions working on the .NET Framework product team, if you are interested  please click on the link and apply... If you know someone who would be just prefect for the job, please send them a link!

A few general tips for folks interested:

  1. Know what you want to do, but be flexible.  We typically hires for a specific role on a specific team.  The system is geared to work that, so if you have some solid ideas on what you'd like to get more information on that helps a lot.  During the interview process, if it becomes clear you'll be a better fit for a different position we will suggest that you. 
  2. Specific knowledge of .NET is not required.  By and large we don't hire for specific skills.  We hire for passion, smarts and the drive to get things done.  If you have those attributes, my feeling is that you can learn the details of .NET.   In fact, I think it is a plus if a candidate as deep experience in related technologies in the industry. 
  3. Follow your passions. The best way to succeed at Microsoft is to do something you are passion about.  That passion could be about the life changing technology you are working hard to bring out to customers, it could be people you get to work with on a day to day biases, it could be about the way we do development.  Whatever your passion is, find the position that best helps you follow it!
  4. A few details...For most jobs, we do consider international candidates, so please go ahead and apply!  For these jobs we do need you to relocate to sunny Redmond, WA.  We aren't looking for contractors or work-for-hire for any of these, we are looking for folks that want to make a career at Microsoft.

 

Program Manager Jobs

These are job openings on my team.  Read my past few "PM tips".. chances are if they resonate with you you could be a fit.   For both of these are are looking for senior folks...  people with 5+ years of industry experience and of course a passion for these spaces is very important.

Managed Extensibility Framework PM

We are looking for a someone to drive the release and design of the very recently announced Managed Extensibility Framework.  Our high level goal is to evolve the state of the art of component development.  If you know what DI, IoC and TDD\BDD are that is a good first step!  We have some big customers internally and lots of interest already externally.  Please see Krys's blog for more details and apply on line

Core ASP.NET Feature PM

ASP.NET runs a large chunk of the web today and there are some very exciting plans for what we can do to make it even better in the future.  If the day in the life of a ASP.NET PM sounds like your prefect day at work, then apply on line.

 

Software Development in Test

SDETs on our team are talented application developers and testers.  Our products (ASP.NET, ASP.NET AJAX, Silverlight 2, and Windows Forms) are focused on developer technologies and are first-class frameworks.  SDETs on our team are successful by representing the customer, participating in product design, and innovating in the test space.  SDETs are often the very first people that get to build applications with new technology that comes out.  They thrive on finding design and implementation issues early in the cycle and ensuring the customers have an excellent experience with the product. Apply on line.

Test Manager

For the ASP.NET Test Manager, we’re looking for someone who can manage a strong team of 25+ technical SDET’s to deliver the next generation web application platform.  Successful candidates are those that have experience shipping multiple products and can advance the state of the art of software testing. Apply on line

 

ASP.NET Developer

As you likely know AJAX development is becoming increasingly important.  We have some great plans for what is coming in the future of the Microsoft Ajax Library (part of ASP.NET AJAX) and we'd love to have your help in making it happen.  Of course you can work on any of the other tons of Ajax libraries out there, but only at Microsoft can you work on the Ajax framework that will be the best tooled, best server integrated, best supported and most used!   Apply on line

 

Other Positions:

Also, related my good buddy Doug Purdy has some very cool sounding positions open on his team as well:

Emacs.Net
New Languages & Compilers

 

Please apply on line for any of these positions or drop me an email if you have specific questions. 

Posted 30 April 08 09:46 by BradA | 13 Comments   
Reusable WPF Transitions Project is live!

Jared Bienz has been working on getting some cool WPF transitions code posted to codeplex.  The project, Transitionals, is now live!

Transitionals is a framework for building and using WPF transitions which provide an easy way to switch between UI views in a rich and animated way. Think of transitions for applications in the same way you think of transitions for video editing. Wipe, Cut, Dissolve, Star, Blinds and 3D Rotating Cube are all examples of transitions supported by the Transitionals framework.
The best way to get started with the Transitionals framework is to download and take a look at the TransitionalsHelp file. You can find it on the Releases tab and it includes a pretty comprehensive Getting Started guide. You can also download the binary archive which includes two sample projects. Transitionals is compiled in Visual Studio 2008 against .Net Framework 3.5.

clip_image001

Posted 29 April 08 12:33 by BradA | 2 Comments   
Filed under
Managed Extensibility Framework

Krzysztof recently announced on his blog that we have begun working on an extensibility framework for .NET..

 

We will blog more details about MEF in the upcoming months, but here are some early details (subject to changes, of course): MEF is a set of features referred in the academic community and in the industry as a Naming and Activation Service (returns an object given a “name”), Dependency Injection (DI) framework, and a Structural Type System (duck typing). These technologies (and other like System.AddIn) together are intended to enable the world of what we call Open and Dynamic Applications, i.e. make it easier and cheaper to build extensible applications and extensions.

[....]

And finally here is some code showing basic scenarios our framework supports:

Creating an Extension Point in an Application:

public class HelloWorld {

  [Import] // import declares what a component needs
  public OutputDevice Output;

  public void SayIt() {
        Output.WriteLine("Hello World");
  }
}

// Extension 
public abstract class OutputDevice {
  void WriteLine(string output){}
}

1. Creating an Extension

[Export(typeof(OutputDevice))] // export declared what a component gives
public class CustomOutput : OutputDevice {
  public void WriteLine(string output) {
    Console.WriteLine(output);
  }
}

 

2. Magic that makes composes (DIs) the application with the extensions.

var domain = new ComponentDomain();
var hello = new HelloWorld();
// of course this can be implicit
domain.AddComponent(hello); 
domain.AddComponent(new CustomOutput());
 
domain.Bind(); // bind matches the needs to gives
hello.SayIt();

We'd love to hear what you think.. please join the discussion on Kry's blog.

Posted 28 April 08 06:14 by BradA | 1 Comments   
Filed under ,
Where should the ASP.NET team release stuff?

One of the things I love about working on the ASP.NET team is that the team is very focused on getting customer feedback. One of the ways we do this we need to ship bits early and often.   While I think this is great, it does present some challenges in terms of helping customers find the right bits for their need.

Scott Galloway has been doing some thinking about this and has a good conversation over on his blog.  I encourage you to chime in!

Where should the ASP.NET team release stuff?

Posted 26 April 08 08:39 by BradA | 2 Comments   
Filed under
Silverlight FlickR Example

In this example I will demonstrate a very simple call to the FlickR REST APIs from a Silverlight client. At the end we will end up with an app that looks like this:

image

Part 1. Define some Silverlight UI

Part 2. Show the local open file dialog support

Part 3. Call the FlickR Service to find a picture

Part 4. Use IsolatedStorage to preserve some local settings across runs

Part 5. Skinning the UI

 

You are welcome to also get the completed sample, and demo files

 

Part 1. Define some Silverlight UI

You can go back and look at the post on my End to End Silverlight Application post for the getting started.  In Blend add a TextBox, and Button to the window and layout as shown. 

image

 

 

Be sure to give them some meaningful name in the properties window so we can refer to them pragmatically later.   Mine are called searchTermTextBox, and button. 

 

Drag an example image on to the window so we can have something to work with.  (You can use cow.jpg from the SilverlightFlickRDemoFiles zip)

image

Make sure you name this one as well... I used searchResultsImage

 

Part 2. Local Open File Dialog

Just to test out our layout, add support for popping open the open file dialog and work with the image client side.  This is something that you can't readily do in Ajax\HTML today.

In page.xaml Add a Click event handler

<Button x:Name="button" Width="100" Height="50" 
        Content="Go"
        Click="button_Click"

In page.xaml.cs implement the button click to call open file dialog.

    private void button_Click(object sender, RoutedEventArgs e)
    {
        OpenFileDialog ofd = new OpenFileDialog();
        ofd.Filter = "JPEG Files (*.jpg;*.jpeg)|*.jpg;*.jpeg | All Files (*.*)|*.*";
        ofd.FilterIndex = 1;

        if (ofd.ShowDialog() == DialogResult.OK) {
            Stream stream = ofd.SelectedFile.OpenRead();
            BitmapImage bi = new BitmapImage();
            bi.SetSource(stream);
            searchResultsImage.Source = bi;
            stream.Close();
        }
    }

This code will open the system open file dialog allowing the user to select a file on disk.  Then the developer is given only save access to the file (that is just the bits, not hte path to the file).  Notice how we are able to work with the image client slide. 

image

 

image

At this point you could upload the file to a server or store it locally in Isolated Storage.   But this is a little off from where we were going with searching flickr for an image..

 

Part 3. Call the FlickR Service to find a picture

Now we get into the meat of it.   We need to pass our search term into FlickRs REST API and set the image to be the results.  As the user clicks on the image, we can show the next one in the results set. 

First we need to call FlickRs REST API.  To do this you need a key, which you can get for free from FlickR...

Next we need to actually call the REST API from the Silverlight client.  To do that, let's define a helper method

void LoadPhotos(string topic)
{
    string apiKey = "<<get your own >>";
    string secret = "<<get your own >>";
    string url = String.Format("http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key={1}&text={0}",
       topic, apiKey, secret);
    WebClient flickRService = new WebClient();
    flickRService.DownloadStringCompleted += new DownloadStringCompletedEventHandler(flickRService_DownloadStringCompleted);
    flickRService.DownloadStringAsync(new Uri(url));
searchTermTextBox.Text = "Calling FlickR...";
}

Next we need to parse the results.  You can see the format of the results by looking at http://flickr.com/services/api/explore/ .  Basically, the results look like this:

<?xml version="1.0" encoding="utf-8" ?>
<rsp stat="ok">
  <photos page="1" pages="32769" perpage="100" total="3276843">
    <photo id="2436622217" owner="22956152@N04" secret="6c8293bb5c" server="2070" farm="3" title="IMG_3492_resize" ispublic="1" isfriend="0" isfamily="0" />
    <photo id="2437437876" owner="41848473@N00" secret="97a7e1a066" server="2303" farm="3" title="Eric & Dog" ispublic="1" isfriend="0" isfamily="0" />
  </photos>
</rsp>
  

So, we need to do a little Xml parsing.  Luckily this is very easy to do in Silverlight with LinqToXml support.   Just add a reference to the System.Xml.linq.dll  assembly.

image

Now let's implement flickRService_DownloadStringCompleted.  The first thing we need to do is a little error checking... This will help a lot making sure everything is right calling FlickR.

XDocument xmlPhotos = XDocument.Parse(e.Result);
if (e.Error != null ||
    xmlPhotos.Element("rsp").Attribute("stat").Value == "fail"){
    string results = e.Result;
    searchTermTextBox.Text= "Error! (" + results + ")";
    return;
}
else {
    searchTermTextBox.Text = "It worked!";
}

Now we just need to wire up the call to LoadPhotos.

private void button_Click(object sender, RoutedEventArgs e)
{
    LoadPhotos(searchTermTextBox.Text);
}

 

Run it.  If you see this, go back and check your API key.

image

When you see this, you are golden and ready for the next step!

image

Now, we need to parse the Xml results and pull out the URLs to the image.  We are going to use the magic of Linq to handle all the ugly Xml parsing.  All we need to do is to define a .NET class we want the XML elements mapped into.

public class FlickRPhoto
{
    public string Id { get; set; }
    public string Owner { get; set; }
    public string Secret { get; set; }
    public string Server { get; set; }
    public string Farm { get; set; }
    public string Title { get; set; }
}

And, let's just add another property that class which follows the FlickR URL conventions for form up a Url to the image

public string ImageUrl
{
    get
    {
        return string.Format("http://farm{0}.static.flickr.com/{1}/{2}_{3}.jpg",
            Farm,Server,Id,Secret);
    }
}

Now, we need the Linq code that maps the Xml elements into this class.

Photos = from photo in xmlPhotos.Element("rsp").Element("photos").Descendants().ToList()
         select new FlickRPhoto
         {
             Id = (string)photo.Attribute("id"),
             Owner = (string)photo.Attribute("owner"),
             Secret = (string)photo.Attribute("secret"),
             Server = (string)photo.Attribute("server"),
             Farm = (string)photo.Attribute("farm"),
             Title = (string)photo.Attribute("title"),
         };

Let's define Photos as a field on this class, so we can access it later.

IEnumerable<FlickRPhoto> Photos;

Now we just need to display the image. To do that, we just grab the first record from the result set and display it! 

FlickRPhoto p = Photos.First();
this.searchResultsImage.SetValue(Image.SourceProperty, p.ImageUrl);
searchTermTextBox.Text = p.Title;

Now that is cool, but I want to see the other photos.. A quick a dirty way to do this is to change the photo when the photo is clicked.  To do this sign up for the event handler

<Image MouseLeftButtonDown="searchResultsImage_MouseLeftButtonDown" 
             x:Name="searchResultsImage" 

 

And implement it.  I  do a little error checking up front, and use a new class field called ImageNumber to keep up with here I am.

private void searchResultsImage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    if (Photos == null) return;
    if (ImageNumber >= Photos.Count()) ImageNumber = 0;

    FlickRPhoto p = Photos.Skip(ImageNumber).First();
    this.searchResultsImage.SetValue(Image.SourceProperty, p.ImageUrl);

    ImageNumber++;

}

Now as you click on the picture, it cycles you through the result set

image

 

Part 4: Use IsolatedStorage to preserve some local settings across runs

Now, let's see about preserving some of this state across runs. 

First, let's save off the results of the textbox when the "Go" button is pressed. 

private void button_Click(object sender, RoutedEventArgs e)
{
    LoadPhotos(searchTermTextBox.Text);
    ApplicationSettings.Default["searchTerm"] = txtBox.Text;
    ApplicationSettings.Default.Save();

}

and the same idea when the image changes

private void searchResultsImage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    if (Photos == null) return;
    if (ImageNumber >= Photos.Count()) ImageNumber = 0;

    FlickRPhoto p = Photos.Skip(ImageNumber).First();
    this.searchResultsImage.SetValue(Image.SourceProperty, p.ImageUrl);
    searchTermTextBox.Text = p.Title;

    ApplicationSettings.Default["imageNumber"] = ImageNumber;
    ApplicationSettings.Default.Save();

    ImageNumber++;

}

Then when the application starts up we can pull the latest set and initialize with it. 

public Page() {
    InitializeComponent();
    if (ApplicationSettings.Default.Contains("searchTerm")){
        this.txtBox.Text = (string)ApplicationSettings.Default["searchTerm"];
        button_Click(null, null);
    }
    if (ApplicationSettings.Default.Contains("imageNumber")){
        ImageNumber = (int)ApplicationSettings.Default["imageNumber"];
    }
}

When you run it the first time, it has not saved state so it uses the default.  But run it again and notice it picks up where you left off! 

Part 5: Skin it

Now, let's give it a nice skin.  Again, I will use Corrina's Rough Skin

Just cut and paste the <ApplicationResources> section from Corrina's example into your App.Xaml

Then add the styles

<Button Style="{StaticResource buttonStyle}"
<TextBox Style="{StaticResource textBoxStyle}" 

Done!

 

image

 

You are welcome to also get the completed sample, and demo files

Posted 25 April 08 11:23 by BradA | 11 Comments   
InfoQ Interview: Framework Design Studio Released

InfoQ recently did an interview with Krzysztof Cwalina about Framework Design Studio.

Check it out:Framework Design Studio Released

Updated ASP.NET Dynamic Data Bits Posted

We just posted some updated bits and samples for ASP.NET Dynamic Data... I encourage you to go pick them up and let us know what you think...

Some cool new stuff in this release:

  1. Visual Studio Integration is much cleaner
  2. Now supports "pretty" URLs
    http://products/details/1 instead of http://products/details.aspx?id=123
  3. Full documentation
  4. Additional support for 3rd party control vendors and O/R Mappers (more details coming soon)

Scott Hunter recently did a HanselMinutes podcast that you should check out..

ScottGu did a post recently that hits the high points of Dynamic Data... 

Give them a try, we'd love to have your feedback!

http://code.msdn.microsoft.com/dynamicdata

Posted 23 April 08 10:17 by BradA | 11 Comments   
Filed under
End-to-End Data Centric Application with Silverlight 2

I have been having some fun with an end to end Silverlight 2 demo recently and I thought I'd share it here.   My goal with the demo is to show how easy it is to create an end to end Silverlight application.  In particular, my goal is to show

  1. Explain the Silverlight Project Format
  2. How to do rich layout and animation
  3. Uses line of business controls for Silverlight such as DataGrid
  4. Has a Linq based data layer on the server on the server
  5. Exposes business logic over that data via a WCF web service
  6. Consume the service on the client in Silverlight and databind to the DataGrid
  7. Store result locally to minimize round trips to the server across instantiations with IsolatedStorage
  8. Re-theme the UI to make it look more cool.
  9. Do it all in code that you could easily write on stage in about 30-45 mins
  10. Have Fun!

 

Download the completed sample (VB one is available as well)

 

Here is a screen show of what we are going to end up with..

image

 

I. Create the Silverlight Project

In VS2008 with the Silverlight 2 Tools for Silverlight installed, create a new Silverlight application

image

 

image

VS asks you to create a home web site for your Silverlight application.  This is important because we are going to implement our data access layer and web services layer in this web site. 

What VS creates for you looks like:

image

The web project contains an ASP.NET page that gives you already wired up to the Silverlight application.    Notice we use the new ASP.NET Silverlight control here to host rich silverlight content within your ASP.NET page. 

        <asp:Silverlight ID="Xaml1" runat="server" 
            Source="~/ClientBin/EndToEndSilverlightDemo.xap"
            Version="2.0" Width="100%" Height="100%" />

There is also a plain-old HTML page that you can host on any web server (Apache, etc)..  Nothing in Silverlight requires ASP.NET or even IIS on the server.

Notice the EndToEndSilverlightDemo.xap file?  This is the client side silverlight application.  The .xap file is just a zip file format.  So in fact if you just rename it to .zip, you can easily see inside. 

image

image

What we see here is the Manifest for the Silverlight application, the actual C# or VB code you write for the application and then the parts of the framework your application needs.  By Silverlight RTM, many of controls will be rolled into the core so you don't have to include them in your application.

 

 

II. Rich Layout

Let's start with some very basic Silverlight coding.  In page.xaml, let's start easy and just add a button.

image

Now, that button defaults to filling the entire grid, so let's give it a size, and some text...

image

As an aside, it is interesting to note that the property label on the button is called "Content" rather than "Text" or something like that.. the reason is because you can in fact have anything in a the button... for example a couple of images, a video, or even, well, another button!

image

Not sure why you would want to do that, but it is good to know you can!

let's leave the Xaml to look like this:

        <Button x:Name="btnOne" Width="100" Height="50" 
                Content="Push Me!"
                Click="Button_Click">
        </Button>

 

OK, enough playing around.. this is Silverlight 2, it is all about .NET code, so let's show a little of that.   Let's add a simple event handler and write some code. 

Notice how VS makes it so easy to add an event handler

image

And VS also makes it very easy for me to navigate to that event handler

image

 

In the event handler, notice I get strongly typed access to the button I just defined in Xaml.  I can also set a break point..

image

Now, hit F5 and run the solution.. notice what is happening:  the code you just typed is being compiled into a .dll, that dll is being zip'ed up in the .XAP file for deployment with any of its dependences.  It is being copied up to the server project, the ASP.NET page is being loaded, it loads the silverlight player in the browser which downloads .XAP file unzips it loads the Xaml and executes our C# code!

That is a lot for a little button!

image

Notice how clicking on it changes the next and hits our break point!  All the power of VS right here in your Silverlight application.

image

Ok, that is cool, but how about some of that richness...  Let's talk a little bit about the developer\designer collaboration in VS\Expression. 

Notice on my Xaml file, I can right click and Open in Expression Blend

image

This is where we move into design mode and actually build some cool UI...

In Blend, select the Layout Root and give it a nice gradient fill. 

image

(you can tell I am not really a designer...)

Now, let's do a little animation, to do that, add a new Story Board

image

Now simply change any properties of the button, move it around the screen to define the animations you want to happen and the different times

image

 

Save the changes, and why you pop back to VS, it tells you the Xaml has been changed... This is way better than passing bitmaps around between developers and designers.

image

Then we go back into our click event and start that story board

 

private void Button_Click(object sender, RoutedEventArgs e)
{
    btnOne.Content = "I've been pushed!";
    Storyboard1.Begin();
}

For a fun little Silverlight version of the moving button game, add an event handler for mouse enter and play the story board from there

      <Button x:Name="btnOne" Width="100" Height="50" 
                Content="Push Me!"
                Click="Button_Click"
                MouseEnter="btnOne_MouseEnter"
                RenderTransformOrigin="0.5,0.5">

 

private void btnOne_MouseEnter(object sender, MouseEventArgs e)
{
    Storyboard1.Begin();
}

 

Ok... that was fun, but let's go back into Blend now and define a little more sensible UI..

Notice the snap lines and layout help?

image

And how easy it is to define a grid layout and get docking and resize to work correctly

image

 

Now to work around a little bug in Expression, we need to add a reference to the System.Windows.Controls.Data.dll assembly where the DataGrid control lives.   You do that under Project\Add Reference.  It should be in the "C:\Program Files\Microsoft SDKs\Silverlight\v2.0\Libraries\Client" directory.

image

 

Now from the asset library it is easy to find DataGrid

image

image

 

 

III.  Defining the Data Layer

Ok, that is all fun UI stuff, but now let's get down to it.   Let's go back to the server project and add a data layer. 

First, just to get access to the database, I add northwind.mdf to the app_data director

 

Then, right click, add new item and select Ling to Sql Classes.  Notice that EF or NHibranate or whatever would work in the same way.

image

 

Then, in the Linq designer add the Products table from Northwind

image

Now, we need to make sure that the Linq model is created in a way that we can expose it over a web service.  So I need to change the Serialization Mode to Unidirectional

image

Clearly if this were more real world, at this point I would add data validation and business logic. 

 

 

IV. Define the Services Layer

I'll show creating a services layer with WCF. 

Right click, add new Item and select WCF Service

image

 

Open the services interface and define the right contract

[ServiceContract]
public interface INorthwindService
{
    [OperationContract]
    List<Product> GetProducts(string productNamePrefix);
}

 

Then open the concrete implementing and click on the smart tag on the interface and implement the interface

image

This creates the following code

public class NorthwindService : INorthwindService
{
    public List<Product> GetProducts(string productNamePrefix)
    {
        throw new NotImplementedException();
    }

}

Now I need to create the implementation  of this method which is simple with Linq.

public List<Product> GetProducts(string product