do { Engine.BlogAbout(".NET","Silverlight"); } while ( alive );  

Mark Monster

Mark Monster  

Posts Tagged ‘Silverlight’

MeXperience – Step 3 – Architecture, implementing pipes and filters

In step 2 I explained about the Architecture of MeXperience I had in mind. This article explains the implementation of the pipes and filters pattern to filter the list of experience objects. I will start to tell that my implementation is based on an article from Oren Eini.

The Filter

In MeXperience there are currently only two types of filters: by tag and by role. But I could think about others like a filter by year of experience.

The idea of the filter in the pipes and filters patterns is to have a simple operation, and a lot of combine simple operation in one pipeline make a complex operation.

My filters are also used as objects to represent an item in the TagCloud. This is my base.

public abstract class CloudItem
{
    public CloudItem()
    {
        Weight = 1;
    }

    public int Weight { get; set; }
    public string Name { get; set; }

    public abstract IEnumerable<Experience> Filter(IEnumerable<Experience> experiences);
}

Yes I know it’s abstract and there’s no filter implementation. First the signature, there’s an enumeration of experiences coming as input, and there’s an enumeration as output. Let’s see one of the implementations, the of CloudItemTag.

public class CloudItemTag : CloudItem
{
    public override IEnumerable<Experience> Filter(IEnumerable<Experience> experiences)
    {
        foreach (Experience experience in experiences)
        {
                if (experience.Tags.Where(t => t.Name == Name).Count() > 0)
                    yield return experience;
        }
    }
}

The Filter implementation is a simple loop through the experiences, and if the experience corresponds to the filter it will yield return this experience.

You can probably figure out how the CloudItemRole would look like. These filters are simple, let’s combine the pipeline of filters.

The Pipeline

Code explains more than words.

private IEnumerable<Experience> ApplyFilter(IEnumerable<CloudItem> filter)
{
    if (filter == null)
        return _experiences;
    IEnumerable<Experience> current = _experiences;
    foreach (CloudItem filterItem in filter)
    {
        current = filterItem.Filter(current);
    }
    IEnumerator<Experience> enumerator = current.GetEnumerator();
    while (enumerator.MoveNext()) ;

    return current;
}

Alright a little bit of explanation. First we start with the list of filters to apply, if the list is null we will not filter, else we will start with chaining all filters together in line 8. After everything is chained together we see all the magic happen in line 11. Until line 11 no filter has been executed, but by iterating through the experiences filter chain we will get a filtered list of experiences.

What’s to come?

There will at least be one more article on the MVVM implementation, but this is how it’s going to look like. Or look at this video.

image

Capture usage information of a Silverlight application using Google Analytics and a Blend Behavior

It’s already more than a year ago since Tim Heuer published his article on Event tracking in Silverlight. Since that time we’ve got Silverlight 3 and Silverlight 4 beta. So it’s time for a different implementation that can even be used by non-coders. This article will make use of Silverlight 4 beta, but in the end almost everything works on Silverlight 3 as well.

The idea

Create an Expression Blend Behavior that does all the tracking for me!

The implementation – Some Javascript

Blend Behaviors are extremely easy to use. Designers can use them without having knowledge on code and besides that they don’t dirty the business flow of the code. Also Behaviors can be used very easily in combination with DataTemplates and BindingExpressions.

Let’s start with the very beginning. On the html-page there needs to be some Javascript from Google Analytics to start the Analytics script.

<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>
<script type="text/javascript">
  try{
    var pageTracker = _gat._getTracker("UA-xxxxxx-x");
    pageTracker._trackPageview();
  } catch(err) {}
</script>

This is the generic Google Analytics script to start the normal tracking. Please make sure that you replace xxxxxx with your own Tracking Code you received from Google. The documentation around Tracking Events is very good. The important part is that we need to call a Javascript function from Silverlight. I have wrapped the Google Analytics Track Events call in my own Javascript function.

function trackEvent(category, action, label) {
  pageTracker._trackEvent(category, action, label);
}

But this will directly call into Google Analytics, which is very difficult to test because the data will arrive but the stats aren’t real-time. That’s why I created a small test version of this function that shows an alert.

function trackEvent(category, action, label) {
  alert(category + ", " + action + ", " + label);
}

Alright we now have done all the Javascript stuff, for now I will be using the test version of trackEvent because we’re in the development stage. Let’s continue with the implementation of the behavior.

The implementation – Behavior

