Friday, October 29, 2010

So What's the Fuss about Silverlight?

Perception is said to be reality and there has been a lot of speculation recently about where Silverlight is going and what Microsoft's position is. One of the reasons for this is the emphasis on HTML5 at the recent PDC conference. Unfortunately, that has led to talk and fear about the "doom and demise" of Silverlight. As a Silverlight MVP, what are my thoughts about this?

The HTML5 Story

First, let's start with the HTML5 story. It's a great one. I'm a fan of it, and I believe it is going to be a very positive force. The consumer-facing web is an enormous block of real estate that can only be enriched by providing a cross-browser standard that is far more current than the specification is today.

The problem is twofold: I don't think a lot of people really "get" what HTML5 is or does, and are trying too hard to make it solve problems it wasn't intended to.

If you read this post about a similar topic to my own, you'll see a link to this game written in HTML5 that is quite impressive. You'll also read that "It likely would taken about half of that in Silverlight" and that ... here is the kicker ... the developers "dealt with all kinds of browser compat issues."

The Promise: Cross Browser Compatibility

The Reality: Ummm ... Not Yet

Microsoft itself while embracing HTML5 managed to demonstrate some of the problems with HTML5. They showed how their IE9 browser used powerful graphic acceleration to provide HTML5 experiences at a frame rate much higher than competing browsers. I had to scratch my head there, because suddenly I've lost the consistency. It's great that IE9 does this, but what happened to a consistent cross-browser experience? The HTML5 promise falls short when the experience isn't consistent. If I want to reach my users, I have to code to the lowest common denominator and assume they're not going to be runnig this cool new version of the browser!

Just following the posts about audio and video and graphics I can't tell you how many examples I've read also come with which build of what browser it works on. "This only works with x build because of the audio features." That doesn't doom HTML5, but it sends a strong, "We're not quite ready message."

So What did History Teach Us?

Remember that old, ugly, out-dated HTML4 technology that HTML5 is coming to replace? The HTML4 specification appears 11 years ago: check it out yourself. How long is 11 years? It's a long time - it's before my 10-year old daughter was born. With that much time to digest the standard, take a look at the Acid 2 tests and let me know how far we've come for that truly cross-browser experience.

And we think HTML5 will somehow be different?

Again, don't get me wrong ...I like HTML5. I am taking a serious look at it and know it will be a force in the future. But as I tweeted earlier today, I have to ask ... "Will the real HTML5 line of business please stand up?" It's not there.

Oh, and, lest I forget ... HTML5 is markup. It's not a framework. It is popular to compare it with Silverlight, but Silverlight isn't just a way of rendering things ... it's an actual engine that runs business logic, stores data, and much more. I've written an object-oriented database called Sterling that runs in both Silverlight and on the Windows Phone 7 ... how easy would that be to build in, say, HTML5 for relational queries in offline mode?

My point is that I believe HTML5 is compelling for online content and document-based websites, but I'm nowhere near sold on the line of business story (or even the game story - there are some neat games but the frame rates are absolutely horrid compared to similar Silverlight and Flash games ... yes, Flash, I'm talking to you, too).

The Promise: Write Once, Run Anywhere

Do you remember the popular CASE Tools we were bombarded with in the 80s and 90s? The promise was that we could drag and drop some business logic on a service or use a pseudo-language and everything would generate to native languages for various systems and run everywhere. Of course, when we wanted to customize that, suddenly we had to crack open the tool and wade through obscure code and tweak it to behave the way we needed. What ended up happening is that the CASE tools began collecting dust in most cases and we went back to having different teams that would target different platforms. Most large corporations have this today - ever heard of the rivalry between the "Java shop" and the ".NET shop" in house?

One common trend recently is to tell me that HTML5 is going to be GREAT for phone applications because you'll write it once and it will run everywhere.

Do you really believe that?

Personally, my phone browser is fine for rendering 90% of the websites out there today. And I hate it. It is not a good user experience to have to use a pinch gesture to expand the screen and try to avoid accidentally clicking a link and then navigate somewhere and get something done.

Some of these sites are friendly and offer a mobile version. This is great. It makes a lot of sense to me for browsing pictures and flipping through articles. But this isn't accomplished by "write once." They might be using a single technology (HTML) but there are two pages, one for desktops, and one for browsers. That's "write twice."

I helped build a software company that provided mobile device management (MDM) solutions. We had to create clients for various devices such as Windows Mobile, RIM (Blackberry), Android, and iPhone (this was before Windows Phone 7 was released). So we invested a lot of time in looking at the best way to take a line of business application and penetrate the various platforms.

Of course, this problem had already been solved by other companies, and I think everyone would do well to learn from their examples. When I want to browse Facebook on the iPhone, I don't go to the Facebook page. I run an iPhone Facebook application. It wasn't written in HTML. It was written in Objective-C. Netflix has a streaming experience on the iPhone and it wasn't written in HTML either. Their version for Windows Phone 7 isn't HTML5, it's written in my language of choice, Silverlight.

What's my point? For rich, high-touch applications - I mean the ones with lots of functionality and interaction with the user - HTML doesn't cut it. And before we say HTML5 will solve it, you can make the interface you need with HTML4 and Javascript. It's a pain, but it's possible - and it also just doesn't feel nearly as good as having the response of the native application.

So we ended up doing what most successful mobile shops do. We took an idea, then we had an iPhone team write it for the iPhone, a Windows Mobile team write it for Windows Mobile, a Blackberry team write it for Blackberry ... you get the point.

If there was any "write once, run anywhere" code, it was the service layer. It turns out you can write a rich set of REST + JSON services that are very easy to consume and use across all platforms. That's the cloud coming into play, not HTML5.

Why I'm a Silverlight MVP

