Monday, November 29, 2010

Silverlight and WCF Service Consumption: Event vs. APM

I've blogged several times about how I like to handle services in Silverlight. For two key posts, take a look at:

In this post we'll explore the difference between using the actual contract for a WCF service in Silverlight versus using the generated client. The difference is subtle but important, and involves not only the event-based model and the Asynchronous Programming Model (APM) but also some nuances with threads.

There is no code project for this because the nuance is in the way you call the service, not the example itself, but there should be enough code in this post for you to recreate the example if you want to test it yourself.

Here's the rub: let's create a simple service that adds two integers and returns the result. The contract is simply:

[ServiceContract(Namespace = "http://jeremylikness.com/silverlight/")]
public interface IAddService
{
    [OperationContract]
    int Add(int x, int y);
}

Now we can implement it - just add the numbers and return the value:

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class AddService : IAddService
{        
    public int Add(int x, int y)
    {
        return x + y;
    }        
}

So that's easy enough (and we've jumped through our first Silverlight hoop by flagging the service with the compatibility attribute). Now we'll fire up a new Silverlight application and reference the service. I'm not doing anything fancy, just discovering the service within the project and letting Silverlight generate the code. Now things get a little more interesting.

Let's add two list boxes that will race each other for results. They are bound to a simple list of integers:

<UserControl.Resources>
    <DataTemplate x:Key="ListTemplate">
        <TextBlock Text="{Binding}"/>
    </DataTemplate>               
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="1*"/>
        <ColumnDefinition Width="1*"/>
    </Grid.ColumnDefinitions>
    <TextBlock Text="Events"/>
    <TextBlock Text="APM" Grid.Column="1"/>
    <ListBox x:Name="Events" ItemsSource="{Binding Series1}" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Top"
                ItemTemplate="{StaticResource ListTemplate}"/>        
    <ListBox x:Name="APM" ItemsSource="{Binding Series2}" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Top"
                ItemTemplate="{StaticResource ListTemplate}"/>          
</Grid>

As you might have already guessed, we're going to use the event model and the APM model. Let's wire some code.

First, two collections to hold the results. I also bind the data context to itself so I can bind to the series in the code-behind.

private const int MAX = 999999999;

public ObservableCollection<int> Series1 { get; private set; }
public ObservableCollection<int> Series2 { get; private set; }      

public MainPage()
{
    InitializeComponent();

    Loaded += (o, e) => LayoutRoot.DataContext = this;

    Series1 = new ObservableCollection<int>();
    Series2 = new ObservableCollection<int>();

    if (DesignerProperties.IsInDesignTool) return;

    _Series1(); 
    _Series2();
}

The Main Event

Now let's implement a Fibonacci sequence using the event-based model:

private void _Series1()
{
    var y = 1;
    var x = 1;

    var client = new AddServiceClient();

    Series1.Add(1);
    client.AddCompleted += (o, e) =>
                                    {
                                        x = e.Result;

                                        Series1.Add(x);            

                                        var z = y;
                                        y = x;

                                        if (x < MAX)
                                        {
                                            client.AddAsync(x, z);
                                        }                                             
                                    };
    client.AddAsync(x, y);
}

Notice how we recursively call the service. What is also important to note, however, is that there is no special use of the dispatcher. I know for a fact this will come back to me on the UI thread. Why? Because I registered the event on the UI thread, and that registration is where the return call will go. I can prove it because despite the fact the series is databound to the UI, it populates without any issue.

Switching to the Asynchronous Programming Model

Now, let's wire the same service using the Asynchronous Programming Model (APM). I'm going to kick it off the same way and in the same context as the event-based version. Notice that because Silverlight creates the client proxy using the event-based model, I actually have to cast the client to the interface in order to take advantage of the APM model.

The APM call adds two methods. One is an AsyncCallback delegate that will be called when the service is done fetching results, and the other is an optional object to store state (this is passed to the callback so you can reference data from the original call).

private void _Series2()
{            
    // add the first item
    Series2.Add(1);

    var client = (IAddService) new AddServiceClient();

    AsyncCallback end = null;
    end = ar =>
                            {
                                var state = ar.AsyncState as Tuple<IAddService, int>;
                                if (state == null) return;
                                var x = state.Item1.EndAdd(ar);
                                var y = state.Item2;

                                Deployment.Current.Dispatcher.BeginInvoke(() => Series2.Add(x));

                                if (x < MAX)
                                {
                                    state.Item1.BeginAdd(x, y, end, Tuple.Create(state.Item1, x));
                                }
                            };

    client.BeginAdd(1, 1, end, Tuple.Create(client,1));            
}          

There's a bit going on there - I preserve the original client along with the current value in the state by casting them both into a tuple, and unroll them in the callback. The call is recursive until the max is reached, just like the event model, and using an anonymous method. However, there is a subtle difference between the AddAsync method we called earlier and the BeginAdd method here.

The difference? Our "end" callback is not invoked on the UI thread! The APM model lets the result come back on a different thread. If you take out the dispatcher call I use to populate the series and add the value directly, you'll get a cross-thread exception.

So what does this mean?

Most of the time, quite frankly, not much. In my tests, the bulk of the time for the process to run is taken in the service call going over the wire and coming back, and there is little noticeable difference between staying on the UI thread or not (even on a local machine).

However, if you are receiving a result and doing heavy processing before displaying the data, you don't want to return on the UI thread. It's wasteful and can causing blocking, the exact thing the asynchronous pattern is supposed to avoid! While you could send your work to a background worker, the easiest solution is to use the APM model. You'll return on a background thread where you can do all of your heavy lifting and simply dispatch the result when you're ready.

Bottom line: when using services in Silverlight, know where your results land!

Jeremy Likness

Monday, November 15, 2010

Jounce Part 7: Validation and Save/Confirm/Cancel Operations

Line of business applications are full of what we call "CRUD" screens for Create, Read, Update, and Delete. Jounce addresses common concerns that are found in these types of transactions to help with your Silverlight 4 DataForms:

Is Dirty and Commit

It is common to check the status of a record and only apply changes when changes actually exist. Once a user edits a field, it is marked as "dirty" and this is the trigger to begin considering updates. Jounce uses the inverse of the "dirty" flag: the "committed" flag. When a view model derives from the BaseEntityViewModel class, it inherits several properties including the committed functionality. This includes:

  • Committed — a boolean that is set to false when pending changes that have not been committed exist
  • CommitCommand — a command that you can bind to your save buttons that will automatically remain disabled unless the record is both dirty and passes all validations
  • _OnCommitted — a method that is called when the commit command is executed, allowing you to apply changes

Validation

Validation needs to be performed on fields. Jounce provides access to field-level validation via the ClearErrors and SetError methods. You can specify an error for a property simply by passing the property name and the error text. In addition, Jounce provides an override named _ValidateAll that is called before any commit command is processed. This allows you to perform any entity-level validations (or re-apply existing validations) before the commit cycle is triggered. The HasErrors boolean is set to true if any validation errors exist.

Let's take a look at the sample EntityViewModel application that is shipped with the Jounce quick starts (you can download the Jounce source by clicking here and choosing "Download" under the "Latest Version" box in the upper right).

Set up the View Model

We'll start with the view model for a very simple contact record. It will have a first name, a last name, a phone number, and an email. Because this is an entity with validation, we'll derive the view model from the BaseEntityViewModel:

[ExportAsViewModel("MainViewModel")]
public class MainViewModel : BaseEntityViewModel
{
    public MainViewModel()
    {
    }
}

Let's go ahead and create the properties we'll be validating. For each property, we'll call a method for validation of that property. The validation method will first clear any outstanding errors that exist for the given property, then validate. If validation fails, it will call SetError to add the error to the error collection for that property.

private string _firstName;

public string FirstName
{
    get { return _firstName; }
    set
    {
        _firstName = value;
        RaisePropertyChanged(() => FirstName);
        _ValidateName(ExtractPropertyName(() => FirstName), value);
    }
}

private string _lastName;

public string LastName
{
    get { return _lastName; }
    set
    {
        _lastName = value;
        RaisePropertyChanged(() => LastName);
        _ValidateName(ExtractPropertyName(() => LastName), value);
    }
}

private void _ValidateName(string prop, string value)
{
    ClearErrors(prop);

    if (string.IsNullOrEmpty(value))
    {
        SetError(prop, "The field is required.");
    }
}

private string _phoneNumber;

public string PhoneNumber
{
    get { return _phoneNumber; }
    set
    {
        _phoneNumber = value;
        RaisePropertyChanged(() => PhoneNumber);
        _ValidatePhoneNumber();
    }
}

private void _ValidatePhoneNumber()
{
    var prop = ExtractPropertyName(() => PhoneNumber);
    ClearErrors(prop);
    if (string.IsNullOrEmpty(_phoneNumber) || !Regex.IsMatch(_phoneNumber,
                                                                @"^((\+\d{1,3}(-| )?\(?\d\)?(-| )?\d{1,5})|(\(?\d{2,6}\)?))(-| )?(\d{3,4})(-| )?(\d{4})(( x| ext)\d{1,5}){0,1}$"))
    {
        SetError(prop, "Field should be a valid international phone number such as +1 404-555-1212");
    }
}

private string _email;

public string Email
{
    get { return _email; }
    set
    {
        _email = value;
        RaisePropertyChanged(() => Email);
        _ValidateEmail();
    }
}

private void _ValidateEmail()
{
    var prop = ExtractPropertyName(() => Email);
    ClearErrors(prop);
    if (string.IsNullOrEmpty(_email) ||
        !Regex.IsMatch(_email, @"^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$", RegexOptions.IgnoreCase))
    {
        SetError(prop, "Field should be a valid email address.");
    }
}

Notice that our validations are straightforward: for the required fields, we're just checking for the string to be filled with something. For phone number and email, we're using regular expressions and checking for a match to ensure the validation passes.

Global Validation

Before we commit the record, we want to make sure all of our validations pass. We might have record-level validations, service-based validations (i.e. call a service to make sure a name is not duplicate), or validations that haven't fired yet due to the nature of data-binding. With Jounce, it is easy to call these validations because we can simply override the validation method that is called before committing the transaction. If any validations fails, it will not commit.

protected override void _ValidateAll()
{
    _ValidateName(ExtractPropertyName(() => FirstName), _firstName);
    _ValidateName(ExtractPropertyName(() => LastName), _lastName);
    _ValidatePhoneNumber();
    _ValidateEmail();
}

When we commit the record or cancel from editing it, we'll need to clear the fields and reset the commit state (remember, committed goes to false if pending changes exist, but this is not the case when we first enter the transaction). We'll create a short routine that clears the fields and then resets the committed flag:

private void _Reset()
{
    PhoneNumber = string.Empty;
    Email = string.Empty;
    FirstName = string.Empty;
    LastName = string.Empty;
    Committed = true;
}

Handling Commits

Because we're "just pretending" with this example, we'll just clear the transaction when the user saves. How do we handle a save? We simply override the committed method (Jounce guarantees your validations have passed before it will call this method):

protected override void _OnCommitted()
{
    MessageBox.Show("Record was saved.");
    _Reset();
}

Jounce provides the command to commit, but does not make any assumptions about how we want to handle cancellation. We'll wire our own cancel command. It should check to make sure there is something to cancel from (i.e. if the record hasn't been changed, there is no point in performing a cancel). We'll also need to make sure we update the status of the command whenever the commit status changes. Finally, we'll prompt the user and if they confirm, we'll just call our reset routine to clear the record.

Cancel That

Here's the definition of the command:

public IActionCommand CancelCommand { get; private set; }

In the constructor, we'll wire it up. While we're there, let's go ahead and add some design-time data as well.