There are a few different types of behavior that can be created: Behavior<T>, TriggerAction<T>, TargettedTriggerAction<T>. I won’t explain every type of behavior, but for the implementation of our Google Analytics Tracking behavior I chose fore TriggerAction<T>. I did this because of the flexibility in events which a TriggerAction can react on, besides that this specific behavior won’t change anything in the Silverlight application itself, it will just call out to a Javascript function. To start with the behavior you will first have to add a reference to System.Windows.Interactivity.dll. You can find this library in: C:\Program Files (x86)\Microsoft SDKs\Expression\Blend 3\Interactivity\Libraries\Silverlight\.

public class TrackEventAction : TriggerAction<UIElement>
{
    protected override void Invoke(object parameter)
    {
        try
        {
            HtmlPage.Window.Invoke("trackEvent", new object[] {Category, Action, Label});
        }
        catch
        {
        }
    }
}

The implementation is simple, just a call out to the Javascript function trackEvent, and three parameters. I explicitly put everything in a try catch swallow statement, because I don’t want a failure in this tracking behavior to cause problems in the application. Further on I made it of type UIelement because in that case all UI elements can Track Events. You can see that the parameters for the call to the Javascript function are properties: Category, Action and Label. I explicitly didn’t put in the code, because the code is straight forward, they are DependencyProperties. Full Code is collapsed below ;-).

public class TrackEventAction : TriggerAction<UIElement>
{
    public static readonly DependencyProperty CategoryProperty =
        DependencyProperty.Register("Category", typeof (string),
                                    typeof (TrackEventAction),
                                    new PropertyMetadata("Silverlight.Event"));

    public static readonly DependencyProperty ActionProperty =
        DependencyProperty.Register("Action", typeof (string),
                                    typeof (TrackEventAction),
                                    new PropertyMetadata("Unknown Action"));

    public static readonly DependencyProperty LabelProperty =
        DependencyProperty.Register("Label", typeof (string),
                                    typeof (TrackEventAction),
                                    new PropertyMetadata("Unknown Action fired"));

    public string Category
    {
        get { return (string) GetValue(CategoryProperty); }
        set { SetValue(CategoryProperty, value); }
    }

    public string Action
    {
        get { return (string) GetValue(ActionProperty); }
        set { SetValue(ActionProperty, value); }
    }

    public string Label
    {
        get { return (string) GetValue(LabelProperty); }
        set { SetValue(LabelProperty, value); }
    }

    protected override void Invoke(object parameter)
    {
        try
        {
            HtmlPage.Window.Invoke("trackEvent", new object[] {Category, Action, Label});
        }
        catch
        {
        }
    }
}

The implementation - Let’s use it

Like any other TriggerAction we can add the Interaction.Triggers element to some UIElement. The below example combines Binding in a DataTemplate.

<DataTemplate>
    <Button>
        <ContentControl Content="{Binding Name}"/>
        <Interactivity:Interaction.Triggers>
            <Interactivity:EventTrigger EventName="Click">
                <GoogleAnalytics:TrackEventAction Category="MeXperience.CloudFilter" Action="{Binding Name, StringFormat='Filter.Remove[\{0\}]'}" Label="{Binding Name, StringFormat='Remove filter for \{0\}'}" />
            </Interactivity:EventTrigger>
        </Interactivity:Interaction.Triggers>
    </Button>
</DataTemplate>

In this example I used the Click-event but it could have been any other event as well, just use the different event name and you’re done. If you’re making use of Silverlight 3, be aware that the StringFormat part of the Binding Expression doesn’t exist, if you remove it everything else will be working fine.

After some days you will get data in Google Analytics which will look something like this.

EventTrackingExample

MeXperience – Step 2 – Architecture and more

Besides the purpose of the application itself, I want to make sure I expand my knowledge on Silverlight. This would be especially on the architecture of Silverlight applications.

Architecture - MVVM

I’ve read quite a lot of articles on MVVM, but there weren’t many article series that were as complete as the series on RIA, MEF and MVVM by Shawn Wildermuth (1,2,3,4). I have no intend to write an article or series on MVVM because it’s not really in my fingers yet. But to know more on MVVM please read the fantastic series by Shawn. But then again my intend is to make use of MVVM for MeXperience. The idea is to introduce two ViewModels (please let me know if you’d advice a different setup for ViewModels).

1. The ExperienceFilterViewModel, which supports showing all experience-tags and the ability to form a filter.

2. The ExperienceViewModel, which has control over all experience-parts that are found in the data store and can interact independent from the filter but can have filters applied as well.