If I haven't convinced you that not many well-positioned mobile shops are going to start converting their mobile applications to 100% HTML5 anytime soon, let me share the story from a different angle. I was an ASP.NET developer and was helping build some pretty rich and interactive websites using JQuery, AJAX, and all of the latest and greatest technologies. The only problem was that a lot of companies are still running Internet Explorer 6.0 (yes, even with 9.0 coming out) and compatibility was an issue. It wasn't just IE6, though. Chrome and FireFox all had their subtle ways of "interpreting" the standards and we were burning development cycles trying to create a compelling experience that worked across all browsers (don't even get me started, Safari and Opera).

When I researched Silverlight, it was mainly from the perspective of improving the UI and also being able to truly write the UI once and have it run everywhere. That's all I was looking for and all I expected to get out of it.

Then I wrote the first proof of concept project, and the rest, as they say, was history. I was able to create a compelling experience so fast that my boss didn't even believe me until I demonstrated what I had done. We found it was incredibly easy to train the team and in no time were producing functionality we couldn't even begin to consider using the old HTML technology ... with fewer defects and in about 25% of the time.

You read that correctly. By my best estimate, transitioning parts of the web-based application to Silverlight enabled us to produce 4x what we did before and at higher quality. That is what led me to Silverlight. Frustrated that I had to dig to find this out, and seeing the public perception that it was nothing but a trumped up media player trying to compete with Flash ads, I set out to educate people about the platform through my blog, talks, and other venues. This ultimate lead to the Silverlight MVP award which I am thankful for because of the voice it has given me to share how powerful Silverlight is for serious, enterprise line of business applications.

So Why am I Sharing all of This?

HTML5 is a great technology and it will transform consumer-facing websites. I just don't buy that it is going to be a major player in the line of business world. Perhaps years down the road when standards start to coalesce and more tools are available, but now? The spec isn't set to be finalized until the year 2022. Yes, you read that correctly ... remember HTML4, 11 years ago? That's HTML5, 11 years in the future. Great things will happen before then.

However, I don't believe it is going to replace writing native mobile platform code. Silverlight is alive and well on Windows Phone 7 and will continue to thrive there as Java does on Android and Objective-C does on the iPhone. Let's talk economics - all of these platforms have application stores. Will HTML5 circumvent that? Will the platform truly transform to not need an application store because it's all HTML5? Is your third-person shooter game really going to run just as smoothly in an HTML5 engine? I don't think so ... only time will tell.

Let's forget the mobile side though. We aren't getting rid of laptops and desktops. They're here to stay, and you can't ignore how compelling that platform is for rich applications.

But that is where the playing field is different. Because I'm a Silverlight fan, I'll even use a competitive example: Adobe AIR. I don't see much speculation about AIR going away, when it is a platform for develping rich applications that run on different platforms.

Silverlight shares the same story. With the in-browser experience you can write rich, compelling, powerful applications that easily connect to services and drive user experiences in a short amount of time. I am able to write a fully-styled feed reader application from scratch that interrogates RSS feeds, shows them on an optimized grid and serves links to the sites and stores the data for offline reading in about 20 minutes. I challenge anyone reading this to do the same in HTML5 (and I'm not saying it can't be done - that would be a GREAT example I'll gladly link to when presented).

Silverlight in out-of-browser (OOB) mode is even more compelling. Now I can create a full-fledged application that runs even when disconnected from the Internet ... and here's the kicker: I can easily install it with a click over the web, and I'm confident it will run as I designed it on both a Windows and a Mac. I can truly write it once and run it on multiple targets.

That's a powerful story and I work every day for companies that see the value in that story. So while the speculation out there is that Silverlight is doomed, the project queue says otherwise and the applications you sometimes don't see are the ones driving critical business functions behind the four walls of the corporate intranet.

And the Final Word...

And as a final word, I do have to address the misconception that the PDC event failed to mention Silverlight at all. I heard the word "Windows Phone 7" multiple times, and short of XNA-based games, Windows Phone 7 is practically synonymous with Silverlight. So just substitute "Silverlight" for "Windows Phone 7" and you'll see it got plenty of coverage this go around.

I usually try to focus on technical articles but this is such a large topic that I felt I should share my insights and thoughts. Now I'm excited to hear your feedback as well! Please post comments below.

Jeremy, what's your position?

I believe Silverlight is the absolute BEST choice in MANY scenarios (but not all) and will continue to be a powerful force in the line of business, web-based application arena. I don't think it makes sense to replace web sites with Silverlight, and I absolutely agree that HTML5 is the technology to look at for media, content, and document-based websites and applications ... but for line of business? Give me Silverlight.

Jeremy Likness

Wednesday, October 27, 2010

Jounce Part 4: Region Management

Now that we've explored the concept of Jounce navigation, the next logical step is to examine region management. This concept was introduced by the Prism framework and is very powerful and functional for both WPF and Silverlight applications.

Region management deals with a few moving pieces. A region is simply an area of the display that has been marked or tagged to hold a control. Regions are as flexible as their containers: they can be a fixed size or dynamic. They can hold a single control or a collection of controls. Typical containers include ContentControl for single controls, ItemsControl for multiple controls, and any type of panel including Grid and TabControl.

Regions are tagged with a region name. In Jounce, tagging a region is as simple as using the ExportAsRegion attached property, as you can see here:

<Controls:TabControl Grid.Row="1" Regions:ExportAsRegion.RegionName="TabRegion"/>
<ItemsControl Grid.Row="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"
                Regions:ExportAsRegion.RegionName="AppRegion">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Horizontal"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>

Once a region is tagged, a view or control can be pointed at the region. In Jounce, you would use the ExportViewToRegion tag and pass the view's tag and the target region. In this example, the view is exported as we discussed in the previous post. An additional tag is added to route the view to the target region:

[ExportAsView(REQUEST_SQUARE, MenuName = "Request a Square")]
[ExportViewToRegion(REQUEST_SQUARE,LocalRegions.TAB_REGION)]
public partial class RequestSquare
{
}

So how does the view end up in the region? This is where the region adapter comes into play. The region adapter knows how to manage views in a specific region. For example, in Jounce, the ContentRegion adapter manages content controls, while the ItemsRegion adapter manages items controls. Because you can wrap these in a panel type, or override the ItemsControl to use any panel from Canvas to Grid or StackPanel as the container, these are the only adapters that ship with Jounce. We'll talk about creating your own adapters in a little while.

Here is the example from the Jounce quick start, showing the two regions and some controls:

Jounce region management

First, let's see how the view ends up in the region. The region is tagged and the RegionManager is notified. This manager knows about all regions and all region adapters. The region manager imports all of the view metadata for region routes:

[ImportMany(AllowRecomposition = true)]
public Lazy<UserControl, IExportViewToRegionMetadata>[] Views { get; set; }

It also exposes an indexer making it easy to find the region metadata given a view tag:

public IExportViewToRegionMetadata this[string viewType]
{
    get
    {
        return (from v in Views
                where v.Metadata.ViewTypeForRegion.Equals(viewType, StringComparison.InvariantCultureIgnoreCase)
                select v.Metadata).FirstOrDefault();
    }
}

When we navigate to the view, you'll remember the core Jounce framework wires the view to the view model, then raises the ViewNavigatedArgs message. This is what region management listens for:

[Export(typeof (IRegionManager))]
public class RegionManager : IRegionManager, IPartImportsSatisfiedNotification, IEventSink<ViewNavigatedArgs>
{
   ...
   public void HandleEvent(ViewNavigatedArgs navigationEvent)
    {
        if (navigationEvent.Deactivate)
        {
            DeactivateView(navigationEvent.ViewType);
        }
        else
        {
            ActivateView(navigationEvent.ViewType);
        }
    }
}

The activate and deactivate are similar. If this is the first time the region manager has seen the view, it stores the tag for the view, then finds the region adapter that manages the type of the view. It then informs the region adapter of the view so that it can be added to the region adapter's internal collection. Finally, regardless of whether the view has been "seen" before or not, all adapters that manage the view are called upon to activate the control.

var viewInfo = GetViewInfo(viewName);

if (viewInfo == null)
{
    return;
}

if (!_processedViews.Contains(viewName) && _regions.ContainsKey(viewInfo.Metadata.TargetRegion))
{
    // add any views that were waiting for this region to become available 
    var region = _regions[viewInfo.Metadata.TargetRegion];
    var regionAdapterInfo = GetRegionAdapterForType(region.GetType());
    if (regionAdapterInfo != null)
    {
        var regionAdapter = regionAdapterInfo.Value;
        regionAdapter.AddView(viewInfo.Value, viewName, viewInfo.Metadata.TargetRegion);
        _processedViews.Add(viewName);
    }
                
}
            
foreach(var ra in GetRegionAdaptersForView(viewName))
{
    ra.ActivateControl(viewName, viewInfo.Metadata.TargetRegion);                
}             

For the built in content control, activation is simple: the content is updated ot the new control, overwriting the old one (it still remains in memory, but is not longer part of the content control as a view).

public override void ActivateControl(string viewName, string targetRegion)
{
    _ValidateControlName(viewName);
    _ValidateRegionName(targetRegion);

    var region = Regions[targetRegion];            
    region.Content = Controls[viewName];           
}      

First, it validates it has the view. Next, it validates it has the region. Finally, it grabs the region (which in this case is a content control) and sets the content to the control.

So how does the region manage the view? The RegionAdapterBase is typed to the region (i.e. ContentControl) and keeps track of regions (a dictionary with region names and the actual content controls) and views (a dictionary with the view tag and the actual view control). It has default behaviors for activating and deactivating a control, and then allows the adapter to override this. The adapter simply exports itself with the type of region it can handle:

[RegionAdapterFor(typeof(ContentControl))]
public class ContentRegion : RegionAdapterBase<ContentControl>
{
}

Note that it inherits from the base control, typed to the type it is handling, and also exports the type so the region manager can find it.

Because tab controls have headers, Jounce doesn't natively provide an adapter because you might want the header to behave differently than Jounce would expect. In the quickstart, an example tab adapter is provided. Here is the code:

[RegionAdapterFor(typeof(TabControl))]
public class TabRegion : RegionAdapterBase<TabControl>
{
    [Import]
    public IEventAggregator EventAggregator { get; set; }

    private readonly List<string> _addedViews = new List<string>();

    [ImportMany(AllowRecomposition=true)]
    public Lazy<UserControl, IExportAsViewMetadata>[] Views { get; set; }               
        
    public override void ActivateControl(string viewName, string targetRegion)
    {
        _ValidateControlName(viewName);
        _ValidateRegionName(targetRegion);

        var region = Regions[targetRegion];

        if (!_addedViews.Contains(viewName))
        {
            _addedViews.Add(viewName);
            _SetupTabForView(region, viewName);
        }
                                   
        region.SelectedIndex = _addedViews.IndexOf(viewName);
    }

    private void _SetupTabForView(ItemsControl region, string viewName)
    {
        var metadata =
            (from v in Views where v.Metadata.ExportedViewType.Equals(viewName) select v.Metadata).FirstOrDefault();

        var header = metadata == null ? viewName : metadata.MenuName;
            
        var tabControlItem = new TabItem {Header = header, Content = Controls[viewName]};            

        region.Items.Add(tabControlItem);           
    }
}

In this case, the tab adapter keeps track of views added as well. It imports the metadata for the views because we'll use the extra fields such as "menu name" to build out our tabs. When the view is activated for the first time, the meta data is inspected and a TabItem is created with the menu name as the header and the control as the body. It is added to the tab control. Anytime the navigation event for the view is fired, the tab adapter sets the index to the correct tab so it is selected. This allows other triggers to navigate to the correct tab.

As you can see, region management greatly simplifies the problem of navigation by allowing easy, composite sections that can be nested as deep as necessary. Classes don't have to understand the layout or regions at all: there is no need to understand how to select a tab, because the region manager handles this. All that happens is the navigation event is raised, then the region manager steps in and takes care of the rest.

Here's a quick tip as well: you might want to have some fancy transitions that appear when you move from one page to the next. The content region prevents this because the old control is immediately overwritten. How would you handle this in Jounce?

It's easy. Use an ItemsControl instead and for each control, provide a visual state for showing the control and hiding it. That visual state can have any type of animation you like. Override the template panel for the control to be a Grid so all of the views are stacked on top of each other.

In the _Activate method of the view model, simply go to the visual state for the "show" state. In the _Deactivate, go to the visual state for the "hide" state. You can set up a class that listens for navigation events and keeps track of the last item. Whenever a new item is navigated, it will publish the "deactivate" event for the old item. This allows the old item to transition away while the new item transitions in. While the views are stacked in a region that handles multiple views, only the view with the "show" visual state will be visible at any given time.

Next post I'll share how you can integrate Jounce navigation with the built-in Silverlight navigation framework.

Jeremy Likness

Saturday, October 23, 2010

Jounce Part 3: Basic Navigation

Navigation is always an interesting topic and something I often see people struggle with in Silverlight applications. I am not a fan of navigation methods that force you to change the way your views behave, which is why I am not a fan of the navigation framework that is built into Silverlight (you suddenly have to use pages instead of user controls and understand frames and interact with special classes). Jounce takes a less intrusive approach that decouples how navigation works from how navigation is requested.

In the core Jounce framework you'll notice there are two classes, a ViewNavigationArgs and a ViewNavigatedArgs that derives from the first class. If you take a look at the code:

public class ViewNavigationArgs : EventArgs
{
    public ViewNavigationArgs(Type viewType)
    {
        ViewType = viewType.FullName;
    }

    public ViewNavigationArgs(string viewType)
    {
        ViewType = viewType;
    }

    public bool Deactivate { get; set; }

    public string ViewType { get; private set; }

    public override string ToString()
    {
        return string.Format(Resources.ViewNavigationArgs_ToString_ViewNavigation,
                                Deactivate
                                    ? Resources.ViewNavigationArgs_ToString_Deactivate
                                    : Resources.ViewNavigationArgs_ToString_Activate, ViewType);
    }
}

You can see there is not much to the class except a "view type" which is simply what we tagged our view with (the string that we discussed in the last post) along with a flag to indicate whether we are deactivating the view. To start the navigation cycle for Jounce, all you have to do is simply publish a ViewNavigationArgs message. Pretty simple, no?

Of course, I know you might not want your navigation to work the same way I like mine, so Jounce doesn't make any assumptions about how navigation will take place, it only provides the framework for notifying you about the event. When the event is notified, Jounce will take care of instancing the view (but remember it will not place it in the visual tree) and wiring the view model to the view (this happens in the loaded event, so the wiring also won't trigger until the view is in the visual tree). You must actually place the view, or use region management and a region adapter will place the view (more on that later).

There are several reasons why I chose to include the view tag for the navigation event. First, it is consistent with the same tag you use to bind views to view models so it gives a common "name" to a view you would like to see, without having to understand how the view is implemented. Second, it abstracts the type and implementation of the view. For a view model to "navigate" it simply publishes an event with a tag, and doesn't have to reference user controls or pages or any other navigation construct - in fact, you can build a completely new view with a completely different style, tag it the same, and have the navigation work.

Another reason why this is effective is because many applications have dynamically loaded modules. When the XAP file is loaded dynamically, the calling assembly does not have access to the type in the dynamically loaded assembly (until after it is loaded). I never felt that navigation should have to be aware of whether it is triggering a module load or not, which is why I built the ViewXapRoute class.

The view XAP route simply informs Jounce that a view with a particular tag can be found in a different XAP file. The class looks like this:

public class ViewXapRoute
{
    private ViewXapRoute()
    {
    }
        
    public static ViewXapRoute Create(string viewName, string viewXap)
    {
        return new ViewXapRoute {ViewName = viewName, ViewXap = viewXap};
    }

    public string ViewName { get; private set; }

    public string ViewXap { get; private set; }

    public override string ToString()
    {
        return string.Format(Resources.ViewXapRoute_ToString_View_Route_View, ViewName, ViewXap);
    }
}

The RegionManagement solution has an example of how this dynamic navigation works. If you take a look at RequestSquare.xaml.cs, you can see that a route is exposed for the view tagged "Dynamic" indicating it can be found in the XAP file called "RegionManagementDynamic.xap". The code looks like this:

[Export]
public ViewXapRoute DynamicRoute
{
    get
    {
        return ViewXapRoute.Create("Dynamic", "RegionManagementDynamic.xap");
    }
}

With this hint, Jounce can easily navigate now even if it means loading the XAP. Now take a look at two events. Both load a view, but one loads a view that is in another XAP file. Can you tell which one?

private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
{
    EventAggregator.Publish(new ViewNavigationArgs(Square.SQUARE));
}

private void ButtonDynamic_Click(object sender, System.Windows.RoutedEventArgs e)
{
    EventAggregator.Publish(new ViewNavigationArgs("Dynamic"));
}

The only difference is that one uses a magic string which could just as easily have been a constant instead (I wanted to demonstrate different ways). Notice that the request for the view is exactly the same in either case, which is what we want: the module requesting the view shouldn't have to know if it is dynamic or not. Somewhere else we provide the hint that the view exists in a different XAP file, and Jounce takes care of reading that hint and loading the XAP file.

We covered a little about what happens when the navigation is fired in the last blog post (the view model and view are wired and certain methods are called on the view model). Now let's take a full look at the event handler for the ViewNavigationArgs event that is in the ViewRouter class:

public void HandleEvent(ViewNavigationArgs e)
{
    if (e.Deactivate)
    {
        ViewModelRouter.DeactivateView(e.ViewType);
        EventAggregator.Publish(new ViewNavigatedArgs(e.ViewType){Deactivate = true});
        return;
    }

    var viewLocation = (from location in ViewLocations
                        where location.ViewName.Equals(e.ViewType, StringComparison.InvariantCultureIgnoreCase)
                        select location).FirstOrDefault();

    if (viewLocation != null)
    {
        DeploymentService.RequestXap(viewLocation.ViewXap,
                                        exception =>
                                            {
                                                if (exception != null)
                                                {
                                                    throw exception;
                                                }
                                                _ActivateView(e.ViewType);
                                            });
    }
    else
    {
        // just activate the view directly
        _ActivateView(e.ViewType);
    }
}

Notice that if the event is a deactivation, the view model is informed by calling the Deactivate method. This allows the view model to save state, release references, or do anything else that might need to be done when the view is being swapped out.

When the event is an activation, Jounce looks for a location hint. If it exists and the dynamic XAP file has not yet been loaded, it is loaded and the activation delayed until the dynamic XAP file is ready. Otherwise, it calls activation directly.

Also notice that at the end of Jounce's cycle of dealing with the event, it raises the ViewNavigatedArgs to indicate it has done the preliminary wiring of view models and XAP loading. This is something important to understand with Jounce navigation: the navigation is fired before Jounce does anything, and the navigated is fired afterwards, so if you are tapping into navigation and want to know that the view models have been activated, this is the event to listen for.

Again, Jounce is about being lightweight and guiding, not imposing. Because not everyone uses regions, the core Jounce framework does not include regions. Region management is packaged as a separate assembly that is optional. I'll talk about region management more in my next post, but it's important to know it is non-intrusive. If the region management assembly is included, the region manager will hook into the navigated event and use that event to move views into target regions.

Of course, we've now fired events, wired view models and even informed our view models that something has happened, but you are probably wondering, "Where is the navigation? So far I haven't seen a view actually do something."

Again, this is where Jounce gives you flexibility but I've included an example in the quick starts to help out. The SimpleNavigation project shows one way navigation can be handled.

In this sample project, several views are tagged with additional information. Jounce provides extra attributes for views that you can use to parse and categorize these. In our example, we're assuming that a view available from the top level navigation menu will have a category called "Navigation." We also want to provide a simple menu name for the view, as well as a description. One such view, a red square, is tagged like this:

[ExportAsView("RedSquare",Category="Navigation",MenuName = "Square",ToolTip = "Click to view a red square.")]
public partial class RedSquare
{
    public RedSquare()
    {
        InitializeComponent();
    }
}

Notice that these attributes are optional. The main navigation view, that should not also be an option itself, is simply tagged with a name:

[ExportAsView("Navigation")]
public partial class Navigation
{
    public Navigation()
    {
        InitializeComponent();
    }
}

The navigation view model pulls in the metadata for the views:

[ImportMany(AllowRecomposition = true)]
public Lazy<UserControl, IExportAsViewMetadata>[] Views { get; set; }

It then processes the views to create an observable collection with the tag for the view, the text that should display on the navigation button, and a tool tip for the button - note we are only looking for the navigation category:

foreach(var v in from viewInfo in Views where viewInfo.Metadata.Category.Equals("Navigation")
                    select Tuple.Create((ICommand)NavigateCommand,
                    viewInfo.Metadata.ExportedViewType, 
                    viewInfo.Metadata.MenuName, 
                    viewInfo.Metadata.ToolTip))
{
    _buttonInfo.Add(v);
}

Our navigation command does two things. It is only allowed if the command is for something other than the current view (we keep track of the current view in the CurrentView property). It also simply raises the view navigation event with the appropriate tag when fired. Also, notice the extension method that makes it easy to turn a string into a ViewNavigationArgs class. Here is the command:

NavigateCommand = new ActionCommand<string>(
view =>
    {
        CurrentView = view;
        EventAggregator.Publish(view.AsViewNavigationArgs());
    },
view => !string.IsNullOrEmpty(view) && !view.Equals(CurrentView));

And here is the XAML that binds the command:

<Button Margin="5" Command="{Binding Item1}" 
   CommandParameter="{Binding Item2}" Content="{Binding Item3}"
   ToolTipService.ToolTip="{Binding Item4}"/>

So basically the metadata for the views is used to generate a collection with information, and buttons are bound to those views that show the user-friendly name, provide a tool tip, and when fired, raise the view navigation event.

The shell for the project as a ContentControl that is bound to a property called CurrentView:

<ContentControl Grid.Row="1" Content="{Binding CurrentView}"/>

The view model listens for navigation events. When they happen, it uses the view model router to grab the view and bind it to the control. Because the view model router has to inspect all views in order to bind them to view models, it provides an indexer to make it easy to locate a view (without understanding the underlying details or implementation of the view). You simply pass the view tag and if the view has been found already, it will be returned. Take a look here - this is probably what you've been waiting for. It's an event that listens for navigation, and when a navigation event is raised, grabs the view so it appears in the content control:

public void HandleEvent(ViewNavigationArgs publishedEvent)
{
    if (!publishedEvent.ViewType.Equals("Navigation"))
    {
        CurrentView = Router[publishedEvent.ViewType];
    }
}

That's it - the button click raises the event, the view model listens for the event, cross-references the view and pushes it into a content control.

Of course, if that seemed like a lot of work, I agree, it was. This is on purpose: Jounce does not want to impose a navigation framework, so we are responsible for listening for publishing and receiving navigation events and doing something with them.

What's nice about this model is that navigation doesn't have to be a page. It can be an item, a sub-module, or even a tiny little control buried in the footer of the page. It is truly agnostic of your hierarchy, so it makes it easy to build composite applications because anywhere you need something to appear, you just raise an event with its tag.

If you want to make it even easier, forget about listening to the events and wiring in the views. We don't really want to deal with the views at all. We're developers, and we're focused on the code. So how do we take care of that, and let the views take care of themselves? We'll need to use a pattern introduced by Prism called "region management."

When I go into detail about region management in the next post, you'll start to see an emerging pattern with Jounce: tagging and binding. To get a view model bound to a view, we tag the view and view model, then give Jounce a hint to bind them. When we have a view in a different XAP file, we tag the view, then we export a binding that maps the view to the XAP. Region management is the same way. Instead of listening to navigation and manually grabbing views and setting them, region management lets us tag an area of the page and call it a region, then tag a view and let Jounce know the view belongs in that region.

We'll learn more about that next time ... but in the meantime, you can pull down the simple navigation project to explore these concepts further.

Jeremy Likness

Tuesday, October 19, 2010

Jounce Part 2: Getting Started (Tagging and Binding)

In this second installment about the Jounce framework, I'll share how to get started with Jounce and wire your first view bound to a view model.

The Jounce project is available online at http://Jounce.Codeplex.com.

Jounce injects itself as a service to the application. In my opinion, using IApplicationService is the right way to handle start up and shut down in a Silverlight application. It is also an opportunity to wire in configuration because a context is passed that contains the initialization parameters, as well as hook into events such as unhandled exceptions.

When you create a new application, you can blow away all of the junk you see in the App.xaml.cs code behind. Instead, you go into the XAML and insert the Jounce Application Service in a special section for application services:

<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
             xmlns:Services="clr-namespace:Jounce.Framework.Services;assembly=Jounce.Framework" 
             x:Class="Jounce.App"
             >
    <Application.ApplicationLifetimeObjects>
        <Services:ApplicationService/>
    </Application.ApplicationLifetimeObjects>
</Application>

That's all Jounce needs to register and plug into the Silverlight application lifecycle. When the appplication first starts, it will call this method:

public void StartService(ApplicationServiceContext context)
{
    var logLevel = LogSeverity.Warning;

    if (context.ApplicationInitParams.ContainsKey(Constants.INIT_PARAM_LOGLEVEL))
    {
        logLevel =
            (LogSeverity)
            Enum.Parse(typeof (LogSeverity), context.ApplicationInitParams[Constants.INIT_PARAM_LOGLEVEL], true);
    }

    _mainCatalog = new AggregateCatalog(new DeploymentCatalog()); // empty one adds current deployment (xap)

    var container = new CompositionContainer(_mainCatalog);

    CompositionHost.Initialize(container);
    CompositionInitializer.SatisfyImports(this);

    if (Logger == null)
    {
        ILogger defaultLogger = new DefaultLogger(logLevel);
        container.ComposeExportedValue(defaultLogger);
    }

    DeploymentService.Catalog = _mainCatalog;
    _mefDebugger = new MefDebugger(container, Logger);
}

Here, Jounce is doing several things. It sets a default level for logging, but also checks the initialization parameters and overrides the level to whatever is set. This allows you to control the logging level by adding the parameter to the Silverlight object host. In this example, Jounce is being configured to log verbosely:

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

Jounce wires in a default aggregate catalog to manage various deployment catalogs (this is what allows for dynamic XAP loading). There are two key things that also happen. First, Jounce declares an import for the logger contract that looks like this:

[Import(AllowDefault = true, AllowRecomposition = true)]
public ILogger Logger { get; set; }

The "allow default" tells MEF that null is fine and don't complain if nothing is exported for the contract. If you decide to implement your own logger and export it, it will be imported here and Jounce will use your custom logger. However, if you don't provide one, that's fine - Jounce can check the property for a null value and supply it's own default logger that simply writes to the debugger. This is exported to the container so future requests for a logger will pull in the default logger supplied by Jounce.

The MefDebugger is also important because it wraps the MEF container with some debug code that spits out lots of diagnostic information to help you troubleshoot what's going on with the Managed Extensibility Framework. If you debug the default application shipped with Jounce, you'll see plenty of activity in the debug window.

0/19/2010 6:44:29 AM Verbose Jounce.Core.MefDebugger :: MEF: Found part: Jounce.ViewModels.WelcomeViewModel
10/19/2010 6:44:29 AM Verbose Jounce.Core.MefDebugger ::    With import: Jounce.Core.ViewModel.BaseViewModel.EventAggregator (ContractName="Jounce.Core.Event.IEventAggregator")
10/19/2010 6:44:29 AM Verbose Jounce.Core.MefDebugger ::    With import: Jounce.Core.ViewModel.BaseViewModel.Router (ContractName="Jounce.Core.ViewModel.IViewModelRouter")
10/19/2010 6:44:29 AM Verbose Jounce.Core.MefDebugger ::    With import: Jounce.Core.ViewModel.BaseViewModel.Logger (ContractName="Jounce.Core.Application.ILogger")
10/19/2010 6:44:29 AM Verbose Jounce.Core.MefDebugger ::    With export: Jounce.ViewModels.WelcomeViewModel (ContractName="Jounce.Core.ViewModel.IViewModel")
10/19/2010 6:44:29 AM Verbose Jounce.Core.MefDebugger ::       With key: ViewModelType = Welcome
10/19/2010 6:44:29 AM Verbose Jounce.Core.MefDebugger ::       With key: ExportTypeIdentity = Jounce.Core.ViewModel.IViewModel
10/19/2010 6:44:29 AM Verbose Jounce.Core.MefDebugger ::    With export: Jounce.ViewModels.WelcomeViewModel (ContractName="Jounce.Core.ViewModel.IViewModel")
10/19/2010 6:44:29 AM Verbose Jounce.Core.MefDebugger ::       With key: ViewModelType = Welcome
10/19/2010 6:44:29 AM Verbose Jounce.Core.MefDebugger ::       With key: ExportTypeIdentity = Jounce.Core.ViewModel.IViewModel

This is just one example of a "part" found and the resulting imports and exports. This is very valuable to track down why imports or exports might not be behaving the way you expect. Take a look at the source for the MefDebugger. It spins through all parts in the container, but then hooks into an event so that any time the container is rewired (such as when a dynamic XAP file is loaded) it will dump the changes for you as well.

The next important lifecycle event is the Starting method, called after the initialization but before the visual tree is completely wired. Take a look here:

public void Starting()
{
    Application.Current.UnhandledException += Current_UnhandledException;

    var viewInfo = (from v in Views where v.Metadata.IsShell select v).FirstOrDefault();

    if (viewInfo == null)
    {
        var grid = new Grid();
        var tb = new TextBlock
                        {
                            Text = Resources.ApplicationService_Starting_Jounce_Error_No_view
                        };
        grid.Children.Add(tb);
        Application.Current.RootVisual = grid;
        Logger.Log(LogSeverity.Critical, GetType().FullName,  Resources.ApplicationService_Starting_Jounce_Error_No_view);
        return;
    }

    Application.Current.RootVisual = viewInfo.Value;
    Logger.LogFormat(LogSeverity.Information, GetType().FullName,
                        Resources.ApplicationService_Starting_ShellResolved, MethodBase.GetCurrentMethod().Name,
                        viewInfo.Value.GetType().FullName);
    Logger.Log(LogSeverity.Information, GetType().FullName, MethodBase.GetCurrentMethod().Name);            

    EventAggregator.Publish(viewInfo.Metadata.ExportedViewType.AsViewNavigationArgs());
}

Jounce hooks into the unhandled exceptions and provides a method that basically raises a custom message. This is done using the Event Aggregator (more on that later) but makes it easy to subscribe to exceptions and even handle them in your code.

When you tag a view in Jounce, you can mark a view as the shell. This should only be done with one view. Jounce looks for that view and sets it to the root visual, or creates an error message and displays that instead.
The view metadata is imported to the application service like this:

[ImportMany(AllowRecomposition = true)]
public Lazy<UserControl, IExportAsViewMetadata>[] Views { get; set; }

Jounce then logs some messages and raises a navigation event. We'll cover navigation in Jounce later - the important thing to know is that this navigation event is what informs Jounce that it should go out and find any related view models and bind them to the view.

How did we get the view to appear as the shell?

[ExportAsView("Welcome",IsShell = true)]
public partial class Welcome
{
    public Welcome()
    {
        InitializeComponent();            
    }
}

Jounce gives you plenty of flexibility to tag your views and view models. The only rule is that each tag should be unique. Here, we're just using a "magic string" but in most applications (and you'll see the examples in the quick starts) constants can be defined instead. In this case, the view is tagged with the tag "Welcome" and set as the shell. That's all it takes to make that view the first view that appears.