public MainViewModel()
{
    CancelCommand = new ActionCommand<object>(obj => _Confirm(), obj => !Committed);
    var committedProp = ExtractPropertyName(() => Committed);

    if (InDesigner)
    {
        FirstName = "Jeremy";
        LastName = "Likness";
        PhoneNumber = "+1 404-555-1212";
        Email = "jeremy@jeremylikness.com";
    }
    else
    {
        // anytime the committed status changes, we should re-evaluate the cancel button
        PropertyChanged += (o, e) =>
                                {
                                    if (e.PropertyName.Equals(committedProp))
                                    {
                                        CancelCommand.RaiseCanExecuteChanged();
                                    }
                                };
    }
}

private void _Confirm()
{
    var result = MessageBox.Show("Are you sure?", "Confirm Cancel", MessageBoxButton.OKCancel);
    if (result == MessageBoxResult.OK)
    {
        _Reset();
    }
}

Notice we don't allow cancel unless the record is not committed (meaning there is something pending). We also hook into the property change events for the commit flag so we can update the command status automatically.

The View

That's it - we're done with our view model! Let's wire up the view. One behavior developers find out quickly with Silverlight is the binding behavior of text boxes. The text box binding is typically not updated until the user tabs out of the text box (the text box loses focus) and then only if there was activity inside the text box. This isn't always the ideal behavior - sometimes you want immediate feedback. So, I created a simple behavior that ensures the text box updates the binding immediately:

namespace EntityViewModel
{
    public class TextBoxChangedBehavior : Behavior<TextBox>
    {
        protected override void OnAttached()
        {
            AssociatedObject.TextChanged += AssociatedObject_TextChanged;
        }

        protected override void OnDetaching()
        {
            AssociatedObject.TextChanged -= AssociatedObject_TextChanged;
        }

        static void AssociatedObject_TextChanged(object sender, TextChangedEventArgs e)
        {
            var tb = sender as TextBox;
            if (tb == null) return;
            var binding = tb.GetBindingExpression(TextBox.TextProperty);
            if (binding != null)
            {
                binding.UpdateSource();
            }
        }
    }
}

I'll connect this to the first text box to demonstrate how it works.

First, let's make our view design-time friendly by binding to the view model (remember, we added some sample data in the constructor).

<Grid x:Name="LayoutRoot" 
        HorizontalAlignment="Center" MinWidth="400"
        Background="White" d:DataContext="{d:DesignInstance local:MainViewModel,IsDesignTimeCreatable=True}">

When we're done, we'll end up with nice design-time data like this:

Jounce design-time friendly data

Let's take a look at our text box for first name:

<TextBox HorizontalAlignment="Stretch" x:Name="FirstName"
            Grid.Column="1"
            Text="{Binding FirstName, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True}">
    <Interactivity:Interaction.Behaviors>
        <local:TextBoxChangedBehavior/>
    </Interactivity:Interaction.Behaviors>
</TextBox>

The important things to note are the ValidatesOnDataErrors and the NotifyOnValidationError. These directives instruct Silverlight to inspect the error properties on our view model and to show error messages accordingly. These are handled via the SetError methods that we used. Also note the interaction block that I use to attach the changed behavior so this text box will update its bindings immediately.

Our buttons are bound to the commands for commit (supplied by Jounce) and cancel (the one we made):

<Button Content="Save" Command="{Binding CommitCommand}" Grid.Row="4" Grid.Column="0"/>
<Button Content="Cancel" Command="{Binding CancelCommand}" Grid.Row="4" Grid.Column="1"/>

Finally, we'll add a validation summary control. This comes with the toolkit and automatically displays a summary of all validation errors:

<Controls:ValidationSummary Grid.Row="5" Margin="5" Grid.ColumnSpan="2"/> 

That's really it! We export the view and view model along with a binding, and we're good to go. What you should notice is that the save and cancel buttons are automatically disabled when you enter the application. Once you type a character in the first name, both are enabled. However, if you click save without entering the other fields, the validations will fire and prevent the save from happening. The save button will remain disabled until you clear all errors. Once you save or cancel, the record will reset. Here's an example of the validation that is generated:

Jounce validations

Remember, the source for this is included with the quickstarts on the Jounce CodePlex page, and if you want to see the end result, look no further:

Jeremy Likness

Wednesday, November 10, 2010

Jounce Part 6: Visual States and Multiple Views per ViewModel

