OpenID User Control in Silverlight – Part 1 UI Design

More and more I see sites supporting OpenID as Authentication mechanism. I’m for example a user of sites like: I need to read this, Get Satisfaction and Google Login more or less.

To support my own family I set up OpenID on my own domain, http://openid.mymonster.nl/ hosted by MyOpenID. This just works like a charm. For the purpose of this article I created a test identity at my MyOpenID. I suggest everyone doing development for openid connectivity to create a test identity, I don’t want to mess with my real OpenID identity. This is part one of a three part series on the creation of an OpenID User Control. I initially created the control for use in my own application and have submitted it to the Silverlight Control Builder Contest of 2009.

UI Design

One of the first things I was thinking about, was my design capacities. I came up with the following design, before signing in.

OpenID User Control before signing in

After you have signed in.

OpenID User Control after signing in

Yes I know, it’s very straightforward, and all the designers in this world could have thought about a better alternative. That’s why I thought this control to require the ability to template it. There are some articles on the web about templating Silverlight User Controls, this one helped me a lot.

To support Templating for a user control it needs to inherit ContentControl

Visually I identified two states:

- Unauthenticated – This state is active when the user hasn’t signed in yet.

- Authenticated – This state is active when the user has successfully signed in.

If the sign in was successful the control will move to the authenticated state. These Visual States can be used in the Xaml part of the user control, which we will do later on.

As you can see in the above pictures we can think about three essential parts in this control.

- LoginButton, typeof(Button)

- IdentityInput, typeof(TextBox)

- IdentitySuccessLabel, typeof(TextBlock)

When you combine just these parts the C# file will look like this. (Please note, some parts are left out for the clear picture).

 

[TemplatePart(Name = LoginButton, Type = typeof(Button))]
[TemplatePart(Name = IdentityInput, Type = typeof(TextBox))]
[TemplatePart(Name = IdentitySuccessLabel, Type = typeof(TextBlock))]
[TemplateVisualState(Name = VisualStates.Unauthenticated, GroupName = VisualStates.CommonStates)]
[TemplateVisualState(Name = VisualStates.Authenticated, GroupName = VisualStates.CommonStates)]
public class OpenIdLoginControl : ContentControl
{
    private const string IdentityInput = "IdentityInput";
    private const string IdentitySuccessLabel = "IdentitySuccessLabel";
    private const string LoginButton = "LoginButton";

    private TextBox m_identityInput;
    private TextBlock m_identitySuccessLabel;
    private Button m_loginButton;
    
    public OpenIdLoginControl()
    {
        DefaultStyleKey = typeof(OpenIdLoginControl);
    }

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        m_identityInput = (TextBox)GetTemplateChild(IdentityInput);
        m_loginButton = (Button)GetTemplateChild(LoginButton);
        m_identitySuccessLabel = (TextBlock)GetTemplateChild(IdentitySuccessLabel);
    }

    #region Nested type: VisualStates

    private static class VisualStates
    {
        internal const string Authenticated = "Authenticated";
        internal const string CommonStates = "CommonStates";
        internal const string Unauthenticated = "Unauthenticated";
    }

    #endregion
}

 

image To give this control a default style there needs to be a Themes directory and a xaml-file called Generic.xaml inside the library which will contain this control. The Generic.xaml file is a ResourceDictionary, and in this case we add a style for the OpenIdLoginControl. The xaml file kind of looks like the following (I removed the VisualStateManager parts for the VisualStates).

<ResourceDictionary 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"
  xmlns:openid="clr-namespace:MM.OpenId.Controls">
    <Style TargetType="openid:OpenIdLoginControl">
        <Setter Property="Width" Value="330" />
        <Setter Property="Height" Value="50" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="openid:OpenIdLoginControl">
                    <Border BorderBrush="Black" CornerRadius="4" BorderThickness="1">
                        <Grid x:Name="LayoutRoot" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
                            <Grid.RowDefinitions>
                                <RowDefinition />
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="auto" />
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition Width="auto"/>
                            </Grid.ColumnDefinitions>
                            <Image Grid.Column="0" Grid.Row="0" Source="/MM.OpenId.Controls;component/openid-icon.png" Width="30" Height="30" Margin="8"/>
                            <TextBlock Grid.Column="1" Grid.Row="0" x:Name="IdentitySuccessLabel" VerticalAlignment="Center" Margin="8" HorizontalAlignment="Center" />
                            <TextBox Grid.Column="1" Grid.Row="0" x:Name="IdentityInput" Text="http://openid.mymonster.nl/demo" HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="8" />
                            <Button Grid.Column="2" Grid.Row="0" x:Name="LoginButton" Content="Sign In" Margin="8" />
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