Of course, the whole point of the framework is MVVM. In the Jounce default example, we create a simple view model that handles a welcome message but also kicks off an animation when loaded. The view model looks like this:

[ExportAsViewModel("Welcome")]
public class WelcomeViewModel : BaseViewModel 
{
    public WelcomeViewModel()
    {
        Title = "Welcome to Jounce!";            
    }

    private string _title;

    public string Title
    {
        get { return _title; }
        set
        {
            _title = value;
            RaisePropertyChanged(()=>Title);
        }
    }

    public override void _Activate(string viewName)
    {            
        GoToVisualState("WelcomeState",true);
    }
}

This view model is also tagged like the view. It can have a different tag than the view, because Jounce allows for the same view model to be bound to multiple views. What's important here is that it is tagged.

The base view model offers several services, including methods you can override when the view model is created for the very first time (_Initialize), anytime the view it is bound to becomes activate (_Activate) and anytime the view is deactivated (_Deactivate).

When the navigation event for a view is raised, Jounce intercepts this call and binds the view model and calls the appropriate methods.

Take a look at the view router. It is an event sink for the view navigation event - this allows it to receive any global messages sent for navigation:

public class ViewRouter : IPartImportsSatisfiedNotification, IEventSink<ViewNavigationArgs>

The view router in turn calls the ActivateView method on the ViewModelRouter. The method looks like this:

public bool ActivateView(string viewName)
{
    Logger.LogFormat(LogSeverity.Verbose, GetType().FullName, Resources.ViewModelRouter_ActivateView,
                        MethodBase.GetCurrentMethod().Name,
                        viewName);

    if (HasView(viewName))
    {
        var view = ViewQuery(viewName);

        var viewModelInfo = GetViewModelInfoForView(viewName);

        if (viewModelInfo != null)
        {
            var firstTime = !viewModelInfo.IsValueCreated;

            var viewModel = viewModelInfo.Value;

            if (firstTime)
            {
                viewModel.GoToVisualState =
                    (state, transitions) =>
                    JounceHelper.ExecuteOnUI(() => VisualStateManager.GoToState(view, state,
                                                                                transitions));
                _BindViewModel(view, viewModel);
                viewModel.Initialize();
            }
            viewModel.Activate(viewName);
        }

        return true;
    }
    return false;
}

As you can see, a view model is found to match the view and is bound (the view model router will look for LayoutRoot and bind the view model there ... if it can't find it, it will bind it to the parent control's DataContext). You can also see that Jounce binds an action called GoToVisualState that executes on the UI thread. This allows the view model to fire a visual state transition without referencing the view. In our example, the _Activate method is overridden to call:

public override void _Activate(string viewName)
{            
    GoToVisualState("WelcomeState",true);
}

