Monday, January 31, 2011

The Visual State Manager Explained

This evening I presented a talk that dove into the details of Silverlight's Visual State Manager. I enjoyed the talk and had a terrific crowd with some very good questions. The deck explains much of what I went into with the talk, including exactly how the VSM works from when and how it starts and stops storyboards to the timing and flow of a state change event (including a flow chart that maps out the exact steps).

I've posted the PDF for the slide deck here for you to download.

The sample project for exploring the VSM, what I call the "Visual State Explorer" can be downloaded from here. This example provides some simple buttons and states to click and watch transitions. It hooks into the events and storyboards behind the VSM and shows exactly when and how they are started and stopped.

Finally, a more comprehensive examle is included in the quick starts that are part of Jounce.

Thanks to everyone who attended and I hope those who didn't find value in these resources.

Jeremy Likness

Wednesday, January 19, 2011

Jounce Part 10: Visual State Manager

One of the most common questions I get about Jounce is how to handle transitions, since the ContentControl immediately swaps out content. There are some third-party solutions, such as the TransitioningContentControl from the Silverlight toolkit, but Jounce can also handle this right out of the box.

Take a look at an example:


The secret is through what I believe is the appropriate use of the Visual State Manager and a Jounce helper that was recently added, the Visual State Aggregator. To demonstrate how transitions work, I created a sample project in the Jounce quick start called VSMAggregator. You can download the source to view the example here (simply choose the download link in the upper right for the latest version).

States and Parts

Before we fully dive into why I chose to use the Visual State Manager explicitly within Jounce, you must understand the "states and parts" model present in Silverlight. An excellent series on understanding the model can be found here.

In essence, Silverlight provides for a "lookless" model using parts and states. Lookless allows you to define a control that has a very specific purpose, yet allow the user quite a bit of room in customizing how it looks and behaves. This is because you don't define the full UI for the control. Instead, you define the parts of the control (the surface of a button, the "thumb" on a scroll bar, the background of a slider) and allow the user to template them. Then, you define the possible states of the control and give the user the ability to respond to a state by changing colors or animating properties.

This is a very powerful system and allows much of the design work to be completely offloaded to the design team while the developer can focus on development. As a developer, the main thing you need to know is that you can go into a state. Controls can have different groups that represent different aspects of states, but we'll keep it simple for now. Assume we have a group called "NavigationStates" and we know we can either be in a "HideState" or a "ShowState."

A Question of Control

The Visual State Manager (VSM) provides an attached property called VisualStateGroups for defining states. When you are creating a custom control, there are precise areas where you can define the states for the parts of your template. Likewise, if you are customizing a templated control, the template supplies all of the states and you simply have the task of deciding what they do.

What happens in a UserControl that doesn't technically have parts? This is where it gets interesting. You can only change the state of an element that inherits from Control. However, defining your visual states at the UserControl level may have unexpected results because you are not able to reference the elements that are contained within the root visual. Fortunately, if you define the visual states groups in the root visual for the UserControl, even though the attached property exists at the level of an element that may not inherit from Control, those states will still be available to the parent control.

This means that typically you will define the visual states for a UserControl in whatever you choose as the "LayoutRoot" which in most cases will be a Grid. The only caveat is that when you programmatically manipulate states, you must climb the visual tree to the nearest Control element to choose as a target for your state changes. More on that in a bit.

States are Storyboards

States are storyboards, pure and simple. As long as you think of states that way, you should have no problem grasping the significance. If you aren't already aware of the hierarchy used by Silverlight to evaluate dependency properties, I suggest you hurry up and read about Dependency Property Value Precedence. Basically, the VSM uses storyboards to coerce dependency properties into a different value.

It can sometimes get confusing when you are defining a visual state because you have two parts to work with: a state (required) and a transition (optional). These are actually two different storyboards that operate against the target control. What's even more confusing is that a "state" is supposed to be the fixed value of the control but you can supply a duration and animate a state! What?

Don't do it! Just because you can doesn't mean you should.

States and Transitions

To make your life easier, I'd like to suggest what I consider some best practices. To make visual states behave the way you want and expect, follow these two guidelines:

One: States are zero-duration storyboards that represent exactly how your control should look when it is in that state.