I knocked out quite a few items for the next release of Jounce, the primary being explicit support for multiple views bound to the same view model. In the previous version, the visual state binding would only reflect the most recent view (and a bug prevented the actual binding of the view model, but that's been fixed).

The changes are targeted for release 1.0, but you can download the latest now simply by navigating to the Source Code tab and then clicking "download" in the upper right under "Latest Version."

That same download addresses several items I received requests for.

Refactoring the Simple Navigation to use Regions

While I've provided examples of navigation with regions and without, the region version was very different than the simple navigation example that was without regions. Someone suggested that I build an example to show the same solution, but using regions. I decided to do that and a little more ... but first, let's focus on how I refactored the SimpleNavigation example to demonstrate SimpleNavigationWithRegion.

First, I was able to take out the bindings and code that were explicitly placing the controls because the region management takes it over. If you recall, creating a region is as simple as tagging it:

   <ContentControl Grid.Row="0" Regions:ExportAsRegion.RegionName="NavigationRegion"/>
        <ItemsControl Grid.Row="1" Regions:ExportAsRegion.RegionName="ShapeRegion">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Grid/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>

Notice what I did for the shape region: I used an items control instead of a content control, but overrode the panel to be a Grid. This will force the views routed to the region to take up the same space, but there is a method to my madness ... I'll explain in a bit.

In the views, we place a simple tag to route the region:

[ExportViewToRegion("RedSquare", "ShapeRegion")]
public partial class RedSquare ...

The ShellViewModel no longer has to manage the navigation region or maintain state. In fact, the only thing it needs to do is raise a navigation event for the navigation region so the navigation controls will show. Here is what it ends up looking like:

[ExportAsViewModel("Shell")]
public class ShellViewModel : BaseViewModel, IPartImportsSatisfiedNotification
{       
    public void OnImportsSatisfied()
    {           
        EventAggregator.Publish(new ViewNavigationArgs("Navigation"));
    }               
}

The old navigation view used a command to fire navigation. The latest Jounce features a NavigationTrigger that you can use to trigger navigation from XAML. In this case, we bind it the view name that we build from the meta data for the views:

<ItemsControl.ItemTemplate>
    <DataTemplate>
        <Button Margin="5" Content="{Binding Item2}" ToolTipService.ToolTip="{Binding Item3}">
            <Interactivity:Interaction.Triggers>
                <Interactivity:EventTrigger EventName="Click">
                    <Services:NavigationTrigger Target="{Binding Item1,FallbackValue=Navigation}"/>
                </Interactivity:EventTrigger>
            </Interactivity:Interaction.Triggers>
        </Button>
    </DataTemplate>
</ItemsControl.ItemTemplate>

The "item1" etc comes from the tuple I create by parsing meta data to get the button names and tool tips. The behavior now handles firing the navigation event for me, and because the views are wired to the region, the region manager will place them on the control surface.

Multiple Views per ViewModel

The original Jounce supported binding multiple view models to a view, but a bug prevented the second instance from being bound. Not only is this fixed, but I also improved the support for the visual state manager. We'll cover that in a second. First, I wanted to make a view model called ShapeViewModel to handle some transitions. The same view model will be bound to all of the shape controls. To do this, I simply created a simple Binding class and exported all of the bindings:

public class Bindings
{
    [Export]
    public ViewModelRoute Circle
    {
        get { return ViewModelRoute.Create("ShapeViewModel", "GreenCircle"); }
    }

    [Export]
    public ViewModelRoute Square
    {
        get { return ViewModelRoute.Create("ShapeViewModel", "RedSquare"); }
    }

    [Export]
    public ViewModelRoute Text
    {
        get { return ViewModelRoute.Create("ShapeViewModel", "TextView"); }
    }
}

Jounce handles this with no problem and simply uses the same view model (still only calling Initialize the first time it is created) and binds it to each view (calling Activate whenever a view is navigated to, and passing in the view name).

So what can we do with this?

Page Transitions

Another question I received was, "How do I do transitions between pages, i.e. fade one out, the next in, etc?" Obviously with a content control this is impossible because the new view replaces the old view.

But there is a solution!

Instead of replacing views, you can take the approach I did in the sample application, and use an items control with a grid for a panel. This allows you to stack the views on the same surface, but manipulate their visibility using visual states.

For visual states, I used the same set of groups and transitions on every control. There is a visible and a hidden state. The hidden state slowly expands and fades the old control.

<VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="ShowAndHide">
                <VisualStateGroup.States>
                    <VisualState x:Name="ShowState">
                        <Storyboard>
                            <ObjectAnimationUsingKeyFrames BeginTime="0:0:0" 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.RenderTransform).(ScaleTransform.ScaleX)"
                                             From="0.3" To="1.0"/>
                            <DoubleAnimation Duration="0:0:0" Storyboard.TargetName="LayoutRoot"
                                             Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)"
                                             From="0.3" To="1.0"/>
                            <DoubleAnimation Duration="0:0:0" Storyboard.TargetName="LayoutRoot"
                                             Storyboard.TargetProperty="(UIElement.Opacity)"
                                             From="0.3" To="1.0"/>
                        </Storyboard>
                    </VisualState>
                    <VisualState x:Name="HideState">
                        <Storyboard>
                            <DoubleAnimation Duration="0:0:0" Storyboard.TargetName="LayoutRoot"
                                             Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)"
                                             From="1.0" To="1.2"/>
                            <DoubleAnimation Duration="0:0:0" Storyboard.TargetName="LayoutRoot"
                                             Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)"
                                            From="1.0" To="1.2"/>
                            <DoubleAnimation Duration="0:0:0" Storyboard.TargetName="LayoutRoot"
                                             Storyboard.TargetProperty="(UIElement.Opacity)"
                                             From="1.0" To="0.3"/>
                            <ObjectAnimationUsingKeyFrames BeginTime="0:0:0" Storyboard.TargetName="LayoutRoot"
                                                       Storyboard.TargetProperty="(UIElement.Visibility)">
                                <DiscreteObjectKeyFrame KeyTime="0:0:0.0">
                                    <DiscreteObjectKeyFrame.Value>
                                        <Visibility>Collapsed</Visibility>
                                    </DiscreteObjectKeyFrame.Value>
                                </DiscreteObjectKeyFrame>
                            </ObjectAnimationUsingKeyFrames>
                        </Storyboard>
                    </VisualState>
                </VisualStateGroup.States>
                <VisualStateGroup.Transitions>                    
                    <VisualTransition To="HideState">
                        <Storyboard>
                            <DoubleAnimation 
                                Duration="0:0:0.2"
                                Storyboard.TargetName="LayoutRoot"
                                             Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)"
                                             From="1.0" To="1.2"/>
                            <DoubleAnimation Duration="0:0:0.2" Storyboard.TargetName="LayoutRoot"
                                             Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)"
                                             From="1.0" To="1.2"/>
                            <DoubleAnimation Duration="0:0:0.2" Storyboard.TargetName="LayoutRoot"
                                             Storyboard.TargetProperty="(UIElement.Opacity)"
                                             From="1.0" To="0.3"/>
                            <ObjectAnimationUsingKeyFrames BeginTime="0:0:0" Storyboard.TargetName="LayoutRoot"
                                                       Storyboard.TargetProperty="(UIElement.Visibility)">
                                <DiscreteObjectKeyFrame KeyTime="0:0:0.2">
                                    <DiscreteObjectKeyFrame.Value>
                                        <Visibility>Collapsed</Visibility>
                                    </DiscreteObjectKeyFrame.Value>
                                </DiscreteObjectKeyFrame>
                            </ObjectAnimationUsingKeyFrames>
                        </Storyboard>
                    </VisualTransition>
                </VisualStateGroup.Transitions>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>