This triggers a visual state transition and animations the "Welcome to Jounce" message.

There is one final piece missing. We never specified how to know that the welcome view model belongs with the welcome view. In Jounce, I decided against a traditional locator pattern to provide more flexibility. Instead, a binding is exported that maps a view model to a view. What's nice about this approach is that the binding can be consolidated in a class that holds all bindings for the application, or localized to the view so it is clear what view model the view is binding to. You could also write your own interface, such as a fluent or convention-based interface, and export bindings to the container to achieve the same result.

In the Welcome view, we mark the binding by exporting the tag for the view model and the tag for the view here:

[Export]
public ViewModelRoute Binding
{
    get
    {
        return ViewModelRoute.Create("Welcome", "Welcome");
    }
}

That's all there is to it! From your perspective, you had to:

  1. Add the Jounce Application service to the App.xaml
  2. Build your view model and tag it
  3. Build your view and tag it, then mark it as the shell
  4. Export a binding for the view model and the view

Jounce took care of all the rest.

The Jounce project is available online at http://Jounce.Codeplex.com.

Jeremy Likness

Friday, October 15, 2010

Don't Miss the December Silverlight Firestarter Event!

Silverlight Firestarter

It seems like ages ago when I was trying to decide how to take a mobile device management application to the next level. The product provided a very rich and detailed web-based interface that was designed to look and feel like a native desktop application. Needless to say, the development team had to become experts with CSS, JavaScript, and AJAX technologies, not to mention cross-browser incompatibilities. Silverlight looked like it was going to be a phenomenal solution, but I wanted to connect with others who had more experience to understand any limitations and learn what best practices and patterns were being used. This led me to a local Firestarter event ... and the rest, as they, is history!