In the example, our RedView defines three states. A "hidden state," a "shown state" and a special state it goes into when a slide-in is chosen. To begin with, we simply define exactly how we want it to look in the given state.

<VisualStateManager.VisualStateGroups>
    <VisualStateGroup x:Name="NavigationStates">
        <VisualStateGroup.States>
            <VisualState x:Name="Default"/>
            <VisualState x:Name="ShowState">
                <Storyboard Duration="0:0:0">
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="(UIElement.Visibility)">
                        <DiscreteObjectKeyFrame KeyTime="0:0:0">
                            <DiscreteObjectKeyFrame.Value>
                                <Visibility>Visible</Visibility>
                            </DiscreteObjectKeyFrame.Value>
                        </DiscreteObjectKeyFrame>
                    </ObjectAnimationUsingKeyFrames>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="(UIElement.IsHitTestVisible)">
                        <DiscreteObjectKeyFrame KeyTime="0:0:0">
                            <DiscreteObjectKeyFrame.Value>true</DiscreteObjectKeyFrame.Value>
                        </DiscreteObjectKeyFrame>
                    </ObjectAnimationUsingKeyFrames>
                </Storyboard>
            </VisualState>
            <VisualState x:Name="HideState">
                <Storyboard Duration="0:0:0">
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="(UIElement.Visibility)">
                        <DiscreteObjectKeyFrame KeyTime="0:0:0">
                            <DiscreteObjectKeyFrame.Value>
                                <Visibility>Collapsed</Visibility>
                            </DiscreteObjectKeyFrame.Value>
                        </DiscreteObjectKeyFrame>
                    </ObjectAnimationUsingKeyFrames>
                </Storyboard>
            </VisualState>
            <VisualState x:Name="GreenState">
                <Storyboard Duration="0:0:0">
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="(UIElement.Visibility)">
                        <DiscreteObjectKeyFrame KeyTime="0:0:0">
                            <DiscreteObjectKeyFrame.Value>
                                <Visibility>Visible</Visibility>
                            </DiscreteObjectKeyFrame.Value>
                        </DiscreteObjectKeyFrame>
                    </ObjectAnimationUsingKeyFrames>
                    <DoubleAnimation 
                        Duration="0:0:0"
                        Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationY)"
                        From="0" To="-80"/>
                    <DoubleAnimation 
                        Duration="0:0:0"
                        Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)"
                        From="0" To="-205"/>                            
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="(UIElement.IsHitTestVisible)">
                        <DiscreteObjectKeyFrame KeyTime="0:0:0">
                            <DiscreteObjectKeyFrame.Value>false</DiscreteObjectKeyFrame.Value>
                        </DiscreteObjectKeyFrame>
                    </ObjectAnimationUsingKeyFrames>
                </Storyboard>
            </VisualState>
        </VisualStateGroup.States>
     </VisualStateGroup>
   </VisualStateManager.VisualStateGroups>

The collapsed state only concerns itself with one thing: being collapsed. The visible state ensures it is both visible and hit test visible so the surface is clickable. In the special state with the green slide-in, the control is visible and rotated in a plane projection. It also is not hit test visible, so the "sideways buttons" can't be clicked. Notice that all of the storyboards have a duration of 0. This is the precise set of values we want imposed on our visual when the control is in different states.

Two: Transitions can animate whatever you like, but they should always end up in the same state as the state definition being transitioned to.

Transitions provide quite a bit of flexibility. What's important to understand when dealing with states, however, is that there are two ways to programmatically drive states in Silverlight. You can go to the state with transitions, or without. In all cases, Silverlight will fire the state animation. In the case of transitions, it will fire the transition animation, and then immediately fire the state animation after the transition storyboard completes. The VSM leaves the state storyboard playing after the transition is complete, but will stop the transition storyboard.

If the states and transitions don't match, your control will be left in an unknown state - especially if code has the flexibility to use transitions (or not). Further, if a property is left in a different state by the state animation than the transition animation, only one storyboard can "win" so you always want the animations to conclude on the properties you desire.

The easiest way to make a transition work is to simply specify the states you are transitioning from and to, and provide a generated duration. The visual state manager will modify the storyboard in the state definition to last for the duration, and smoothly animate the properties.