Because I chose to use the articles by Shawn as my knowledge base for MVVM, I will make use of the same components: MEF and MVVM Light Toolkit. If you have suggestions for other libraries, I’m interested, but this will be my start.

Architecture – Pipes and Filters

For the purpose of filtering the experience I want to introduce a Pipes and Filters design pattern. I know it’s not absolutely necessary but it makes sense for this purpose.

Architecture – Experience Data Store

It’s very standard to give an application a database for it’s data store. But to be honest there are many situation where a database isn’t the best choice. So in this occasion I think an xml file containing all the experience information will do. For now MeXperience will only be about the presentation of the experience. Maybe some time in the future there will be an application to edit this xml file.

User Interface Components

I’ve searched around the web (being aware of some licenses I won during Silverlight Control Builder Contest) for some User Interface components that could really help implementing the User Interface that I proposed in my prototype in the first part of my series.

MeXperience Basic ViewMeXperience Detailed View

The first piece of User Interface that I want to cover is the TagCloud. But when I search on Google, I first find a component on Codeplex which has tight integration with WCF, second result is an article from my own hand (July 2008), third is the very nice 3D-Tagcloud by Peter Gerritsen. But in the end all that looks like the best suitable component is the Silverlight Tag Cloud by Infragistics (I’m lucky to have the license).

Further on the experience table-tile-view thingy. After some investigation I’m sure, it’s called a Tile View. Both Infragistics and Telerik have such a control. But because I already found a Tag Cloud by Infragistics I will use the Infragistics controls. Hope this will be a good choice.

MeXperience – Step 1 – Idea + Prototype

Every now and then I have some ideas. That’s also the main reason why I created a Windows Mobile application to support at least the storage of my ideas to be sure that those ideas don’t get lost. Some ideas will never be more than just an idea. One of my latest ideas was the below idea. I want to implement this idea, in the time I have (very little).

MYdea: MeXperience

I want application that can show my work experience in an interactive way. I was thinking tag-cloud, tiles, details, photo.

Yes a tag-cloud with the different technologies and roles that have been covered in the experience. If you would click on any item in the tag-cloud, it would be part of the filter. The filter would be applied to the experience.

I wanted to show blocks of experience, to offer a different experience than what people are used to in common curricula vitae. And when you click on a block, the details of the block would be shown.

Maybe more features would be appear in the future, but this is it for now.

Prototype

So what I did was, start with Sketch Flow. Sketch Flow is my favorite tool for prototyping and specially is very easy to use. Please look at below screens for the result.

MeXperience Basic View MeXperience Detailed View

What do you think about this prototype? Do you have ideas for features that should be included?

Alright, lets hope to find some time for discussing architecture and the basic setup, and probably even more to follow.

Silverlight 4 – Credentials, we’ve got it!

image I’ve been writing on Credentials in context of Silverlight for some time now. I didn’t like the options that were available to secure services and allow integration with Silverlight. For some history search for “credentials” on my blog.

July 2008 – Silverlight 2 – A series of articles on possible (failed) work-arounds for getting Credentials in Silverlight.

March 2009 – Silverlight 3 – WebClient, WebRequest and WCF calls using Credentials?

July 2009 – Silverlight 3 – Did we get support for Credentials?

 

In Silverlight 3 we already got the property Credentials on both WebClient and WebRequest. But sadly there still was no implementation available. After the launch of Silverlight 3 Tim Heuer already commented that the feature for credentials was being considered for future versions. Very nice, specially because we finally got it in Silverlight 4 (beta).

Support for Credentials has come to the ClientHttp stack, so you must make sure you register the http prefix to be using ClientHttp stack.

WebRequest.RegisterPrefix("http://", System.Net.Browser.WebRequestCreator.ClientHttp);

Besides that we also need to make sure that we set the property UseDefaultCredentials to false. Depending on whether you make use of a WebRequest or use a WebClient it will look like this.

request.UseDefaultCredentials = false;

WebRequest with Credentials

When you want do a simple webrequest to a url that’s secured using credentials this can look like this.

private void DoWebRequestWithCredentials()
{
    WebRequest.RegisterPrefix("http://", System.Net.Browser.WebRequestCreator.ClientHttp);
    var request = WebRequest.Create(new Uri("http://mark.mymonster.nl")) as HttpWebRequest;
    request.Credentials = new NetworkCredential("username", "password");
    request.UseDefaultCredentials = false;
    request.BeginGetResponse(ResponseCallBack, request);
}