On December 2, 2010 from 8am PST / 11am EST until 5pm PST / 8pm EST you can attend the global Silverlight Firestarter event at no charge.

Speakers and presenters Scott Guthrie, Dan Wahlin, John Papa, and many more will spend the day covering topics ranging from LINQ and MVVM to Windows Phone 7. This will be an exciting event and is being streamed live from Redmond.

Check out this link for more details and I hope to "see" you there!

Jeremy Likness

Tuesday, October 12, 2010

Jounce Part 1: Why?

Over the next few weeks I will be publishing a series of posts explaining the open source Jounce project that I just released. Jounce provides guidance for building modular Silverlight applications using the Managed Extensibility Framework (MEF) and Model-View-ViewModel (MVVM).

Origins

I have been building enterprise Silverlight applications since late in the version 2.0 cycle. Many of these applications require extensibility: making it easy to add new screens or modules. The applications often require modularity as well, specifically to allow for dynamic loading of assemblies to reduce the initial memory footprint and impact on the client browser.

In addition, after trying Model-View-Controller (MVC) and experimenting with MVVM, I quickly found MVVM to be the right tool for my job. My background of developing mission critical applications for over a decade had already imposed the discipline of creating decoupled solutions that are easy to unit test and build in parallel to scale across large development teams. After exploring homegrown factory methods, Unity, and other patterns, I found that MEF really addressed most of my Silverlight needs out of the box.

