Click here to Skip to main content
15,867,835 members
Articles / Mobile Apps / Windows Phone 7

MixModes Synergy Toolkit for Windows Phone 7

Rate me:
Please Sign up or sign in to vote.
4.58/5 (4 votes)
30 Jan 2011CPOL6 min read 13.3K   4   2
Synergy is a platform toolkit that unleashes the power of WPF, Silverlight and Windows Phone 7 platforms. In this blog I will discuss the Windows 7 phone part of this library and how it helps to somehow converge development practices to that of WPF and leverage data binding capabilities.

Introduction

Synergy is a platform toolkit that unleashes the power of WPF, Silverlight, and Windows Phone 7 platforms. In this blog, I will discuss the Windows 7 Phone part of this library and how it helps to somehow converge development practices to that of WPF and leverage data binding capabilities.

The Synergy toolkit can be accessed at its CodePlex home where Santa (myself) keeps delivering new gifts and wishes everyday. I have released v1 of the Windows Phone 7 library in the downloads section.

For a background on the WPF Synergy toolkit, you can refer to my previous blog here. I also wrote a docking Windows framework in the WPF edition of Synergy that you can refer here and a window theming article here.

You can follow me on twitter @ashishkaila.

Without further adieu, let me walk you through the features of the v1 release.

Base Framework for Windows Phone 7 Development

Commanding Support

The CommandBase class provides a flexible implementation for custom commands. This class works on an Action<object> instance for command invocation, and an optional Predicate<object> for command execution evaluation. If the predicate is not specified, it is assumed that the command can be executed anytime. Simply speaking, we can instantiate the CommandBase class as follows:

C#
CommandBase myCommand = new CommandBase(arg=>Foo(arg), arg=>CanFoo(arg));

Dependency System Enhancements

Several enhancements in the dependency system have been provided that helps developers tap easily into the framework for advanced operations related to binding and dependency properties. These enhancements are as follows:

Observable Binding

The ObservableBinding class, as the name suggests, observes a binding for value changes and provides the most recent value for the binding. This class is very useful within custom behaviors and trigger actions where the data context of the associated object does not propagate to these entities. This is so because Behaviors and Triggers are attached properties within the static Interaction class (declared in the System.Windows.Interactivity assembly) and are therefore detached from the dependency system.

By declaring a dependency property of type Binding and using ObservableBinding within a Behavior/TriggerAction, you can both hook into the dependency property system of the associated object as well as evaluate the most recent value of the binding.

For instance, the EventToCommand trigger action defines a dependency property Command of type Binding:

C#
public static DependencyProperty CommandProperty = 
    DependencyProperty.Register("Command", 
                                typeof (Binding), 
                                typeof (EventToCommand), 
                                new PropertyMetadata(null, OnCommandChanged));

It also declares an ObservableBinding for the command:

C#
private readonly ObservableBinding _observableCommandBinding = new ObservableBinding();

This _observableCommandBinding member will provide us with the latest value of Binding exposed by the Command property.

There are certain rules to ensure that ObservableBinding works as expected for Binding properties. Basically, anytime a Binding property changes, we have to unhook from the old Binding and hook into the new binding. This is done in the OnCommandChanged method which is called when a Binding property is changed (that is the actual Binding property and not the value of the Binding):

C#
private static void OnCommandParameterChanged(DependencyObject d, 
               DependencyPropertyChangedEventArgs e) 
{ 
    EventToCommand eventToCommand = d as EventToCommand;

    if (eventToCommand == null) 
    { 
        return; 
    }

    eventToCommand._observableCommandParameterBinding.Detach(); 
             
    if ((e.NewValue != null) && (eventToCommand.AssociatedObject != null)) 
    { 
        eventToCommand._observableCommandParameterBinding.Attach(
           eventToCommand.Command eventToCommand.AssociatedObject); 
    } 
}

Also, when the associated object is initially attached to the TriggerAction / Behavior, we need to attach to the ObservableBinding:

C#
protected override void OnAttached() 
{ 
    base.OnAttached();

    if (Command != null) 
    { 
        _observableCommandBinding.Attach(Command, AssociatedObject); 
    } 
}

