Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

MixModes Synergy Toolkit for Windows Phone 7

0.00/5 (No votes)
30 Jan 2011 1  
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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here