The end result is that I've been using a common set of patterns across multiple applications. Developers know and understand the value of tools and macros. We're wasting our customers' time if and when we build something from scratch that has already been addressed, and this is why I constantly found myself sharing base classes, interfaces, and other core structures across various projects. I kept this basic framework factored separately to seed new projects and added to it as I encountered new challenges.

This framework is what I recently packaged as Jounce. It is similar to many MVVM frameworks because it provides messaging services and notify property changed handlers. It is different beacuse it does not attempt to abstract the underlying inversion of control - make no mistake: Jounce has a direct, explicit dependency on the Managed Extensibility Framework.

Why Should I Use Jounce?

I've been wanting to share Jounce for quite awhile but resisted because the last thing we need is "yet another framework." I was asked that exact question when I decided that all of my blog posts and examples just don't make as much sense without the context of the underlying framework they derive from.

The answer to the question is a simple one. "Don't."

I'm not trying to compete with the many other mature frameworks that are there. I like all of them. So why Jounce? I released Jounce less as a framework to package and solve all problems, and more as guidance for how I've found MEF can help solve MVVM problems in the Silverlight domain. It's a very specific niche, and I'm not trying to cover both Silverlight and WPF and the Windows Phone 7, nor abstract the notion of dependency injection containers. This is a very specific set of principles that I've used to successfully resolve challenges in a specific space.

