Wednesday, September 05, 2012

Convention Based Page Discovery With The Okra App Framework

One of the key features of the Okra App Framework for developing Windows 8 apps is the navigation framework with its support for the MVVM pattern. By default pages and view-models are marked with attributes so that the framework can locate them. In this post I will describe how you can enable an alternative convention based approach.

The Default Attribute Based Approach

When using the Okra App Framework navigation support all pages are represented by a page name. So that the framework can locate the associated pages and view-models these are normally attributed with either the PageExportAttribute or ViewModelExportAttribute respectively (for more information see my previous post). For example for a page named “Foo” the classes would be attributed as,

[PageExport("Foo")]
public sealed partial class FooPage : LayoutAwarePage
{
    ...
}
 
[ViewModelExport("Foo")]
public class FooViewModel
{
    ...
}

A Convention Based Approach

Often however there will be a common naming pattern throughout the application. In the example above all pages are named XxxPage and view-models named XxxViewModel, where “Xxx” is the associated page name. In a convention based approach you no longer need to apply attributes to classes. Instead they are automatically discovered based on a common naming system.

Since the standard Okra bootstrapper uses MEF for composition (for example when using the Okra.MEF NuGet package), we can use the MEF convention based discovery. To enable this we need to add the following code to the application bootstrapper,

public class AppBootstrapper : OkraBootstrapper
{
    ...
 
    // *** Overriden base methods ***
 
    protected override ContainerConfiguration GetContainerConfiguration()
    {
        ConventionBuilder conventionBuilder = new ConventionBuilder();
 
        conventionBuilder.ForTypesMatching(type => type.FullName.EndsWith("Page"))
                         .Export(builder => builder.AsContractType<object>()
                                                   .AsContractName("OkraPage")
                                                   .AddMetadata("PageName", type => type.Name.Substring(0, type.Name.Length - 4)));
 
        conventionBuilder.ForTypesMatching(type => type.FullName.EndsWith("ViewModel"))
                         .Export(builder => builder.AsContractType<object>()
                                                   .AsContractName("OkraViewModel")
                                                   .AddMetadata("PageName", type => type.Name.Substring(0, type.Name.Length - 9)));
 
        return GetOkraContainerConfiguration()
                .WithAssembly(typeof(AppBootstrapper).GetTypeInfo().Assembly, conventionBuilder);
    }
}

With this code in place we can greatly simplify our page and view-model definitions to the following. Note that since we follow the convention we no longer need to add any attributes.

public sealed partial class FooPage : LayoutAwarePage
{
    ...
}
 
public class FooViewModel
{
    ...
}

Summary

As I have shown, when using the Okra App Framework navigation support you can simplify app development by using a convention based approach to defining pages and view-models for the MVVM pattern.

A sample application demonstrating this is available from the Okra CodePlex downloads.

4 comments:

Marlon said...

I'm a newbie to this framework, enjoying it so far, thanks!.

Question: What are the convention requirements for implementing a Shell? Maybe you haven't gotten around to supporting it yet.

Unknown said...

Hi Marlon,

You can find out more about shell support here. There is no reason why you couldn't combine this with the convention based approach discussed above.

Andy

Christoban said...

Just discovered Okra after limping along with MvvmLight. I'm loving the Windows 8 focus! This is hands down the best Mvvm framework for W8.

I, too have a question, though!

I saw your explanation of how you use a different approach to design time data, using the LayoutAwarePage's DefaultViewModel. That works great on Pages, but what is your approach to UserControls, which usually inherit their runtime data context from their 'owner,' (such as {Binding Child1}) but at design time need another instance?

Unknown said...

Hi Christoban,

I'm please you like the Windows 8 focus of the Okra app framework - more of this to come in the future, in particular with relation to the various system contracts.

Regarding your query, my current favored approach (for both Pages and UserControls) to design-time data is to use the "d:DataContext" property on the root element.

For each view-model (e.g. 'MyPageViewModel.cs') I have a matching design-time version that derives from the view-model (e.g. 'MyPageDesignData.cs'). In the constructor I can then set up all my design-time data, and can even override any properties that only have public setters. In my XAML I then add the line "d:DataContext={d:DesignInstance local:MyPageDesignData IsDesignTimeCreatable=True}" onto the root Page/UserControl element.

Hope this is clear - it is easier with some sample code to show. I have an Okra search sample that I'm looking to publish soon that uses this approach so watch out for that.

Andy