If you want to manipulate other factors, such as a projection, or have key frames, then you must use the transitions. If you are using transitions that don't overlap the state, those animations will stop after the transition is complete and there will be no conflict. Animations that overlap the existing properties in the state storyboard should end with the same values to avoid any conflicts.

Take a look at the transitions on our RedView:

<VisualStateGroup.Transitions>
    <VisualTransition To="GreenState" GeneratedDuration="0:0:1">
        <Storyboard Duration="0:0:1">
            <DoubleAnimation 
                Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationY)"
                From="0" To="-80"/>
            <DoubleAnimation 
                Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)"
                From="0" To="-205"/>                            
        </Storyboard>
    </VisualTransition>
    <VisualTransition From="GreenState" To="ShowState" GeneratedDuration="0:0:1">
        <Storyboard Duration="0:0:1">
            <DoubleAnimation 
                Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationY)"
                From="-80" To="-0"/>
            <DoubleAnimation 
                Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)"
                From="-205" To="0"/>
        </Storyboard>
    </VisualTransition>
    <VisualTransition From="ShowState" To="HideState" GeneratedDuration="0:0:1">                        
        <Storyboard Duration="0:0:1">
            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="(UIElement.Visibility)">
                <DiscreteObjectKeyFrame KeyTime="0:0:0">
                    <DiscreteObjectKeyFrame.Value>
                        <Visibility>Visible</Visibility>
                    </DiscreteObjectKeyFrame.Value>
                </DiscreteObjectKeyFrame>
            </ObjectAnimationUsingKeyFrames>
            <DoubleAnimation Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="(UIElement.Opacity)"
                                From="1" To="0"/>
        </Storyboard>
    </VisualTransition>
    <VisualTransition From="HideState" To="ShowState" GeneratedDuration="0:0:1">                       
        <Storyboard Duration="0:0:1">
            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="(UIElement.Visibility)">
                <DiscreteObjectKeyFrame KeyTime="0:0:0">
                    <DiscreteObjectKeyFrame.Value>
                        <Visibility>Visible</Visibility>
                    </DiscreteObjectKeyFrame.Value>
                </DiscreteObjectKeyFrame>
            </ObjectAnimationUsingKeyFrames>
            <DoubleAnimation Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="(UIElement.Opacity)"
                                From="0" To="1"/>
        </Storyboard>
    </VisualTransition>
</VisualStateGroup.Transitions>

Notice that the animations start by setting the control to visible. This is because of the order I mentioned earlier: the transitions fire, then the states. Therefore, if you want to animate a panel into view, you need to make it visible first in case it was collapsed before, or your animation simply won't be visible. The animations may perform different effects but they should leave the control in the "target" state we are looking for. The end result is that changing states with or without transitions should end up with the same result - again, you can easily mess this up so it's important to keep in mind.

Firing States in Jounce

Jounce provides several mechanisms to fire visual states. One of the most powerful is through the Visual State Aggregator (VSA). The VSA allows you to coordinate states completely in XAML. In a nutshell, you create a "transaction" or an "event" and give it a name. In any control that should respond to the event, you add a subscription and indicate what state the control should go to when the event is raised, and whether or not to use transitions. Here are the subscriptions in the RedView:

<Interactivity:Interaction.Behaviors>
    <Views:VisualStateSubscriptionBehavior EventName="NavigateGreen" StateName="GreenState" UseTransitions="True"/>
    <Views:VisualStateSubscriptionBehavior EventName="NavigateRed" StateName="ShowState" UseTransitions="True"/>
    <Views:VisualStateSubscriptionBehavior EventName="NavigateBlue" StateName="HideState" UseTransitions="True"/>
</Interactivity:Interaction.Behaviors>

As you can see, when the "NavigateGreen" event is raised, the control goes to the "GreenState." Likewise, a "NavigateRed" (the view itself) goes to the "ShowState" and a "NavigateBlue" (a different view) goes to the "HideState."

To fire events, you can either publish them programmatically or use a trigger. In our RedView we have two triggers. One button for the green slide-in simply publishes the event. That's because the GreenView is static text and doesn't require a view model. Another button for the blue view fires both an event to transition and a navigation event to wire in the view model:

<Button HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="10" Content=" GREEN ">
    <Interactivity:Interaction.Triggers>
        <Interactivity:EventTrigger EventName="Click">
            <Views:VisualStateAggregatorTrigger EventName="NavigateGreen"/>
        </Interactivity:EventTrigger>
    </Interactivity:Interaction.Triggers>
</Button>
<Button HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="10" Content=" BLUE ">
    <Interactivity:Interaction.Triggers>
        <Interactivity:EventTrigger EventName="Click">
            <Views:VisualStateAggregatorTrigger EventName="NavigateBlue"/>
            <Services:NavigationTrigger Target="BlueView"/>
        </Interactivity:EventTrigger>
    </Interactivity:Interaction.Triggers>
</Button>

To demonstrate the difference between manipulating views and actually navigating, the RedView updates a date on the screen. You'll notice when you navigate to the BlueView and back, the date changes. When you navigate to the green slide-in and back, the date does not. This is because the green slide-in is simply a visual state transition, while the BlueView is both a transition and an actual navigation event.

Visual States and View Models in Jounce

Obviously, having multiple screens or dynamically loaded ones will prevent you from wiring every possibility in the XAML. When Jounce wires a view to a view model, it sets a delegate that allows you to raise state changes (and automatically marshalls them to the UI threads). This allows view models to handle states. In fact, the generic IViewModel interface supports the visual state methods. Typically, a controller will handle navigation through wizards and selections, and raise the navigation events as described in previous Jounce posts. The view model can then transition to the correct state when the _Active method is called. Because view models may have multiple views, the view tag is passed in _Activate and the state transition can be targetted to that view. This allows Jounce to manipulate states from the view model only knowing what the name of the state and optionally the tag of the view is, without having a direct reference to the view or any view artifacts.

The BlueView takes advantage of this and instead of using behaviors and triggers, manages visual states 100% programmatically. Here is the full code for the BlueViewModel:

[ExportAsViewModel(Globals.VIEWMODEL_BLUE)]
public class BlueViewModel : BaseViewModel, IBlueViewModel  
{
    public BlueViewModel()
    {
        Dates = new ObservableCollection<string>();
        RedCommand = new ActionCommand<object>(o=>_RedAction());     
    }
       
    public ObservableCollection<string> Dates { get; private set; }

    public IActionCommand RedCommand { get; private set; }

    [Import]
    public VisualStateAggregator VsmAggregator { get; set; }

    private void _RedAction()
    {
        GoToVisualState("HideState", true);
        VsmAggregator.PublishEvent("NavigateRed");
        EventAggregator.Publish(Globals.VIEW_RED.AsViewNavigationArgs());
    }

    public override void _Initialize()
    {
        Dates.Add(string.Format("Initialized: {0}", DateTime.Now));
        GoToVisualState("HideState", false);
    }

    public override void _Activate(string viewName)
    {
        Dates.Add(string.Format("Activated: {0}", DateTime.Now));
        GoToVisualState("ShowState", true);
    }        
}

Notice that when the view model is initialized, it goes into a default hidden state and does not use transitions. Initialize is always called before activate. Once activated, it logs the activation and goes to the "ShowState." The button for the RedView fires a command that changes the state of the BlueView, publishes the event for the red navigation that the visual state aggregator listens to, and finally navigates to the RedView.

Manipulating Visual States Directly

In Silverlight 4 you can tap into events and transitions and inspect the various states. You can even get to the storyboards associated with states. Because states are actual storyboards, you can do things like attach behaviors or programmatically access the storyboards and manipulate them to do things like accommodate the current screen resolution or compute complex animations.

Whenever a transition happens to the RedView, a message is displayed. This message is handled in code-behind that hooks into the VSM and latches onto the state transition events. Take a look at the code:

public RedView()
{
    InitializeComponent();
    Loaded += RedView_Loaded;            
}

void RedView_Loaded(object sender, RoutedEventArgs e)
{
    var groups = VisualStateManager.GetVisualStateGroups(LayoutRoot);
    foreach(var group in groups.Cast<VisualStateGroup>().Where(g=>g.Name.Equals("NavigationStates")))
    {
        group.CurrentStateChanged += GroupCurrentStateChanged;                
    }
}