private void ResponseCallBack(IAsyncResult ar)
{
    var request = ar.AsyncState as HttpWebRequest;
    var response = request.EndGetResponse(ar) as HttpWebResponse;
    using(var reader = new StreamReader(response.GetResponseStream()))
    {
        string result = reader.ReadToEnd();
    }
}

WebClient with Credentials

The WebClient has the same properties. Works same as providing credentials to a WebRequest, that’s really nice.

private void DoWebClientDownloadWithCredentials()
{
    WebRequest.RegisterPrefix("http://", System.Net.Browser.WebRequestCreator.ClientHttp);
    var client = new WebClient();
    client.Credentials = new NetworkCredential("username", "password");
    client.UseDefaultCredentials = false;
    client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
    client.DownloadStringAsync(new Uri("http://mark.mymonster.nl"));
}

private void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
    string result = e.Result;
}

WCF?

Sadly during tryout I didn’t find the credentials property to be part of ClientBase<T>. So for WCF we still have to wait some time to get credentials, or will it be part of final Silverlight 4?

What happens when you provide the wrong credentials?

I tried to make a webrequest with the wrong credentials. I expected an exception similar to “Unauthorized”, but instead I received a WebException with the message “The remote server returned an error: NotFound.”. I hope the team changes this to a more meaning full exception, because this makes debugging very hard.

Launch of the Dutch Silverlight and Expression Insiders User Group

sixin-logo Today is the day the Dutch Silverlight and Expression Insiders User Group is officially launched.

How did it start?

A few months ago during the Dutch Microsoft DevDays I met Expression MVP Rob Houweling and creator of Silverlight Spy Koen Zwikstra. We’ve had some good talks around Silverlight and the at that time soon to be release version 3.

Rob Houweling contacted me some time after the DevDays where he informed me about his idea to start a Silverlight User Group, along with a few others: Koen Zwikstra, Timmy Kokke, Antoni Dol and Eric van den Hoek. We had our first informal meeting on the 7th of July 2009. We’ve formed our motto and divided some roles.

The Team

sixin-team

Our team consists of 6 professionals in designing and/or development, Rob Houweling will be our chairman.

Antoni Dol: Blog, Silverlight.net Profile

Eric van den Hoek (secretary/treasurer)

Rob Houweling (chairman): Blog, Silverlight.net Profile

Timmy Kokke: Blog, Silverlight.net Profile

Mark Monster (vice chairman): Blog, Silverlight.net Profile

Koen Zwikstra: Blog, Silverlight.net Profile

More details (in dutch) can be found on the About Us page.

What are we going to do?

First of all we want to be a User Group for both Designers and Developers who are enthusiastic about Silverlight and the Expression tools, who want to learn and share knowledge.

- We will maintain a website where we host interesting blogs and news related to Silverlight and Expression.

- We will organize small events

- We will speak on events organized by other User Groups like SDN or dotNed.

For now just bookmark the website of the Silverlight and Expression Insiders User Group.

Silverlight 3 – Local Messaging Explained + Enhancement

One of the new features Silverlight 3 introduced is called Local Messaging. This feature supports communication between different Silverlight applications that are running on the same client. This is particular useful in areas like Sharepoint where you offer different parts to be positioned at all places on the screen. It’s possible to even communicate between two Slverlight applications running on different domains. For example one app is running on maindomain.com and the second app is running on maindomain.nl or even more different.

The Local Message API is very easy to use. But let’s first setup the Proof of Concept environment. I created a solution with two Silverlight Applications (SUILeft and SUIRight), a hosting project (Web) and a fourth project which is a Silverlight Class Library, we will come to that soon.

image

I combined both the “Left” and “Right” applications in one html page, like this (please remind to change the paths according to your setup).

<div id="silverlightControlHost">
    <object data="data:application/x-silverlight-2," type="application/x-silverlight-2"
        width="40%" height="100%">
        <param name="source" value="ClientBin/MM.Silverlight.SUILeft.xap" />
        <param name="onError" value="onSilverlightError" />
        <param name="background" value="white" />
        <param name="minRuntimeVersion" value="3.0.40624.0" />
        <param name="autoUpgrade" value="true" />
        <a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=3.0.40624.0" style="text-decoration: none">
            <img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight"
                style="border-style: none" />
        </a>
    </object>
    <object data="data:application/x-silverlight-2," type="application/x-silverlight-2"
        width="40%" height="100%">
        <param name="source" value="ClientBin/MM.Silverlight.SUIRight.xap" />
        <param name="onError" value="onSilverlightError" />
        <param name="background" value="white" />
        <param name="minRuntimeVersion" value="3.0.40624.0" />
        <param name="autoUpgrade" value="true" />
        <a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=3.0.40624.0" style="text-decoration: none">
            <img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight"
                style="border-style: none" />
        </a>
    </object>