I placed this in each control. So what now? Because Jounce supports multiple views for the view model, and because Jounce passes in the view information, the view model can trigger state changes without knowing what the views are or even how to interact with the visual state manager - it only needs to know the name of the state.

Take a look at the ShapeViewModel:

[ExportAsViewModel("ShapeViewModel")]
public class ShapeViewModel : BaseViewModel
{
    private string _lastView = string.Empty;

    public override void _Activate(string viewName)
    {
        if (string.IsNullOrEmpty(_lastView))
        {
            _lastView = viewName;
        }
        else if (!_lastView.Equals(viewName))
        {
            EventAggregator.Publish(new ViewNavigationArgs(_lastView) {Deactivate = true});
            _lastView = viewName;
        }

        GoToVisualStateForView(viewName, "ShowState", true);
    }

    public override void _Deactivate(string viewName)
    {
        GoToVisualStateForView(viewName, "HideState", true);
    }
}

Basically, it simply remembers the last view name. When an activation occurs, it raises the deactivate event for the old view. The activation then sets the visual state to the ShowState for the view being activated. On the deactivation call, it sets the visual state to the HideState for the view being deactivated. The net result is that as you navigate, the old views seem to pop and disappear as the new views appear.

Also note that Jounce automatically wires the visual state transitions to execute on the UI thread for you.

Jeremy Likness

Friday, November 5, 2010

Silverlight Data Template Selector using Managed Extensibility Framework

Sometimes it makes sense to have multiple types of views contained within a list or region. In WPF, a data template selector can help determine which template is used based on the data template, allowing a container to mix different types. It's not so straightforward with Silverlight because the DataTemplateSelector class does not exist.

There have been some excellent articles on the web with simple workarounds for this, but they often involve some sort of dictionary or hard-coded selection process. What I wanted to do was provide an example that does several things:

  • Allows you to add new templates easily simply by tagging them with attributes,
  • Is design-time friendly, and
  • Handles dependencies in the templated views, if needed

The target situation is a hypothetical social media feed that aggregates data from different types but shows them in a single list. This example mocks the data but should be sufficient to show how it could be done. Consider this screenshot which shows three different styles of presenting data in the list:

Data Template Selector in Silverlight

So, let's get started. The way I decided to handle the selector would be to use views as my "data templates" (this is similar to how Calburn.Micro does it) and tag the views with the type they handle.

I created three simple types:

Social Media Types

To wire a sample in the designer, I created a design-time model that is not built in release mode. Here is the example for a tweet:

namespace MEFDataTemplate.SampleData
{
#if DEBUG
    public class DesignTimeTweet : Tweet 
    {
        public DesignTimeTweet()
        {
            AvatarUri = new Uri("/MEFDataTemplate;component/Images/twitterprofile.png", UriKind.Relative);
            Message = "This is a tweet inside the designer.";
            Posted = DateTime.Now;
            Username = "@jeremylikness";
        }
    }
#endif
}

Now we can build out template for a tweet. I created a folder called Templates and added a user control called TweetTemplate.xaml. The XAML organizes a sample tweet like this:

    <Grid x:Name="LayoutRoot" HorizontalAlignment="Stretch" Background="White" d:DataContext="{d:DesignInstance design:DesignTimeTweet,IsDesignTimeCreatable=True}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>            
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <StackPanel Orientation="Horizontal" Margin="5">
            <Image Margin="2" Source="/MEFDataTemplate;component/Images/twitter.png"/>
            <TextBlock VerticalAlignment="Center" Margin="2" Text="{Binding Username}"/>
        </StackPanel>
        <Image Margin="2" VerticalAlignment="Top" Source="{Binding AvatarUri}" Stretch="None" Grid.Row="1"/>
        <TextBlock Margin="5" Text="{Binding Posted,StringFormat='MM-dd-yyyy h:mm:ss'}" FontWeight="Bold" HorizontalAlignment="Right" Grid.Column="1"/>
        <TextBlock Margin="5" TextWrapping="Wrap" Text="{Binding Message}" Grid.Row="1" Grid.Column="1"/>
    </Grid>

Inside the designer, it looks like this:

Data Template inside the Designer

Now we need to tag it. If you are familiar with MEF, you know it is not straightforward to create an ExportFactory with metadata, as there is no easy way to reach the metadata and create the corresponding factory (this is what is needed to create new views for each item, rather than having a single copy). Even the Lazy feature won't work for us because the lazy value is lazy loaded, but then retains the same value. So what can we do?

We'll get to the tricky part in a second, but for now we'll export our view twice. First, we'll make a special export that allows us to tag the view with the type of entity it can handle. The metadata looks like this:

namespace MEFDataTemplate.Templates
{
    public interface IExportAsTemplateForTypeMetadata
    {
        Type TargetType { get; }        
    }
}

And the export attribute itself is implemented like this:

namespace MEFDataTemplate.Templates
{
    [MetadataAttribute]
    [AttributeUsage(AttributeTargets.Class)]
    public class ExportAsTemplateForTypeAttribute : ExportAttribute, IExportAsTemplateForTypeMetadata 
    {
        public ExportAsTemplateForTypeAttribute(Type targetType) : base(typeof(UserControl))
        {
            TargetType = targetType;            
        }

        public Type TargetType { get; set; }       
    }
}

Now, we can tag our template and let MEF know it handles Tweet objects:

namespace MEFDataTemplate.Templates
{
    [ExportAsTemplateForType(typeof(Tweet))]
    [Export]
    public partial class TweetTemplate
    {
        public TweetTemplate()
        {
            InitializeComponent();
        }
    }
}

Notice that I export the control twice. The first time is to tag the target type it can handle, and the second time is to give me an export I can work with to create the views inside the templates.

We repeat the process for the photos and blogs. Next, I need a container to help me generate new views. Remember, I can't grab both metadata and export factories, so I'll "cheat" by making a host for the export factory:

namespace MEFDataTemplate.Templates
{
    public class TemplateFactory<T>
    {
        [Import]
        public ExportFactory<T> Factory { get; set; }

        public T Instance
        {
            get
            {
                return Factory.CreateExport().Value;
            }
        }
    }
}

Essentially, when I type this class and create an instance, I can reference the Instance property and it will go to the export factory to give me a new instance. We could simplify this and just restrict T to new(), and that would work fine, but doing this also allows the type to have dependencies. If you wanted to import a logger, event aggregator, or other dependency, this mechanism supports it - and you'll be surprised at how good the performance actually is.

Now we've got some work to do. I'm going to use a value converter that takes the data type and returns the data template. We'll also bind the data to the data template. Here's what it looks like, and I'll explain what's going on below:

namespace MEFDataTemplate.Converters
{
    public class DataTemplateSelector : IValueConverter 
    {
        private const string INSTANCE = "Instance";
        const string LAYOUT_ROOT = "LayoutRoot";                                      
            
        public DataTemplateSelector()
        {
            if (!DesignerProperties.IsInDesignTool)
            {
                CompositionInitializer.SatisfyImports(this);
            }
        }

        [ImportMany]
        public Lazy<UserControl, IExportAsTemplateForTypeMetadata>[] Templates { get; set; }

        private readonly Dictionary<Type,object> _factories = new Dictionary<Type, object>();

        private readonly Dictionary<Type,Func<UserControl>> _templateFactory = new Dictionary<Type,Func<UserControl>>();

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (DesignerProperties.IsInDesignTool)
            {
                return new TextBlock {Text = "Design time row."};
            }

            FrameworkElement view = new Grid();

            if (value != null)
            {                
                if (_templateFactory.ContainsKey(value.GetType()))
                {
                    view = _templateFactory[value.GetType()]();                   
                }
                else 
                {
                        var viewType = (from t in Templates
                                        where t.Metadata.TargetType.Equals(value.GetType())
                                        select t.Value.GetType()).FirstOrDefault();
                        if (viewType != null)
                        {
                            var factory = Activator.CreateInstance(typeof(TemplateFactory<>).MakeGenericType(new[] { viewType }));
                            CompositionInitializer.SatisfyImports(factory);
                            _factories.Add(value.GetType(), factory);
                            Func<UserControl> resolver = ()=>
                                factory.GetType().GetProperty(INSTANCE).GetGetMethod().Invoke(factory, null) as UserControl;
                            _templateFactory.Add(
                                value.GetType(), resolver);
                            view = _templateFactory[value.GetType()]();
                        }
                }

                if (view != null)
                {
                    RoutedEventHandler handler = null;
                    handler = (o, e) =>
                                  {
                                      view.Loaded -= handler;
                                      ((Grid) view.FindName(LAYOUT_ROOT)).DataContext = value;
                                  };
                    view.Loaded += handler;
                }
            }