The idea here is that you can pull down the source for a framework that is extremely lightweight - less than 1,000 lines of code. That's something you can dig into and look at inside and out. The license for Jounce allows you to use it right out of the box if you like, but my guess is that most people have far more specific needs for their framework than any package can provide "out of the box." For this reason, you are welcome to customize and modify Jounce or use the pieces that make sense for your project and toss the rest out.

Jounce is far more lightweight than Prism is and addresses fewer concerns. The Jounce implementation of the region management pattern takes only a few lines of code and isn't quite as powerful as Prism is ... but I've seldom needed that extra power. Jounce does not have automatic convention-based data-binding that Calburn and Caliburn.Micro provide, but again, I've found I prefer to use explicit data binding and design-time view models with a focus designer-developer workflow.

I've tried to note where a portion of Jounce was derived from another framework, both to give due credit and also to give you a place to explore further.

Some of the guiding principles behind Jounce:

How do I Get Started?

The full source code for Jounce is available at Codeplex. You can pull it down, compile, and get started. The framework has a web project with a simple view and view model. There are several quick starts that demonstrate navigation, regions, dynamic XAP loading, and more ... and I plan on adding others for the 1.0 release.

The power of Jounce I believe comes from the experience of using it. None of the quick starts took very long for me to create with no or minimal troubleshooting to make a feature work. That in my mind is the ideal goal of a framework: to be lightweight and easy to use, and easy to troubleshoot when things go sour. I believe Jounce exhibits all of this.

So in conclusion, Jounce is not the "next hot framework" that I'm begging you to download and use. I don't intend to come out with super cool feature-packed releases ... but if I find a better way to solve a problem or a new idea that pops up often, I will look at including that in the future.

In the next posts I'll start drilling into the details of how Jounce works. Much of this will be familiar to long time readers of my blog, with the difference that the ideas are encapsulated in a full reference framework rather than parceled into small "sample project" chunks.

Your feedback and suggestions are more than welcome!

Jeremy Likness