static void GroupCurrentStateChanged(object sender, VisualStateChangedEventArgs e)
{
    JounceHelper.ExecuteOnUI(() => MessageBox.Show(string.Format("Transition {0}=>{1}", e.OldState.Name, e.NewState.Name)));
}

The code grabs the group called NavigationStates, hooks into the state change event, and then uses a message box to publish the transition. To see how to dive even deeper and access storyboards, read Page Brooks' excellent article Finding a Storyboard in the Visual state Manager.

Hosting the Views

The final step is hosting the views. Instead of using a ContentControl, the easiest way to allow views to transition in and out without including a third-party control is use an ItemsControl and override the panel to a Grid, like this:

<ItemsControl HorizontalAlignment="Center" HorizontalContentAlignment="Stretch"
                Width="480" Height="300"
                VerticalAlignment="Center" VerticalContentAlignment="Stretch"
                Regions:ExportAsRegion.RegionName="MainRegion">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Grid/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>

Notice this is a region that views can be routed to. It allows the views to stack on top of each other, and then transition in and out based on the visual states. In Jounce by default, all views are tracked and simply activate or deactivate. The main difference between a ContentControl and an ItemsControl is that in one, the view is completely removed, and in the other, it is collapsed. So what does this look like? The views will remain in the items control but in their collapsed state have no impact on the visual tree. I've used this method with dozens of views and have not noticed any significant, negative impact as the "hidden" views are truly collapsed. Obviously if you are using transient views, then a different strategy such as a TransitioningContentControl should be used instead.

You can download the source to view the example here (simply choose the download link in the upper right for the latest version).

Jeremy Likness

Saturday, January 15, 2011

Sterling Silverlight and Windows Phone 7 Database Triggers and Auto-Identity

Sterling changeset 72063 [browse the source code here] introduces triggers. The trigger is a powerful feature that allows you to universally intercept database operations. Their application in Sterling resolves several concerns, including:

  • Validation — prevent a save operation from succeeding if data integrity is compromised, or prevent a delete operation when prerequisites are not met
  • Data-specific concerns — should the business layer be concerned with a "last modified date" that relates to the database? Use a trigger to set the date consistently without involving other pieces of your application
  • Post-save processing (for example, clear a "dirty flag" once the item is persisted)

Declaring the Trigger

Declaring the trigger in Sterling is straightforward. Every entity that is persisted by sterling is defined by a combination of the type and the key type. The trigger is no different. Internal to Sterling, a basic interface allows the database engine to manage triggers without having to close the generic type:

internal interface ISterlingTrigger
{
    bool BeforeSave(Type type, object instance);
    void AfterSave(Type type, object instance);
    bool BeforeDelete(Type type, object key);
}

Next, we want a more strongly typed version for definitions and for external sources that are aware of the type. This allows those interfaces to close the generic and work with a strongly typed interface:

internal interface ISterlingTrigger<T, TKey> : ISterlingTrigger where T: class, new() 
{
    bool BeforeSave(T instance);
    void AfterSave(T instance);
    bool BeforeDelete(TKey key);
}

Side Note: Covariance and Contravariance

Wikipedia has a good article explaining covariance and contravariance. For their application in C#, read the related Microsoft article. In C# you can declare types in generics as co- and contra- variant using the "in" and "out" keywords. The original interface definition used this until I realized they are no-go on the phone. Basically, the IDE and compiler will allow you to define them, but at runtime they fail with a type load exception.

Defining a Trigger

You may have noticed that the interfaces are internal to Sterling. That's because the way to define a trigger is through the base class that looks like this:

public abstract class BaseSterlingTrigger<T,TKey> : ISterlingTrigger<T,TKey> where T: class, new()
{
    public bool BeforeSave(Type type, object instance)
    {
        return BeforeSave((T) instance);
    }

    public void AfterSave(Type type, object instance)
    {
        AfterSave((T) instance);
    }
       
    public bool BeforeDelete(Type type, object key)
    {
        return BeforeDelete((TKey) key);
    }

    public abstract bool BeforeSave(T instance);

    public abstract void AfterSave(T instance);

    public abstract bool BeforeDelete(TKey key);
        
}