</div>
<iframe id="_sl_historyFrame" style="visibility: hidden; height: 0px; width: 0px;
    border: 0px"></iframe>

Coding the Receiver (Sender to Receiver)

I coded the receiver in the “Left” application. In the constructor I created a new LocalMessageReceiver with the name PoC.Messaging. This name is important because also the sending applications need to know this.

private readonly LocalMessageReceiver _receiver;

public MainPage()
{
    InitializeComponent();
    _receiver = new LocalMessageReceiver("PoC.Messaging");
    _receiver.MessageReceived += _receiver_MessageReceived;
    _receiver.Listen();
}

private void _receiver_MessageReceived(object sender, MessageReceivedEventArgs e)
{
    ResultBlock.Text = e.Message;
}

 

Sending messages (Sender to Receiver)

The sender looks similar, make sure the same name is used. I added a click handler to a button to send the static “Hello World!” messag.

private readonly LocalMessageSender _sender;

public MainPage()
{
    InitializeComponent();
    _sender = new LocalMessageSender("PoC.Messaging");
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    _sender.SendAsync("Hello World!");
}

 

Respond to messages (Receiver to Sender)

After receiving a message there’s an option to reply by simply setting the response. The enhanced MessageReceived handler now looks like this:

private void _receiver_MessageReceived(object sender, MessageReceivedEventArgs e)
{
    ResultBlock.Text = e.Message;
    e.Response = "Hello to you as well!";
}

Accepting response messages (Receiver to Sender)

To enable the sender to accept response messages, a new handler needs to be added to the SendCompleted event. The complete sender code now looks like this:

private readonly LocalMessageSender _sender;

public MainPage()
{
    InitializeComponent();
    _sender = new LocalMessageSender("PoC.Messaging");
    _sender.SendCompleted += _sender_SendCompleted;
}

private void _sender_SendCompleted(object sender, SendCompletedEventArgs e)
{
    string response = e.Response;
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    _sender.SendAsync("Hello World!");
}

This is all you need to do, nothing more, nothing less. But limited to only send strings from sender to receiver, and sending messages back to the sender. But we want more, we don’t want to be limited to sending strings, do we?

Enhancing Local Messaging

The idea is simple: Serialize complete objects on the sender, deserialize the strings back to objects on the receiver.

It’s not difficult at all, we have the same API available in Silverlight as we are used in the full CLR. Just add a reference to System.Xml.Serialization in both the “Left” and “Right” project. Also this is where the “Messages” project comes in to play, add a reference to this project from “Left” and “Right” as well.

This was just for preparation. The Messages project will contain the structures that are going to be serialized and deserialized, kind of a message contract. So let’s add our first structure: Customer.

public class Customer
{
    public string Name { get; set; }
    public string Country { get; set; }
}

This is still a very simple class, but this idea works with larger objects as well. But it’s important to explain there’s a limit to the size of the message that is send, it’s 40 kilobyte. It’s time for the little bit of sending magic.

private void Button_Click(object sender, RoutedEventArgs e)
{
    var customer = new Customer {Name = "Best Customer", Country = "Silverlight Island"};
    var serializer = new XmlSerializer(typeof(Customer));
    using (var sw = new StringWriter())
    {
        serializer.Serialize(sw, customer);
        _sender.SendAsync(sw.GetStringBuilder().ToString());
    }
}

And on the receiver side we need to deserialize the message, like this:

private void _receiver_MessageReceived(object sender, MessageReceivedEventArgs e)
{
    var serializer = new XmlSerializer(typeof (Customer));
    if (serializer.CanDeserialize(XmlReader.Create(new StringReader(e.Message))))
    {
        var deserializedCustomer =
            serializer.Deserialize(XmlReader.Create(new StringReader(e.Message))) as Customer;

        ResultBlock.Text = string.Format("Name:{0}, Country:{1}", deserializedCustomer.Name,
                                         deserializedCustomer.Country);
    }
}

That’s all. I’m interested to see more Silverlight applications making use of Local Messaging.