If you skip the above step, the initial value of Binding will not be tracked by ObservableBinding.

ObservableDependencyProperty

ObservableDependencyProperty on the other hand provides the ability to track changes in a dependency property declared in another DependencyObject by way of event notifications. The constructor of ObservableDependencyProperty takes two parameters: the string representing the name of the dependency property to observe, and a delegate of type DependencyPropertyChangedEventHandler that will be called when the dependency property changes:

C#
ObservableDependencyProperty _verticalScrollChange = 
    new ObservableDependencyProperty("VerticalOffset", OnVerticalScroll);

To track changes in a dependency property of the actual DependencyObject, a ObservableDependencyProperty instance must be attached to it via the AddValueChanged method:

C#
_verticalScrollChange.AddValueChanged(_scrollViewer);

Similarly, to detach or stop property change tracking, the RemoveValueChanged method may be called:

C#
_verticalScrollChange.RemoveValueChanged();

Complete MVVM Framework

Custom View Models can be implemented by simply inheriting from the base class ViewModelBase. To raise a PropertyChanged event, a type safe RaisePropertyChanged method can be called with an expression containing your property reference, e.g.:

C#
RaisePropertyChanged(x=>MyProperty);

ViewModelBase also contains a Navigate method that can navigate the phone to a bound view performing data binding behind the scenes (more on this in the next section), e.g.:

C#
MyViewModel viewModel = new MyViewModel(); 
viewModel.Navigate();
// Note: For this to work, your application must inherit
//       from PhoneApplication class (see below) 

Easier Navigation via Declarative View-ViewModel Bindings

In WPF, DataTemplates could bind a ViewModel to a View on an application level. This binding enables runtime View generation within ContentPresenter. I wanted to extend the same idea to Windows Phone 7 screen navigation since URL based navigation within an application seems error prone and passing query parameters is tedious.

The ViewSelector class provides this declarative binding capabilities in conjunction with the ViewBinding class. Basically, in your application resources, you can define this mapping:

XML
<Framework:ViewSelector x:Key="ApplicationViewSelector"> 
    <Framework:ViewBinding ViewModelType="INameU.ViewModels.NameListViewModel" 
                           ViewUri="/Views/NameListView.xaml" /> 
    <Framework:ViewBinding ViewModelType="INameU.ViewModels.NameDetailViewModel" 
                           ViewUri="/Views/NameDetailView.xaml" /> 
</Framework:ViewSelector>

where Framework is mapped as:

XML
xmlns:Framework="clr-namespace:MixModes.Phone.Synergy.Framework;
                 assembly=MixModes.Phone.Synergy"

Once you have done this, you can call the Navigate method from your ViewModel instance to navigate to the mapped view and data binding to it at the same time, without using any URL(s)!

DockPanel and WrapPanel Controls

I have ported both DockPanel as well as WrapPanel from the Silverlight Toolkit for Windows Phone 7 so you do not have to reference the toolkit if you are not using any additional functionality other than these panels.

EventToCommand TriggerAction

A common pain point is the absence of the Command and CommandParameter properties on controls such as Button and ListBoxItem. To fill this gap, the EventToCommand trigger action can be used to map a RoutedEvent to an ICommand instance along with a CommandParameter instance. To bind the RoutedCommand to an EventToCommand trigger, you need to add the trigger to the associated framework element:

XML
<Button Content="Click Me"> 
    <Custom:Interaction.Triggers> 
        <Custom:EventTrigger EventName="Click"> 
            <Triggers:EventToCommand 
                 Command="{Binding Path=DataContext.ShowDetailsCommand, ElementName=Page}" 
                 PassEventArgsToCommand="True"/> 
            </Custom:EventTrigger> 
        </Custom:Interaction.Triggers> 
</Button>

The PassEventArgsToCommand property, when set to true, passes the RoutedEventArgs as a CommandParameter to the associated ICommand’s Execute method.

ScrollLoadBehavior

ScrollLoadBehavior allows asynchronous loading of data when the user scrolls a scroll viewer vertically to the bottom. ScrollLoadBehavior has an associated ICommand and CommandParameter that are executed anytime the scroll viewer reaches its bottommost point. Loading is detected via ICommand’s CanExecute method. For example:

XML
<ListBox DataContext="{Binding}"                 
         ItemsSource="{Binding Path=Names}"> 
    <Custom:Interaction.Behaviors> 
        <Behavior:ScrollLoadBehavior Command="{Binding Path=LoadNamesCommand}" /> 
    </Custom:Interaction.Behaviors> 
</ListBox>

Notice the explicit DataContext="{Binding}" in the ListBox. This is necessary as without explicit parent data binding, ItemsSource sets the DataContext to its value, thereby failing the binding for ScrollLoadBehavior.

Out of the Box Converters

Many frequently used out of the box converters have been provided in the toolkit:

  • EnumMatchConverter – Matches a value of an enum member with a constant value and returns a boolean indicating the match. For example, if you want to enable a control when the state is StateEnum.IsError, you can do the following:
  • XML
    Enabled="{Binding Path=State, 
        Converter={StaticResource EnumConverter}, ConverterParameter=IsError}" 
  • NotConverter – Inverts the value of a boolean expression.
  • VisibilityConverter – Based on a boolean value, shows (if value is true) or collapses (if value is false) visibility of a control.
  • PipeConverter – Pipes a value through multiple converters to compute the end value of the binding expression. For example, if you want to match a value to an enumeration member Loading and then determine the visibility of a control, you could declare a pipe converter as follows:
  • XML
    <conv:PipeConverter x:Key="LoadingMessageVisibilityConverter"> 
        <conv:EnumMatchConverter /> 
        <conv:VisibilityConverter /> 
    </conv:PipeConverter>

    The values are piped from top to bottom, hence first EnumMatchConverter is invoked and then VisibilityConverter. To use it, simply refer to the PipeConverter as follows:

    XML
    Visibility="{Binding Path=State, 
        Converter = {StaticResource LoadingMessageVisibilityConverter}, 
        ConverterParameter=Loading}" 
  • ThemeBasedResourceConverter – This converter returns resources based on the theme of Windows 7 Phone (either dark or light). To use this converter, we need to point to both theme resources via the LightThemeResource and DarkThemeResource properties. For example, if your application has navigation button images for both themes as NavigationLight.png and NavigationDark.png, you could use ThemeBasedResourceConverter to set the appropriate image as the background of a button as follows:
  • XML
    <phone:PhoneApplicationPage.Resources> 
        <Converters:ThemeBasedResourceConverter x:Key="ThemeBasedResourceConverter" 
              LightThemeResource="../Resources/NavigationLight.png" 
              DarkThemeResource="../Resources/NavigationDark.png" /> 
    </phone:PhoneApplicationPage.Resources> 

    And then refer to the above ThemeBasedResourceConverter in the button:

    XML
    <Button> 
        <Image Stretch="None" 
               Source="{Binding Path=., RelativeSource={RelativeSource Self}, 
                       Converter={StaticResource ThemeBasedResourceConverter}}"> 
        </Image> 
    </Button>

So that’s all in v1 folks. I will be happy to receive any feedback you may have, and will continuously develop the Synergy toolkit to make your development easier whether you code in WPF, WP7, or Silverlight!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) MixModes Inc. | Research In Motion
Canada Canada
Ashish worked for Microsoft for a number of years in Microsoft Visual Studio (Architect edition) and Windows Live division as a developer. Before that he was a developer consultant mainly involved in distributed service development / architecture. His main interests are distributed software architecture, patterns and practices and mobile device development.

Currently Ashish serves as a Technical Lead at RIM leading next generation BlackBerry media experience and also runs his own company MixModes Inc. specializing in .NET / WPF / Silverlight technologies. You can visit MixModes at http://mixmodes.com or follow it on Twitter @MixModes

In his free time he is an avid painter, hockey player and enjoys travelling. His blog is at: http://ashishkaila.serveblog.net

Comments and Discussions

 
GeneralMy vote of 5 Pin
RaviRanjanKr1-Feb-11 4:01
professionalRaviRanjanKr1-Feb-11 4:01 
GeneralRe: My vote of 5 Pin
Ashish Kaila1-Feb-11 5:25
Ashish Kaila1-Feb-11 5:25 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.