This is a common pattern I use when working with generics. The internal engine wants to deal with the object and the type, while the externals want to close the generic. In order to provide a contract to deal with a typed entity and keep the developer from worrying about any conversion, I can use the abstract class to overload from the non-typed to the typed version. The casting is very inexpensive compared to the reflection that would have to happen to manually invoke methods and close the generics in the core database engine.

The TriggerTest included with the Sterling project demonstrates the definition of a trigger:

public class TriggerClassTestTrigger : BaseSterlingTrigger<TriggerClass, int>
{
    public const int BADSAVE = 5;
    public const int BADDELETE = 99;

    private int _nextKey;
        
    public TriggerClassTestTrigger(int nextKey)
    {
        _nextKey = nextKey;
    }

    public override bool BeforeSave(TriggerClass instance)
    {
        if (instance.Id == BADSAVE) return false;
            
        if (instance.Id > 0) return true;

        instance.Id = _nextKey++;                       
        return true;
    }

    public override void AfterSave(TriggerClass instance)
    {
        instance.IsDirty = false;
    }

    public override bool BeforeDelete(int key)
    {
        return key != BADDELETE;
    }
}

This trigger does a few things. It takes in a key and stores that value. In the case of integer identity fields, for example, the portion of your code that initializes the database can perform a query to find the maximum key that exists. You can then increment the value and pass it to the trigger class, which will auto-set the identity for new entities (assuming anything without a positive non-zero id is new) and keep track of the next key.

There is an arbitrary validation that returns false if the id is an explicit value. The Sterling database engine will throw a SterlingTriggerException if the BeforeSave or BeforeDelete methods return false, preventing the class from being persisted. This is meant as a last resort, as exceptions are expensive and your code should validate these conditions and prevent them prior to saving.

In the AfterSave you can see the example of automatically clearing the dirty flag.

Registering the Trigger

Registering the trigger is straightforward. Triggers can be registered anytime after the database is activated (this allows you to query the database and preset and trigger conditions prior to activating them). They can also be unregistered. Triggers in Sterling are more like inceptors and may be transient, rather than traditional relational database triggers which can be thought of as part of the table definition itself.

The following code demonstrates a pattern for handling auto-identity fields. It activates the Sterling engine, registers and activates the database, queries for the highest key value and then registers the trigger using the last known key:

_engine = new SterlingEngine();
_engine.Activate();
_databaseInstance = _engine.SterlingDatabase.RegisterDatabase<TriggerDatabase>();

var nextKey = _databaseInstance.Query<TriggerClass, int>().Any() ?
    (from keys in _databaseInstance.Query<TriggerClass, int>()
        select keys.Key).Max() + 1 : 1;

_databaseInstance.RegisterTrigger(new TriggerClassTestTrigger(nextKey));

Afterword: Windows Phone 7 Tests

As of this blog date an interesting issue exists that the Sterling team is investigating. Triggers appear to work perfectly fine on the Windows Phone 7 and the sample project has been updated to use a trigger. However, when the trigger is included in the unit tests for the phone, the unit test harness initializes but fails to run any tests. This is true when simply the definition of the trigger class is provided.

I will follow up once we determine the cause. It appears to be an issue with the unit test framework and I suspect something happens when it scans the types in the assembly to find tests. For now we have the unit test disabled on the phone but will update you once the cause is found and rectified.

Sterling is getting closer to versoin 1.0 RTM. The remaining pieces include a similar trigger-like architecture for intercepting the byte streams to enable encryption and compression or other manipulation, and streams to expose a backup and restore mechanism.

Visit Sterling online at http://sterling.codeplex.com/.

Jeremy Likness

Saturday, January 8, 2011

Jounce Part 9: Static and Dynamic Module Management

One popular feature in the widely used Prism Framework is the concept of "modules." A prism module is a functional unit that represents a set of related concerns. These concerns can be coded and tested independently and then composed into larger applications. They can also handle dependencies with other modules and be discovered and dynamically loaded.

While the concept worked well for WPF and prior versions of Silverlight, Jounce takes a slightly different approach that recognizes and uses two features that are built into Silverlight. The first is the concept of a XAP file, which is nothing more than a ZIP file with a manifest that lists resources and contains the resources themselves (typically DLLs, images, embedded fonts and data). Jounce treats XAP files as the perfect medium to encapsulate a module within Silverlight. The second feature is the MEF DeploymentCatalog which allows Silverlight to dynamically load and compose parts from a XAP file.