            return view;
        }       

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

So the first thing we do is compose imports if we aren't in the designer. This will pull in all tagged views and the metadata that describes the types that they support.

Notice I have two dictionaries. One dictionary maps the target type to the TemplateFactory<T> for the view that supports the target type. Because we're dealing with generics, I only need one copy per type, so with 1,000 records I still only make three factory objects (one per type). Second, because we'll use reflection to grab the Instance property (since we can't close the generic directly) I don't want to reflect every single row. Next, I store a function that handles the result of the reflection.

When the value comes in, if we're in the designer, we just return a simple bit of text. Otherwise, we set up an empty Grid to return if our data template selection fails. You could throw an exception instead of to make this more robust. If I've already processed the type, I simply look up the function and call it to get a new view.

For the first time I find a type, we get to have some fun. First, I find the view that is intended for the target type by inspecting the metadata. This only has to be done once to get the type for the view. Next, I create an instance of the template factory. This works from the inside out for this statement:

var factory = Activator.CreateInstance(typeof(TemplateFactory<>).MakeGenericType(new[] { viewType }));

The typeof statement gets up what's called an "open generic" or a generic type that hasn't been scoped to a target type yet. In order to close the generic, we call the MakeGenericType method and pass it the type we want to close it with - in this case, the type of the template we found by inspecting meta data. We create an instance, and now we have our MEF factory for generating the view.

Because we can't close the type, we need to use reflection to find the property that grabs the instance for us:

 Func<UserControl> resolver = ()=>
                                factory.GetType().GetProperty(INSTANCE).GetGetMethod().Invoke(factory, null) as UserControl;

This uses reflection to get the property, gets the "getter" method, then invokes it for the factory object we just created and casts it as a UserControl. If you want to support other exports, you can cast this down to a base FrameworkElement if you like.

If you're really concerned with performance, you can cache the result of the GetGetMethod as an Action (storing the full function here will still call the reflection, whereas storing the result of the get will store a pointer to the actual getter without reflection - make sense?) then you could call that cached method, but I didn't see enough issues with performance to go to that level.

Finally, we wire up the DataContext. Here, we have to be careful. If we wire the Loaded event to a method, we'll lose the context of where we're at and not be able to hook the item to the data context. However, if we assign a lambda expression, we're really creating a reference from this converter to the view - which means it will never be released from memory.

So instead, we create a reference to the handler, then pass a lambda expression. The lambda expression is able to reference the item to wire to the data context, and also reference itself to unhook from the Loaded event. The result is that the control will hook to the item and then lose the event handler. This assumes the templates have a LayoutRoot, if you prefer you can connect to the user control directly.

The view model exposes a generic list of objects:

public ObservableCollection<object> SocialFeed { get; private set; }

And wires in 1,000 items randomly chosen between tweets, photos, and blog entries. You can tweak the size to test larger or smaller lists for performance. Using our selector is simple - we declare it and then use it to bind the object to a ContentControl.

    <UserControl.Resources>
        <Converters:DataTemplateSelector x:Key="Selector"/>
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="White">        
        <Grid.DataContext>
            <ViewModel:MainViewModel/>
        </Grid.DataContext>
        <ListBox ItemsSource="{Binding SocialFeed}" HorizontalAlignment="Center" Width="800" VerticalAlignment="Top">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <ContentControl Width="750" HorizontalAlignment="Center" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"
                                    Content="{Binding Converter={StaticResource Selector}}"/>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>

When you run, you see we get a nice virtualizing list box with selected data templates and the performance on my machine is fast.

From here, you can do several things. If your items implement a common interface, you might provide a default template that handles anything and then export specific templates as you implement more detailed types. Obviously you can get rid of the instance factory and the composition initialization if your views will have no dependencies. Once this is wired in place, adding a new "data template" is as easy as design it, tag it, and run it.

To quote Forrest Gump, "That's all I got to say about that." Grab the source here.

Jeremy Likness

Tuesday, November 2, 2010

Jounce Part 5: Navigation Framework

One common request I get is how Jounce can work with the Navigation Framework.

My first reply is always, "Why do you want to use that?" As you can see in previous posts, the Jounce navigation works perfectly fine with region management to manage your needs. If you want the user to be able to "deep link" to a page, you can easily process the query string and parse it into the InitParams for the application and deal with them there.

For the sake of illustration, however, I wanted to show one way Jounce can work with an existing navigation framework. In fact, to make it easy to follow along, the quick start example works mainly from the "Navigaton Application" template provided with Silverlight.

As a quick side note, I am very much aware of the INavigationContentLoader interface. This may be the way to go and in the future I might write an adapter for Jounce, but I just don't see a compelling need to have URL-friendly links as I typically write applications that act like applications in Silverlight, not ones that try to mimic the web by having URLs.

The example here is available with the latest Jounce Source code (it's not part of an official release as of this writing so you can download it using the "Latest Version - Download" link in the upper right).

To start with, I simply created a new Silverlight navigation application.

Without overriding the default behavior, the navigation framework creates a new instance of the views you navigate to. To get around this model which I believe is wasteful and has undesired side effects, I changed the mapping for the various views to pass to one control that manages the navigation for me:

<uriMapper:UriMapper>
<uriMapper:UriMapping Uri="" MappedUri="/Views/JounceNavigation.xaml"/>
    <uriMapper:UriMapping Uri="/ShowText/{text}" MappedUri="/Views/JounceNavigation.xaml?view=TextView&text={text}"/>
    <uriMapper:UriMapping Uri="/{pageName}" MappedUri="/Views/JounceNavigation.xaml?view={pageName}"/>                        
</uriMapper:UriMapper>

Notice how I can translate the path to a view parameter, and that I am also introducing a mapping for "ShowText" that we'll use to show how you can grab parameters.

The JounceNavigation control will get a new copy every time, but by using MEF it will guarantee we always access the same container. To do this, I created NavigationContainer and it contains a single content control with a region so the region is only exported once:

<ContentControl 
                    HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                    HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"
                    Regions:ExportAsRegion.RegionName="MainContainer"/>

In the code-behind, I simply export it:

namespace SilverlightNavigation.Views
{
    [Export]
    public partial class NavigationContainer
    {
        public NavigationContainer()
        {
            InitializeComponent();
        }
    }
}

Now we can use this single container in the JounceNavigation control. What we want to do is attach it when we navigate to the control, and detach it when we navigate away (a control can only have one parent, so if we don't detach it, we'll get an error when the next view is created if the previous view hasn't been garbage-collected yet).

The navigation control also does a few more things to integrate with the navigation framework. It will remember the last view so it can deactivate the view when navigating away. It will default to the "Home" view but basically takes any view passed in (based on the uri mappings we defined earlier) and raises the Jounce navigation event. Provided the view targets the region in our static container, it will appear there. Normally I'd do all of this in a view model but wanted to show it in code-behind for the sake of brevity (and to show how Jounce plays nice with standard controls as well).

public partial class JounceNavigation
{
    [Import]
    public IEventAggregator EventAggregator { get; set; }

    [Import]
    public NavigationContainer NavContainer { get; set; }

    private static string _lastView = string.Empty;
                
    public JounceNavigation()
    {
        InitializeComponent();
        CompositionInitializer.SatisfyImports(this);
        LayoutRoot.Children.Add(NavContainer);
        if (!string.IsNullOrEmpty(_lastView)) return;
        EventAggregator.Publish("Home".AsViewNavigationArgs());
        _lastView = "Home";
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        if (NavigationContext.QueryString.ContainsKey("view"))
        {
            var newView = NavigationContext.QueryString["view"];
            _lastView = newView;
            EventAggregator.Publish(_lastView.AsViewNavigationArgs()); 
            EventAggregator.Publish(NavigationContext);
        }
    }

    protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
    {
        if (!string.IsNullOrEmpty(_lastView))
        {
            EventAggregator.Publish(new ViewNavigationArgs(_lastView) {Deactivate = true});
        }
        LayoutRoot.Children.Remove(NavContainer);
    }

}

Notice that we publish two events. The first is the view navigation to wire in the target view. The second is a NavigationContext event. The navigation context contains all of the query string information. Any view that needs to pull values from the query string can simply listen for this event. Because the view navigation is called first, the view will be in focus and ready when it receives the context message to parse any parameters.

To demonstrate this, let's look at the TextView control. When you pass text in the url, it will simply display it. The XAML looks like this:

<Grid x:Name="LayoutRoot" Background="White">
    <TextBlock x:Name="TextArea"/>
</Grid>

The code-behind looks like this:

[ExportAsView("TextView")]
[ExportViewToRegion("TextView", "MainContainer")]
public partial class TextView : IEventSink<NavigationContext>, IPartImportsSatisfiedNotification
{
    [Import]
    public IEventAggregator EventAggregator { get; set; }

    public TextView()
    {
        InitializeComponent();                        
    }

    public void HandleEvent(NavigationContext publishedEvent)
    {
        if (publishedEvent.QueryString.ContainsKey("text"))
        {
            TextArea.Text = publishedEvent.QueryString["text"];
        }
    }

    public void OnImportsSatisfied()
    {
        EventAggregator.SubscribeOnDispatcher(this);
    }
}

Pretty simple - it exports as a view name, targets the main container region, and then registers as the event sink for NavigationContext messages. In this case we only have one listener. In more complex scenarios with multiple view models listening, the view model would simply inspect the "view" parameter to make sure it matches the target view (it could easily find this in a generic way by asking the view model router) and ignore the message if it does not.

To convert the "Home" and the "About" page took only two steps.

First, I changed them from Page controls to UserControl controls. I simply had to change the tag in XAML and remove the base class tag in the code-behind and the conversion was complete. Second, I tagged them as views and exported them to the main region:

namespace SilverlightNavigation.Views
{
    [ExportAsView("About")]
    [ExportViewToRegion("About", "MainContainer")]
    public partial class About 
    {
        public About()
        {
            InitializeComponent();
        }       
    }
}

That's it - now I have a fully functional Jounce application that uses the navigation framework and handles URL parameters. You can click on the "text" tab to see the sample text and then change the URL to confirm it parses the additional text you create.

Jeremy Likness

Monday, November 1, 2010

Silverlight and HTML5: Microsoft's Official Stance

This weekend was a wild ride with speculation about the future of Silverlight and whether or not Microsoft was abandoning it. It produced a lot of misinformation as anti-Silverlight zealots proclaimed its official demise with a smattering of "I told you so" thrown in.

My blog post on the topic where I detailed just why I didn't believe the hype and how common sense should show that Silverlight is here to stay caused quite a jump in readership (click here to see the trend). People everywhere were scrambling to uncover just what it all meant, and consultants in the Silverlight field found themselves fielding questions by concerned customers.

Fortunately, we can put to rest the nonsense and focus on the official news from Redmond. This is direct from the mother ship, not hearsay or speculation.

¡Viva la Silverlight!

Let's get rid of the fuzz on the screen and dial in to the clear, concise message. To set the stage, here are the myths that were spread:

Myth: "Microsoft has seen the light, and it's not Silverlight."

Fact: Microsoft continues to fully support Silverlight in all of its flavors, with new variations on the horizon.

Myth: "Microsoft embraces HTML5 over Silverlight."

Fact: Microsoft embraces HTML5 with Silverlight as two technologies with different targets.

Myth: "Microsoft switches focus from Silverlight to HTML5."

Fact: Microsoft is a big company that has many teams that can focus on both Silveright and HTML5.

Myth: "Microsoft nails Silverlight's future to Windows Phones."

Fact: Windows Phone 7 is only one of many platforms that support Silverlight. Future versions of Silverlight are being planned for all platforms, not just the phone.

Don't take my word for it, here is Microsoft's from the official Silverlight blog: PDC and Silverlight (by Bob Muglia). You can also read the press statement by Steve Ballmer by clicking here. Tim Heuer shared this this post with a nice graphic by Silverlight that "The reports of my death are greatly exaggerated." Finally, Scott Guthrie who runs many of the development teams at Microsoft, posted his take on the matter (with a little teaser that details for the upcoming Silverlight "vNext" or 5.0 version will be coming soon).

Before I dive into the key points, read this statement from Tim Heuer on what he has personally done to commit himself to the future success of Silverlight. I believe it says a lot about the fact that there is much to do, and many willing to do it:

This past year I decided to take a deeper role in the engineering side of Silverlight. In making this decision I had to relocate to Redmond. This involved me relocating my family away from our comfort zone and having to start completely new. It was perhaps the biggest personal change in my life I’ve made yet. My kids left their friends, as did we. My wife left her close knit family in AZ. My wife stopped a business she was just getting great momentum on before this decision. We sold our house at a significant personal financial loss. We moved to an area where home prices didn't fall like we were used to and thus aren't in a position of buying power. You'll forgive me if I say that no software project has made more of a bet on Silverlight than I have personally this year.

That's a powerful story, but let's move on to the facts. The main points made by Microsoft today (the underlines are mine):

  1. Silverlight is very important and strategic to Microsoft.
  2. We're working hard on the next release of Silverlight, and it will continue to be cross-browser and cross-platform, and run on Windows and Mac
  3. Silverlight is a core application development platform for Windows, and it's the development platform for Windows Phone.

According to Steve Ballmer, "We will also enable browser scenarios that provide additional capabilities, including Silverlight. Silverlight provides the richest media streaming capabilities on the web, and we will continue to deliver that on both Windows and Mac ... Developers can build great applications for [Windows Phone 7] using Win32, .NET, Silverlight and HTML5."

Even before the huge flood of speculative posts and articles exploded on the web, John Papa interviewed Scott Guthrie to talk about The State of Silverlight. If you have 20 minutes, it's a great interview to watch.

Some more quotes that I love:

Silverlight provides the richest way to build Web-delivered client apps.

Silverlight is a critical component of our three-screen strategy.

Silverlight has and will continue to be a pioneering technology that makes it possible to deliver the best media experiences anywhere.

Finally, I want to leave you with the entire last paragraph of Bob Muglia's post because it is so important and echoes exactly what I've been saying the past few days. This is really one of the best summaries of what Silverlight is and can do that I've seen, and descrihes why it plays nicely with (and doesn't compete directly with) HTML5:

The purpose of Silverlight has never been to replace HTML, but rather to do the things that HTML (and other technologies) can't, and to do so in a way that’s easy for developers to use. Silverlight enables great client app and media experiences. It's now installed on two-thirds of the world’s computers, and more than 600,000 developers currently build software using it. Make no mistake; we'll continue to invest in Silverlight and enable developers to build great apps and experiences with it in the future.

There you have it. To all of you who jumped on the "Silverlight is dead" bandwagon this weekend, hate to disappoint you, but: Silverlight is alive, well, and here to stay with Microsoft's full support and backing. Jeremy Likness