Because this is just the default template, you can change this to adjust it to your own application.

Because the extreme size of this article I’ve split this article into multiple articles. I will provide the full source at the end of the last article. You can expect at least the following two parts:

- OpenID Integration

- Integration in your own application

Ps. This article is cross posted on: Mark Monster’s blog and Silverlight Help.

Tracking Silverlight (1, 2 and 3) support in Google Analytics

Half a year ago I blogged about tracking Silverlight support in Google Analytics. I’ve had a lot of reactions on how to track Silverlight support. In my original article I made use of virtual page views to track Silverlight support. But there are different options available to track Silverlight support.

Tracking through Virtual Page Views

When you make use of Virtual Page Views to track Silverlight support you will get inconsistencies in your Page Views. You will get twice as much Page Views as there really are, not the best solution I think.

Tracking through Events

We can also track information in Google Analytics using Events. But it’s difficult to associate this information directly to the amount of visitors. I think using Events for tracking Silverlight support is the second best option we have.

Tracking through User Defined Value

We can also track information using the User Defined Value. This value is directly associated to the visitor. So even if your visitor takes a look at 10 pages, it will only track this value once. But the difficult thing with Google Analytics is the amount of User Defined Values we can have. It’s exactly one. So if you’re already using this User Defined Value for an other purpose, you will have to take advantage of the other options, like Event Tracking or Virtual Page View Tracking.

My preferred way of Tracking Silverlight support is using User Defined Values. If you’re already using the User Defined Value, your best option for tracking is Events.

Implementing tracking using the User Defined Value

First of all, you’ll need the Silverlight.js file from MSDN Code. This file hasn’t changed for the release of Silverlight 3, so if you’ve already got this file, you don’t have to update it.

Reference Silverlight.js:

<script type="text/javascript" src="**/Silverlight.js"></script> 

You probably already have the Javascript code for Referencing Google Analytics, it’s like this.

<script type="text/javascript"> 
	var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
	document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>

Let’s write a Javascript function to return the current Silverlight version. Although there’s backwards compatibility I only want to know the highest version supported.

function getSilverlightVersion() {
	var version = '';
	var container = null;

	try {
		var control = null;
		if (window.ActiveXObject) {
			control = new ActiveXObject('AgControl.AgControl');
		}
		else {
			if (navigator.plugins['Silverlight Plug-In']) {
				container = document.createElement('div');
				document.body.appendChild(container);
				container.innerHTML= '<embed type="application/x-silverlight" src="data:," />';
				control = container.childNodes[0];
			}
		}
		if (control) {
			if (control.isVersionSupported('3.0')) { version = 'Silverlight/3.0'; }
			else if (control.isVersionSupported('2.0')) { version = 'Silverlight/2.0'; }
			else if (control.isVersionSupported('1.0')) { version = 'Silverlight/1.0'; }
		}
	}
	catch (e) { }

	if (container) {
		document.body.removeChild(container);
	}
	return version;
}

Let’s combine this function with the tracking of the value returned in the User Defined Value.

var pageTracker = _gat._getTracker("UA-xxxx-x");
$(function() {
	pageTracker._trackPageview();
	var version = getSilverlightVersion();
	if (version) { pageTracker._setVar(version); }
});

Because I’m using JQuery on my homepage as well, I put this tracking in the ready of page loading.

 

What does it look like in Google Analytics?

Let’s take a look at the statistics for my website containing the User Defined Value since I added support for Silverlight 3 tracking. It’s interesting to see that already a lot of people are using Silverlight 3.

Silverlight usage stats

Ps. This article is cross posted on: Mark Monster’s blog and Silverlight Help.

Silverlight 3 – Did we get support for Credentials?

A few months ago, I investigated Silverlight 3 Beta on support for Credentials.

There are two classes that give a little bit of notion that they are supporting Credentials. Both WebClient and WebRequest have a property Credentials of type ICredentials. Let’s try them one by one.

WebRequest credentials support?

First we need to have some code to request the content behind an url and assign a set of credentials to the request.

WebRequest request = HttpWebRequest.Create("http://mark.mymonster.nl");
request.Credentials = new NetworkCredential("username", "password");

Very straightforward code which you can write for the full CLR as well. This code doesn’t even contain the execution of the request.

NotImplementedException when using Credentials on WebRequest

Yes it fails. If you try to set the de Credentials you will get a NotImplementedException. Sad, so we have not support for credentials in the WebRequest.

WebRequest credentials support outcome: Negative

WebClient credentials support?