OpenID User Control in Silverlight – Part 2 OpenID Integration

It’s already a long time ago when I posted part 1 of the OpenID User Control, but sadly I didn’t have any time to blog, until now.

Recap: Part 1 explained how to create the visual design of the user control with two visual states. The visual design we create was also templatable, so you can provide your own template if you wish without changing any inner code. Let’s continue with this part.

OpenID Integration

The integration with OpenID is basically url-based. The application providing the login view constructs a url, redirects to this url, and after successful authentication it will redirect back to the application with a lot of parameters in the url. First of all the integration starts with the identity, also url based.

For example the identity http://openid.mymonster.nl/demo has even an page attached. The source in the page contains the links to the OpenID server. In this case:

<link rel="openid.server" href="http://www.myopenid.com/server" />
<link rel="openid2.provider" href="http://www.myopenid.com/server" />

This information is used while construction the login-url. The base url in this case would be:

http://www.myopenid.com/server?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=checkid_setup

In addition to this base url the following parameters are added as well.

openid.identity=http://openid.mymonster.nl/demo

openid.claimed_id=http://openid.mymonster.nl/demo

openid.return_to=http://somedemo.mymonster.nl

OpenID also has some extensions available. We can for example already ask for some fields to be filled in on the OpenID page when your application is authenticated for this user for the first time. Basically the first login is a registration.

openid.ns.sreg=http://openid.net/extensions/sreg/1.1

To add required fields add a comma separated list to the following parameter.

openid.sreg.required=email

To add optional fields add a comma separated list to the following parameter.

openid.sreg.optional=country

A complete url could then be something like this:

http://www.myopenid.com/server?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=checkid_setup

&openid.identity=http://openid.mymonster.nl/demo&openid.claimed_id=http://openid.mymonster.nl/demo

&openid.return_to=http://somedemo.mymonster.nl

&openid.ns.sreg=http://openid.net/extensions/sreg/1.1&openid.sreg.required=email&openid.sreg.optional=country

The OpenIdService class I created contains a method to combine the information of the Identity, OpenID server url, RequiredParameters and OptionalParameters.

/// <summary>
/// Creates the URL to the OpenID provider with all parameters.
/// </summary>
private string CreateRedirectUrl(string delegateUrl,
                                 string identity)
{
    string requiredParameters = string.Join(",", RequiredParameters.ToArray());
    string optionalParameters = string.Join(",", OptionalParameters.ToArray());

    var urlBuilder = new StringBuilder();
    urlBuilder.AppendFormat("?openid.ns={0}", HttpUtility.UrlEncode("http://specs.openid.net/auth/2.0"));
    urlBuilder.Append("&openid.mode=checkid_setup");
    urlBuilder.AppendFormat("&openid.identity={0}", HttpUtility.UrlEncode(delegateUrl));
    urlBuilder.AppendFormat("&openid.claimed_id={0}", HttpUtility.UrlEncode(identity));
    Uri documentUri = HtmlPage.Document.DocumentUri;
    string url = documentUri.ToString();
    if (!string.IsNullOrEmpty(documentUri.Query))
        url = url.Replace(documentUri.Query, string.Empty);
    urlBuilder.AppendFormat("&openid.return_to={0}", HttpUtility.UrlEncode(url));

    if (!string.IsNullOrEmpty(requiredParameters) || !string.IsNullOrEmpty(optionalParameters))
    {
        urlBuilder.AppendFormat("&openid.ns.sreg={0}", HttpUtility.UrlEncode("http://openid.net/extensions/sreg/1.1"));

        if (!string.IsNullOrEmpty(requiredParameters))
            urlBuilder.AppendFormat("&openid.sreg.required={0}", HttpUtility.UrlEncode(requiredParameters));

        if (!string.IsNullOrEmpty(optionalParameters))
            urlBuilder.AppendFormat("&openid.sreg.optional={0}", HttpUtility.UrlEncode(optionalParameters));
    }

    return urlBuilder.ToString();
}

The OpenID user control uses this url to redirect to the OpenID login page, so you are basically leaving your application. You will get presented a login screen, after login you will be redirected back to your application. The user control reads the url and parses it to determine if the login was successful.

The return url is something like: http://somedemo.mymonster.nl/?openid.assoc_handle=%7BHMAC-SHA1%7D%7B4aa94a51%7D%7BtceKsw%3D%3D%7D

&openid.claimed_id=http://openid.mymonster.nl/demo&openid.identity=http://openid.mymonster.nl/demo