Creating a Module

To create a module with Jounce is incredibly simple: fire up Visual Studio and create a new Silverlight Application. That is a module!

There will always be exactly one module that is a "host" module. This module must contain the App.xaml.cs code and the initial reference to the Jounce ApplicationService in order to start the application and initialize the Jounce framework. Additional modules may be standalone. For those modules, you can simply create a new application and then delete the App.xaml and MainPage.xaml files that are auto-generated by the template. Instead, you can begin defining views, view models, or other class that you simply export. When the module is loaded by Jounce, these will be automatically imported and composed into the main application.

An example of this is in the Jounce "DynamicXapCalculator" quickstart. The main module is a typical Silverlight application. It defines a display that contains a row to enter digits, a row of "commands" and an "equals" row. It is designed to function like a calculator, but the buttons are not pre-defined. This is the XAML for the button row:

<ItemsControl Grid.Row="2" HorizontalAlignment="Center" ItemsSource="{Binding Commands}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Horizontal"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Button Width="50" Height="50" Margin="5" Content="{Binding Item1}" Command="{Binding Item2}"/>
        </DataTemplate>                    
    </ItemsControl.ItemTemplate>
</ItemsControl>

A quick peek at the view model demonstrates how the Managed Extensibility Framework (MEF) is used to compose the commands. The definition is a list of tuples that represent the display and an ICommand implementation:

[ImportMany(AllowRecomposition = true)]
public ObservableCollection<Tuple<string,ICommand>> Commands { get; set; }

The view model itself exports two implementations of this construct: one for addition, and one for subtraction.

[Export]
public Tuple<string,ICommand> AddCommand
{
    get
    {
        return Tuple.Create("+",
                            (ICommand)new ActionCommand<object>(
                                obj => ExecuteCommand((oldValue, newValue) => oldValue + newValue)));
    }
}

[Export]
public Tuple<string, ICommand> SubstractCommand
{
    get
    {
        return Tuple.Create("-",
                            (ICommand)new ActionCommand<object>(
                                obj => ExecuteCommand((oldValue, newValue) => oldValue - newValue)));
    }
}

The view model itself keeps tracks of values, so the commands simply access those existing values and manipulate those. When you run the program, you are presented with a calculator that you can use to add and subtract.

The idea is that the calculator can be expanded by modules. Other modules might address scientific notation, exponents, factorials, trigonometry functions, or other areas that can be independently built and tested, then merged into the application. For our simple example, the external module supplies code for division and multiplication.

As I wrote earlier, creating the new module (the poorly spelled DyanmicXapCalculatorAdvanced) started with a new Silverlight application. The App.xaml and MainPage.xaml files were deleted and a new class added to export the new commands.

public class AdvancedFunctions
{        
    [Import]
    public Action<Func<int, int, int>> ExecuteCommandImport { get; set; }

    [Export]
    public Tuple<string, ICommand> AddCommand
    {
        get
        {
            return Tuple.Create("*",
                                (ICommand)new ActionCommand<object>(
                                    obj => 
                                        ExecuteCommandImport((oldValue, newValue) => oldValue * newValue)));
        }
    }

    [Export]
    public Tuple<string, ICommand> SubstractCommand
    {
        get
        {
            return Tuple.Create("/",
                                (ICommand)new ActionCommand<object>(
                                    obj =>
                                            ExecuteCommandImport((oldValue, newValue) => newValue == 0 ? 0 : oldValue / newValue)));
        }
    }
}

Notice that the actual "execution" structure that references the values the view model is holding can be imported as a generic action in this module that the exported commands call. This is "bound" by the export in the original module, which exports the method that handles the command executions:

[Export]
public Action<Func<int,int,int>> ExecuteCommandExport
{
    get { return ExecuteCommand; }
}

The syntax above works because ExecuteCommand has the signature specified by the action being exported.

Dynamically Loading Modules

The calculator must have some way to bring in the dynamic module. In this case, it will do so directly. It does this by exposing a load button wired to a load command that indicates the user is ready to bring in the additional functionality. There are two simple steps to load a dynamic module in Jounce. First, reference the IDeploymentService:

[Import]
public IDeploymentService Deployment { get; set; }

Second, call the RequestXap method and pass the URI of the XAP resource. In the example, it is contained in the same ClientBin directory as the host application, so we can use a relative reference. The method has two overrides. One is a "blind" call or "fire and forget." The second will call a delegate once the module is loaded and pass in an Exception object in case something went wrong. For this example, any errors are ignored. If it succeeds, a loaded flag is set and the command for loading notified, so it can be disabled to prevent the user from trying to load it again (this would just be redundant: Jounce keeps track of dynamic modules and won't try to load the same module more than once).

private void _Load()
{
    Deployment.RequestXap("DyanmicXapCalculatorAdvanced.xap",ex=>
                                                                    {
                                                                        if (ex == null) return;
                                                                        _loaded = true;
                                                                        ((IActionCommand)LoadCommand).RaiseCanExecuteChanged();
                                                                    });

}

That's all there is to it. Run the application and click the load button. You will see that the new operators instantly appear and allow you to perform division and multiplication as well as addition and subtraction.

Routed Modules

Of course, explicitly loading modules isn't required. It may be that the module load is triggered as a part of navigation. In that case, you can simply route the views that a module contains to the XAP file for the module, and Jounce will automatically load the dynamic module when it is required. An example of this is in the RegionManagement quick start. This quickstart illustrates how to use Jounce to wire views into regions. In the code-behind for the Square view, you'll see an exported route that maps the view tagged "Dynamic" with the module "RegionManagementDynamic.xap":

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

The view that is tagged "Dynamic" actually exists in a completely separate XAP file (RegionManagementDynamic.xap). You can also create routes on the fly at runtime. This example will do the same thing:

[Import]
public ViewRouter ViewRouter { get; set; }

public ConfigureRoute()
{
    ViewRouter.RouteViewInXap("Dynamic", "RegionManagementDynamic.xap");
}

When the navigation event for the view is fired, Jounce will automatically determine whether the module has been loaded and, if not, will fetch it dynamically. This happens in the click event for the dynamic button:

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

Module Initialization

When Jounce loads a dynamic module, it passes it to a special MEF catalog known as a DeploymentCatalog. MEF processes the parts in the module and recomposes any imports that can take advantage of the new exports. In the calculator example, MEF will take the command export from the host application and inject it in the advanced functions type, then find the command exports and bring those back into the calculator view model.

When you run the region management example, you'll notice after clicking the dynamic button, a message pops up indicating the module has loaded. This is done in the dynamic module using the module initializer. Any module in Jounce can provide code to be called when new modules are loaded. To take advantage of this, simply implement IModuleInitializer in one or more of your classes and export it. The module initializer may be used in the main module as well, and will be called after all of the composition is completed after the application first starts.

The interface declares a property to track if the module is initialized, and a method to call for initialization. Typically, you would implement the method and set the initialized flag to "true." If you want your module to be notified whenever any new module is loaded, however, you can leave the flag unset and it will be called each time until you set the flag. This is useful if your module must inspect certain properties or fields and respond when plug-ins or other extensions are loaded.

The implementation in the region management example is simple:

[Export(typeof(IModuleInitializer))]
public class ModuleInit : IModuleInitializer 
{
    public bool Initialized { get; set; }
        
    public void Initialize()
    {
        JounceHelper.ExecuteOnUI(()=>MessageBox.Show("Dynamic module loaded."));
        Initialized = true;
    }
}

As you can see, it simply displays a message and then sets the flag.

Summary

The Prism concept of modules is extremely powerful and provides multiple means to configure and discover modules, as well as multiple methods to load them. You can read the Prism documentation about modules here. Jounce takes a more simple and native Silverlight approach by encapsulating modules in XAP files and using the deployment service to load the modules. This is intended to make it quick and easy, but provide powerful flexibility. For example, you could easily expose a web service that provides names for plug-ins and then use the deployment service to dynamically load them, or scan a directory for plug-ins on the server, inject the names in the initParams collection and create routes at runtime to allow the user to select and load modules. Providing any "bootstrap" code you need for your module is as easy as implementing the IModuleInitializer interface and exporting it.

Jeremy Likness