Next class with a Credentials property, WebClient. First let’s write some code.

var client = new WebClient
                 {
                     Credentials =
                         new NetworkCredential("username", "password")
                 };
client.DownloadStringCompleted += client_DownloadStringCompleted;
client.DownloadStringAsync(new Uri("http://mark.mymonster.nl"));

Of course if anything goes wrong we can see it in the DownloadStringCompleted. From QuickWatch, we can see what happened.

NotImplementedException when using Credentials on WebClient

Hmm, I could have guessed that WebClient internally makes use of WebRequest. So also WebClient doesn’t support credentials.

WebClient credentials support outcome: Negative

Hmm, last option we have: Adding the Authorization header manually

In the past I also tried to add the Authorization header manually. I won’t go into the details but, the implementation of the Credentials property would mean that some data would be put into the Authorization header.

ArgumentException when trying to apply an Authorization Header

Sad, this still doesn’t work. The ‘Authorization’ header cannot be modified directly.

Manually adding Authorization header outcome: Negative

Conclusion

Sadly both WebClient and WebRequest don’t support Credentials yet. I would have thought it did support, basically because they supplied a property called Credentials that wasn’t in the Silverlight 2 release. I’m just wondering if they just didn’t get the implementation in place on time for the RTW of Silverlight 3.

Trying to do it manually isn’t supported as well. I’m just wondering why do we have a property Credentials on those objects? Would be nice to get some response from the Silverlight team on this one.

Ps. This article is cross posted on: Mark Monster’s blog and Silverlight Help.

Silverlight 3 talking to a RIA Service, everything hosted on Windows Azure

This is my first tryout of Windows Azure. I wanted to know if it’s possible to run the Preview of RIA Service on the CTP of Windows Azure.

First of all you’ll need a lot of different components, and if you want to host your solution in the cloud you’ll also need to register at Windows Azure.

- Register first: http://www.microsoft.com/azure/register.mspx

I assume you’ve already a machine running Visual Studio 2008 SP1. But even then you’ll need to download and install the following components if you haven’t done yet.

- Silverlight 3 Beta Tools for Visual Studio: http://www.microsoft.com/downloads/details.aspx?FamilyId=11dc7151-dbd6-4e39-878f-5081863cbb5d&displaylang=en

- .NET RIA Service May 2009 Preview: http://www.microsoft.com/downloads/details.aspx?FamilyID=76bb3a07-3846-4564-b0c3-27972bcaabce&displaylang=en

- Windows Azure Tools for Visual Studio May 2009 CTP: http://www.microsoft.com/downloads/details.aspx?FamilyID=11b451c4-7a7b-4537-a769-e1d157bad8c6&displaylang=en

 

Everything installed? Let’s start with the general setup

I first created an empty Solution, I always do this, because this is the only to have complete control over the naming. After that I added a Web Cloud Service, just like the screen below.

Add a Web Cloud Service

My solution still basic, no coding done yet, looks like this:

Solution view with Web Project and Web Cloud Project

Add a Silverlight Application project, and ensure that this Silverlight Application is hosted in the Webproject created in the previous step. Normally we would create a Silverlight Business Application, but we are going to associate RIA manually to this Silverlight Application.

New Silverlight application dialog

Let’s add a reference to the RIA library that’s needed to run this solution. You can find this library in: C:\Program Files\Microsoft SDKs\RIA Services\v1.0\Libraries\Silverlight\ (of course this can be different, depending on your installation location).

- System.Windows.RIA

There’s also a different library containing User Interface parts, we aren’t using it in this example but this is:

- System.Windows.Ria.Controls

Enable RIA on this Web Project

Before we start our coding, let’s configure the web project to enable RIA Services. I’ve explained it before, but for Windows Azure it’s a little bit different.

Add references to the following dll’s:

- System.ComponentModel.DataAnnotations

- System.Web.DomainServices

- System.Web.Ria

We still need to add the Http Handler for RIA Services. In my previous explanation I only explained how to do this for older versions of IIS. But Windows Azure is running on IIS 7. So the handler needs to be added to the handlers section in the system.webserver element.

<add name="DataService" verb="GET,POST" path="DataService.axd" type="System.Web.Ria.DataServiceFactory, System.Web.Ria, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />

 

Enable RIA on the Web Cloud Project

To enable RIA on the Web Cloud Project you have to change the ServiceDefinition.csdef file. Set the enableNativeCodeExecution to true.

<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="MM.SilverToGold.Web.Cloud" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
  <WebRole name="WebRole" enableNativeCodeExecution="true">
    <InputEndpoints>
      <!-- Must use port 80 for http and port 443 for https when running in the cloud -->
      <InputEndpoint name="HttpIn" protocol="http" port="80" />
    </InputEndpoints>
  </WebRole>