&openid.mode=id_res

&openid.ns=http://specs.openid.net/auth/2.0&openid.ns.sreg=http://openid.net/extensions/sreg/1.1

&openid.op_endpoint=http://www.myopenid.com/server&openid.response_nonce=2009-09-10T18%3A493A53ZxSfHsI

&openid.return_to=http://somedemo.mymonster.nl/&openid.sig=h1el2rjtXXXxabB7nrsddyjpSTM%3D

&openid.signed=assoc_handle/claimed_id/identity/mode/ns/ns.sreg/

op_endpoint/response_nonce/return_to/signed/sreg.email

&openid.sreg.email=demo@mymonster.nl

I agree this is very long url, but to get the idea if the login was successful we have to check part by part. After we converted the querystring to a Dictionary<string,string>.

To determine if the request is an OpenID request we look for an openid.mode querystring parameter.

public bool IsOpenIdRequest(IDictionary<string, string> dictionary)
{
    return dictionary.ContainsKey("openid.mode");
}

Alright if we have an OpenID request we can continue and check if the login was successful. To get the original identity we look for a querystring parameter openid.claimed_id. If the login was successful the value for openid.mode will be id_res. After that we need to find all the query string keys that start with openid.sreg. to find out the parameter names of the required and optional parameters and the values of course.

public OpenIdUser Authenticate(IDictionary<string, string> query)
{
    var openIdUser = new OpenIdUser
                         {
                             Identity = query["openid.claimed_id"],
                             IsSuccess = query["openid.mode"] == "id_res"
                         };

    foreach (string keyName in query.Keys)
    {
        if (keyName.StartsWith("openid.sreg."))
            openIdUser.Parameters.Add(keyName.Replace("openid.sreg.", string.Empty), query[keyName]);
    }

    return openIdUser;
}

The full OpenIdService is collapsed below.

public class OpenIdService
{
    private static readonly Regex RegexHref = new Regex("href\\s*=\\s*(?:\"(?<1>[^\"]*)\"|(?<1>\\S+))",
                                                        RegexOptions.IgnoreCase);

    private static readonly Regex RegexLink = new Regex(@"<link[^>]*/?>", RegexOptions.IgnoreCase);

    public OpenIdService()
    {
        Downloader = new DefaultDownloader();
        RequiredParameters = new List<string>();
        OptionalParameters = new List<string>();
    }

    public IDownloader Downloader { get; set; }
    public List<string> RequiredParameters { get; set; }
    public List<string> OptionalParameters { get; set; }

    public void DefineLoginUrl(string identity, Action<string> loginUrlDefinedCallBack)
    {
        DefineServer(identity,
                     server =>
                         {
                             if (server == null)
                                 throw new OpenIdException("Determining OpenId server failed.");
                             loginUrlDefinedCallBack(
                                 string.Concat(server.ServerUrl,
                                               CreateRedirectUrl(server.DelegateUrl, identity)));
                         });
    }

    private void DefineServer(string identity, Action<Server> defineServerCallBack)
    {
        Downloader.Download(identity,
                            res =>
                                {
                                    if (string.IsNullOrEmpty(res))
                                        throw new OpenIdException("Couldn't find profile at identity.");
                                    defineServerCallBack(ProcessIdentityResponse(identity, res));
                                });
    }

    private Server ProcessIdentityResponse(string identity, string identityResponse)
    {
        var server = new Server();
        foreach (Match linkMatches in RegexLink.Matches(identityResponse))
        {
            string serverName = "openid.server";
            string delegateName = "openid.delegate";

            if (linkMatches.Value.IndexOf(serverName) > 0)
            {
                Match hrefMatch = RegexHref.Match(linkMatches.Value);
                if (hrefMatch.Success)
                {
                    server.ServerUrl = hrefMatch.Groups[1].Value;
                }
            }

            if (linkMatches.Value.IndexOf(delegateName) > 0)
            {
                Match hrefMatch = RegexHref.Match(linkMatches.Value);
                if (hrefMatch.Success)
                {
                    server.DelegateUrl = hrefMatch.Groups[1].Value;
                }
            }
        }
        if (string.IsNullOrEmpty(server.DelegateUrl))
            server.DelegateUrl = identity;
        if (!string.IsNullOrEmpty(server.ServerUrl) && !string.IsNullOrEmpty(server.DelegateUrl))
            return server;
        return null;
    }

    /// <summary>
    /// Creates the URL to the OpenID provider with all parameters.
    /// </summary>
    private string CreateRedirectUrl(string delegateUrl,
                                     string identity)
    {
        string requiredParameters = string.Join(",", RequiredParameters.ToArray());
        string optionalParameters = string.Join(",", OptionalParameters.ToArray());

        var urlBuilder = new StringBuilder();
        urlBuilder.AppendFormat("?openid.ns={0}", HttpUtility.UrlEncode("http://specs.openid.net/auth/2.0"));
        urlBuilder.Append("&openid.mode=checkid_setup");
        urlBuilder.AppendFormat("&openid.identity={0}", HttpUtility.UrlEncode(delegateUrl));
        urlBuilder.AppendFormat("&openid.claimed_id={0}", HttpUtility.UrlEncode(identity));
        Uri documentUri = HtmlPage.Document.DocumentUri;
        string url = documentUri.ToString();
        if (!string.IsNullOrEmpty(documentUri.Query))
            url = url.Replace(documentUri.Query, string.Empty);
        urlBuilder.AppendFormat("&openid.return_to={0}", HttpUtility.UrlEncode(url));

        if (!string.IsNullOrEmpty(requiredParameters) || !string.IsNullOrEmpty(optionalParameters))
        {
            urlBuilder.AppendFormat("&openid.ns.sreg={0}",
                                    HttpUtility.UrlEncode("http://openid.net/extensions/sreg/1.1"));

            if (!string.IsNullOrEmpty(requiredParameters))
                urlBuilder.AppendFormat("&openid.sreg.required={0}", HttpUtility.UrlEncode(requiredParameters));

            if (!string.IsNullOrEmpty(optionalParameters))
                urlBuilder.AppendFormat("&openid.sreg.optional={0}", HttpUtility.UrlEncode(optionalParameters));
        }

        return urlBuilder.ToString();
    }

    public bool IsOpenIdRequest(IDictionary<string, string> dictionary)
    {
        return dictionary.ContainsKey("openid.mode");
    }

    public OpenIdUser Authenticate(IDictionary<string, string> query)
    {
        var openIdUser = new OpenIdUser
                             {
                                 Identity = query["openid.claimed_id"],
                                 IsSuccess = query["openid.mode"] == "id_res"
                             };

        foreach (string keyName in query.Keys)
        {
            if (keyName.StartsWith("openid.sreg."))
                openIdUser.Parameters.Add(keyName.Replace("openid.sreg.", string.Empty), query[keyName]);
        }

        return openIdUser;
    }
}

How to integration all this in your application?

In part one we already discussed the option to provide your own visual template. But there needs to be an easy way to provide the list optional and required fields from xaml. To be enable changing a list in xaml there needs to be DependyProperty for this list. So the user control OpenIdLoginControl is changed a little bit to enable xaml editing for both OptionalParameters and RequiredParameters.

public static readonly DependencyProperty OptionalParametersProperty =
    DependencyProperty.Register(
        "OptionalParameters",
        typeof (List<String>),
        typeof(OpenIdLoginControl),
        new PropertyMetadata(new List<String>()));

public List<String> OptionalParameters
{
    get { return m_openIdService.OptionalParameters; }
    set { m_openIdService.OptionalParameters = value; }
}

public static readonly DependencyProperty RequiredParametersProperty =
    DependencyProperty.Register(
        "RequiredParameters",
        typeof (List<String>),
        typeof(OpenIdLoginControl),
        new PropertyMetadata(new List<String>()));

public List<String> RequiredParameters
{
    get { return m_openIdService.RequiredParameters; }
    set { m_openIdService.RequiredParameters = value; }
}

After this we can write the following things in the xaml of for example the MainPage.

<openid:OpenIdLoginControl x:Name="LoginControl" OnAuthentication="LoginControl_OnOnAuthentication">
    <openid:OpenIdLoginControl.RequiredParameters>
        <System:String>email</System:String>
    </openid:OpenIdLoginControl.RequiredParameters>
    <openid:OpenIdLoginControl.OptionalParameters>
        <System:String>country</System:String>
        <System:String>city</System:String>
    </openid:OpenIdLoginControl.OptionalParameters>
</openid:OpenIdLoginControl>

The login control also contains an event that will be called upon successful login, you can subscribe to it. To test yourself, the complete solution can be downloaded here. Please remember: this solution is probably not be the most secure option to integrate OpenID with Silverlight, but it’s the only option which doesn’t require anything on the server. Usage on your own risk, no guarantees for this solution.

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.