</ServiceDefinition>

 

Coding

Now it’s time for coding, though I won’t explain much about the code. Please read my other RIA articles to get coding for RIA Service in your fingers.

First the RIA Service part, a very simple HelloWorldDomainService, just add the following class to your Web project.

using System.Web.DomainServices;
using System.Web.Ria;

namespace MM.HelloWorld.Web
{
	[EnableClientAccess]
	public class HelloWorldDomainService : DomainService
	{
		[ServiceOperation]
		public string HelloWorld(string name)
		{
			return string.Format("Hello {0}, from RIA Services running in the Cloud.", name);
		}
	}
}

When you now build the solution the Silverlight part will get generated.

Show the RIA Services generated file 

Coding the Silverlight part

We now have completed the RIA Services part, let’s do the Silverlight coding. First some UI, containing a button, a input field and a result field. I also added a Click event to the button.

<UserControl x:Class="MM.HelloWorld.Sui.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="White">
        <StackPanel>
            <StackPanel Height="45" Width="Auto" Orientation="Horizontal">
                <TextBlock FontSize="20" VerticalAlignment="Center" Width="201" Text="What's your name:"/>
                <TextBox x:Name="NameTextBox" Height="25" Width="192" Background="#FFFFFFFF" BorderThickness="2,2,2,2" FontSize="14"/>
            </StackPanel>
            <Button Content="Hello?" Click="Button_Click" Height="33" FontSize="20" Margin="5,5,5,5"/>
            <TextBlock x:Name="WorldResponse"/>
        </StackPanel>
    </Grid>
</UserControl>

Alright, in the Click event we want to call the RIA Service, and let’s hope the RIA Service running in the cloud answers us.

private void Button_Click(object sender, RoutedEventArgs e)
{
    var helloWorldDomainContext = new HelloWorldDomainContext();
    helloWorldDomainContext.HelloWorldCompleted +=
        HelloWorldDomainContextHelloWorldCompleted;
    helloWorldDomainContext.HelloWorld(NameTextBox.Text);
}

The code for the HelloWorldCompleted event is very simple, it’s just setting the text of a TextBlock. Please note the event automatically get’s you back to the UI-Thread, no manually checking for UI-Thread required.

private void HelloWorldDomainContextHelloWorldCompleted(object sender, InvokeEventArgs e)
{
    if(e.ReturnValue!=null)
        WorldResponse.Text = e.ReturnValue.ToString();
}

When you test this locally, it will run, but the interesting part of course: Will it run on Windows Azure?

Let it run on Windows Azure

To get your solution to Windows Azure you will first have to right-click the cloud-project, and choose for Publish. This will open a Windows Explorer window showing the files that are ready for publishing. In this example it looks like this.

image

We’ll now logon to the Windows Azure portal. In here you have to create a Hosted Services project. I’ve already done this before, but there are articles on the web about creating a Hosted Services project on Windows Azure. To be honest, you can even do this without an article. My portal looks like this.

Windows Azure Developer Portal

Let’s choose the Hosted Services project, this project is called “Monster Cloud” in my Azure environment. But you can have your own name. The next page looks like this.

Windows Azure Developer Portal - Hosted Services project

Let’s click the Deploy button, and see what happens.

Windows Azure Developer Portal - Deploy a new package screen

Ah we need to provide a few things. An application package, we have this already, it’s in our publish folder that was created for us: MM.HelloWorld.Web.Cloud.cspkg.

Let’s browse for this file, and also pick the Configuration Settings file, this is: ServiceConfiguration.cscfg.

All that’s left is providing a label name, and clicking the Deploy button. My screen looked like this before clicking Deploy.

Windows Azure Developer Portal - Deploy a new package screen, filled

When clicking deploy a lot of things are being done behind the scenes. First of all the files are uploaded, but also a kind of VM get’s created for us, this takes some time, might even take 5 minutes or longer. So you definitely have to have a bit of patience. After the deployment was complete my screen looked like this.

Windows Azure Developer Portal - Run the Azure package

Let’s click the Run button.

So let’s try the url that’s created for our staging environment. For a brief period of time, you can take a look at my staging environment.

It’s working as expected. We now have a Silverlight 3 application communicating with RIA Services, and everything running in the cloud.

Testing the Silverlight 3 communicating with RIA Services, all running on the cloud.

You can download the Source Files. If you have any questions or remarks please let me know.

Ps. This article is cross posted on: Mark Monster’s blog and Silverlight Help.