Task Commands revisited

Posted on Feb 23, 2012 in and tagged , , , ,

Half a year ago I blogged about using TPL with MVVM in a test-friendly way. My solution turned out to be pretty handy, though it was far from being perfect. In this post I will describe what was wrong with the initial implementation and can we slightly improve it.

Looking at that solution right now, I wonder why did I decide to use event async pattern (providing the ExecuteCompleted event) in the first place. The intention was to notify the caller about completion of the asynchronous operation. That is what System.Threading.Task is used for this days, isn’t it? So the obvious solution is to expose the ExecuteAsync() method returning the Task instance on the command class. Here is the source of the updated AsyncRelayCommand:

public class AsyncRelayCommand<T> : ICommand {
    private readonly RelayCommand<T> _internalCommand;
    private readonly Func<T, Task> _executeMethod;
 
    public event EventHandler CanExecuteChanged
    {
        add { _internalCommand.CanExecuteChanged += value; }
        remove { _internalCommand.CanExecuteChanged -= value; }
    }
 
    public AsyncRelayCommand(Func<T, Task> executeMethod, Predicate<T> canExecuteMethod)
    {
        if (executeMethod == null)
        {
            throw new ArgumentNullException("executeMethod");
        }
 
        _executeMethod = executeMethod;
        _internalCommand = new RelayCommand<T>(_ => { }, canExecuteMethod);
    }
 
    public AsyncRelayCommand(Func<T, Task> executeMethod)
        : this(executeMethod, _ => true) { }
 
    void ICommand.Execute(object parameter)
    {
        if (parameter is T)
        {
            ExecuteAsync((T)parameter);
        }
 
        else throw new ArgumentException("Parameter should be of type " + typeof(T));
    }
 
    public Task ExecuteAsync(T parameter)
    {
        return _executeMethod(parameter);
    }
 
    public bool CanExecute(object parameter)
    {
        return _internalCommand.CanExecute((T)parameter);
    }
 
    public void RaiseCanExecuteChanged()
    {
        _internalCommand.RaiseCanExecuteChanged();
    }
}

The code is pretty straightforward, here are the key points:

  • Create an inner command that responds to CanExecute, RaiseCanExecute calls and handles subscriptions to CanExecuteChanged event (optional, since I am using MVVM Light with most of my projects, that’s an acceptable solution for me. However, you may easily implement your own commanding logic).
  • Introduce new ExecuteAsync method that returns Task instance.
  • Implement ICommand.Execute explicitly (I find it more consistent not to have Execute method in the AsyncRelayCommand class).

Side note: as I mentioned in the beginning, I find this command extension rather useful. It was successfully used in 5 own projects (both WP7 and WPF), and I hope you enjoy it too.

As usual, the source code is available at bitbucket (TaskCommands.v2.zip).

Late Reader 1.3 released

Posted on Jan 31, 2012 in and tagged ,

Late Reader 1.3 hits the marketplace.

The main goal for this release it to fix some issues caused by breaking changes introduced in 1.2 that occur only when you update the application (you can read about one of them, related to live tile backward compatibility, here).

There are many new features planned for the upcoming major update. Stay tuned ;)

Just in several hours after LateReader v.1.2 being published, I received an error report saying that “No XAML found at the location XXX”. Wait, but that’s an old URI, used in the previous versions, where did it come from? Turned out that a secondary live tile which quickly adds a new article to the list still uses the deep link URI, created with the previous app version. The morale is: keep the backward compatibility in mind and use UriMapper to deal with such tile navigation issues.

And v.1.3 that fixes this bug and some other minor issues is already submitted for the certification :)

Late Reader v.1.2

Posted on Jan 27, 2012 in and tagged ,

Late Reader, my Windows Phone 7 app that allows you to keep a reading list on your phone and synchronize it with ReadItLater.com account, has been updated to v.1.2.

Actually, a lot of internal tweaks were made, though the end user probably won’t even notice them. Starting from 1.2, the local reading list and article cache are stored in SQL CE database (opposed to decoupled isolated storage files in the previous versions) which slightly improved general app performance.

Also I’ve decided to stop using getfavicon.org service to retrieve article icons – it just was not fast enough (took 2-3 seconds to get a single icon) and was not able to resolve the valid favicon for many web addresses. I’ve found http://getfavicon.appspot.com/ web app to be really fast and responsive, except that it gives you an image in GIF format and WP7 doesn’t have a native GIF decoder. So instead of making the manual bit conversion magic on the phone, I put up web service that uses GDI+ tools from BCL to convert a gif file into png. It gives you a small delay for each image request, but thanks to the modern cloud platforms it is still much faster than the old solution (I used Appharbor to host my web service, if you are interested).

Another great feature that you may find useful is list searching. From the main page, you can tap the search button and start typing the name of the article you want to find.

It is always great to hear the feedback from people who use your app. Based on user’s suggestions, ‘tap mode’ option was added. It allows you to choose what happens when you tap the article in the list – you can switch between Text (open text view by default), Web (open IE with the article address) and Hybrid (tapping the article title opens text view, while tapping the site icon or web address opens the article in IE).

And the last update – I decided to display ads in the app, but the good news for those of you who can’t stand seeing ads in the mobile app is that you can turn them off in the settings. Just remember that showing ads provides a small reward for all the effort the developer put in building the app.

You can download v.1.2 here. Don’t forget to rate the app if you like it.

In the previous post I showed how to load the large SQL Server CE database on WP7. However, if you are working with a read-only reference database, deploying it to the phone or emulator may take up to several minutes. In this post I will try to explain how to save your nerves and time, making the regular development and debugging easier.

So for now, there are 2 options for deploying the database with your app:

  • Use Isolated Storage. Upload the database to the device with the help of ISETool. For ~50 mb database it takes 4 minutes on my laptop.
  • Pack it as the application content (setting Build Action to Content). In that case, the database is packed into the XAP file on your machine and then unpacked back on the device. While this approach is a bit faster then the first one, it still takes something about 1 minute to deploy your app, which I find unacceptable.

Note that the 1st option should be used only for Debug configurations. The only way to include the reference database when delivering the app to the users is by packing it into the XAP (you may, however, copy it to Isolated Storage upon the first launch, but I don’t see any sense why should you do so).

So I can’t fasten the packaging process, but I can upload my database to the Isolated Storage once and it will be preserved between the application deploys when debugging. When I need to release the application build, I will include the database to XAP file content. Sounds easy.

Being lazy motivates me to script the uploading routine. Here is a batch file that takes the IS snapshot with ISETool, adds the database file and restores the snapshot on the emulator:

set isePath="C:Program Files (x86)Microsoft SDKsWindows Phonev7.1ToolsIsolatedStorageExplorerToolISETool.exe"
set tempPath="C:TempDatabaseIS"
set dbPath="your_db_path"
set appId=your_app_id
%isePath% ts de %appId% %tempPath%
copy %dbPath% %tempPath%
%isePath% rs de %appId% %tempPath%

In the snippet above you need to replace your_db_path with the path to your .sdf file (relative to the script location) and your_app_id with Product Id of your application (you can find it WMAppManifest.xml file in the Properties subfolder of your project).

You will need another script file that will copy database to the emulator. The only difference is that you will use “xd” argument instead of “de” in lines 8 and 10.

Not hard so far, right?

Now we need to adjust the connection string, so that it points to the isolated storage in DEBUG configuration and to the content file otherwise.

#if DEBUG
    var source = "isostore:test.sdf";
#else
    var source = "appdata:/test.sdf";
#endif
    var connectionString = String.Format(
        "Data Source='{0}'; Max Database Size = 60; File Mode= Read Only;",
        source);

The last step – configuring the project file so the database file is not packed into XAP in DEBUG mode. Unfortunately, Visual Studio doesn’t allow you to specify conditions for files in the project, so you have to modify the project file manually.

Add the database file to the project and set its Build Action to Content.

Locate database file and change its build action

Save you solution, right-click the project file and select Unload Project, then right-click again and select Edit %ProjectName%.csproj.

Locate “test.sdf” string in the project file:

Change build condition for the database file

Add the Condition attribute the the Content node, so it looks like:

<Content Include="test.sdf" Condition="'$(Configuration)' == 'Release'">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>

Here we specify that file test.sdf should be included in the project as Content only when the project configuration is set to ‘Release’.

Now you can reload the project in Visual Studio. You won’t see any changes in the Solution Explorer, but try building the solution – the database will be added to XAP only in Release configuration. Don’t forget to re-launch the deployment scripts every time you uninstall the application or debug it on the new phone.

Recently I ran into an issue when trying to load the large database (approx. 50 mb) from isolated storage on WP7. Whenever I tried to query the Data Context, I got the following SqlCeException:

The database file is larger than the configured maximum database size. This setting takes effect on the first concurrent database connection only. [ Required Max Database Size (in MB; 0 if unknown) = 0 ]

After some searching over the web, I found this blog post covering some details of that parameter.

The general work-around is adding ‘Max Database Size’ value (in megabytes) to the connection string, so it looks like:

var connectionString = "Data Source='isostore:DB.sdf'; Max Database Size = 60";

Now your app should be able to init the data context.

Building cool animated tiles with Rx

Posted on Nov 15, 2011 in and tagged ,

I have a small WP7 project called Summarize. It is a fun math-based game (check it out for free).

It has a nice effect of loading tiles. Several people has asked me how I have done it, so in this post I will cover how to build something similar on your own.

Let’s start with building the UI. We have the toolkit’s WrapPanel inside the ItemsControl and a simple TileControl with TextBlock displaying the tile number.

TileControl.xaml markup:

<UserControl 
    x:Name="UserControl"
    x:Class="AnimatedTilesSample.TileControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    Loaded="OnLoaded"
    Width="76" Height="76">
    
    <UserControl.Projection>
        <PlaneProjection RotationY="-180"/>
    </UserControl.Projection>
    
    <UserControl.Resources>
        <Storyboard x:Key="LoadedStoryboard">
            <DoubleAnimation 
                Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationY)"
                Storyboard.TargetName="UserControl"
                To="0"
                Duration="0:0:0.2"/>
            <ObjectAnimationUsingKeyFrames
                Storyboard.TargetProperty="UIElement.Visibility"
                Storyboard.TargetName="Text">
                <DiscreteObjectKeyFrame 
                    KeyTime="0:0:0.1">
                    <DiscreteObjectKeyFrame.Value>
                        <Visibility>Visible</Visibility>
                    </DiscreteObjectKeyFrame.Value>
                </DiscreteObjectKeyFrame>
            </ObjectAnimationUsingKeyFrames>
        </Storyboard>
    </UserControl.Resources>
    
    <Grid 
        x:Name="LayoutRoot"
        Background="{StaticResource PhoneChromeBrush}">
        <TextBlock 
            x:Name="Text"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            Visibility="Collapsed"
            Style="{StaticResource PhoneTextTitle2Style}"
            Text="{Binding ., Mode=OneTime}"/>
    </Grid>
</UserControl>

In the code-behind we start the animation when the tile is loaded:

private void OnLoaded(object sender, RoutedEventArgs e)
{
    var sb = Resources["LoadedStoryboard"] as Storyboard;
    if (sb != null)
    {
        sb.Begin();
    }
}

MainPage.xaml markup:

<phone:PhoneApplicationPage 
    x:Class="AnimatedTilesSample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
    xmlns:local="clr-namespace:AnimatedTilesSample"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait" >
     <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid x:Name="ContentPanel" Margin="12,0,12,0">
            <ItemsControl
                x:Name="Cells">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <toolkit:WrapPanel
                            Orientation="Horizontal"/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <local:TileControl Margin="2"/>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
            
            <Button
                HorizontalAlignment="Left"
                VerticalAlignment="Bottom"
                x:Name="PlayAnimationButton"
                Content="Play animation"
                Click="PlayAnimationHandler"/>
        </Grid>
    </Grid>
 
</phone:PhoneApplicationPage>

Clicking the button generates the observable sequence producing values every 80 milliseconds. We use only the first 25 values and when the every next value is available, the appropriate tile is created and added to the list. Adding the tile starts its ‘loaded’ animation.

private void PlayAnimationHandler(object sender, RoutedEventArgs e)
{
    Cells.Items.Clear();
    PlayAnimationButton.IsEnabled = false;


    Observable.Interval(TimeSpan.FromMilliseconds(80))
        .Take(25)
        .ObserveOnDispatcher()
        .Finally(() =>
        {
            PlayAnimationButton.IsEnabled = true;
        })
        .Subscribe(x => Cells.Items.Add(x));
}

Because changes to ObservableCollection should occur only on the thread that it was created on, I used DispatcherObservable.ObserveOnDispatcher extension method that schedules all observer actions on the runtime dispatcher.

You can download sample app with sources here.

If you have changed the marketplace region as I described in the previous post and now you try updating applications that were installed in the old region, you are probably going to get the next error:

“We’re having a problem with this app or game because it no longer appears in your purchase history. This can happen if you’ve recently switched the gamertag associated with your Windows Live ID. We’re sorry, but you’ll need to buy this app or game again to continue using it.”

And now this application is stuck on your phone, you won’t be able neither to update nor to delete it.

The only work-around I’ve found so far is reinstalling the application through the web marketplace. So go to windowsphone.com/marketplace, search for your app and hit ‘Reinstall this app’ button.

When I created Zune account for my brand new WP7 device, I selected the US region – mine was not supported at that moment. Few months later, tired from not being able to buy any apps or games (my card was not accepted by the US marketplace), I decided to switch to my local region. Thankfully, Microsoft has added support for new markets prior to the Mango update.

The process if migrating your account is not so obvious. You won’t find anything related at Zune or Live websites. That can only by done through the Xbox account management (in case you didn’t know – your account is shared between Xbox Live and Zune services).

So go to your account page at https://live.xbox.com/en-US/Account, find the Manage Account Region link.

Manage account region

You will be noted that you can change the region once in 12 months and that you will not be able to re-download all your Xbox Live or Zune content, however it will not be deleted from your devices, so you can back it up.

Read that information carefully and ensure that you understand the consequences. Click Next and select the new region, then keep moving through the confirmation pages.

If everything is fine, your account will be switched to another region. Note that it may take some time to update your Zune account.

MVVM + TPL + Unit tests

Posted on Sep 27, 2011 in and tagged ,

UPD: This implementation didn’t work as well as expected, please read the follow-up article.

Recently I’ve faced an issue with writing unit tests for view model commands that use Task Parallel Library for asynchronous operations. Searching over the web was not really helpful, so I spent some time designing the approach to handle such cases.

The most common way to start tasks with TPL is by using one of the static Task.Factory.StartNew(…) methods. If you are mockist TDD guy, this should already raise an alert in your head. Static members are not test-friendly and do not allow you to inject mock dependencies into your module. One may say that using tasks is just an implementation details and should not be concerned when testing the behavior. I would agree if we were speaking about a regular async method that allows you to perform some action when its execution completes (in a form of callback or event, doesn’t really matter).

That simply doesn’t work for me because the asynchrony is used inside the command handler, so there is no way to specify the continuation outside of the view model.

My first (and probably the bad one) idea was to create a service for running the tasks. Then it would be easy to mock it and run tasks synchronously. Hopefully, I rejected this solution – creating another dependency just didn’t sound right.

I decided to create a special ICommand implementation instead that would notify me when the execution completes. This should do the trick because you can always use the thread sync primitives in the test environment to wait on the asynchronous task.

For simplicity’s sake I just created the wrapper around RelayCommand from MVVM Light Toolkit (you could use your favorite library or implement the command from scratch yourself).

public class AsyncDelegateCommand: ICommand
{
    private readonly RelayCommand _innerCommand;

    public event EventHandler ExecuteCompleted;

    public event EventHandler CanExecuteChanged
    {
        add { _innerCommand.CanExecuteChanged += value; }
        remove { _innerCommand.CanExecuteChanged -= value; }
    }

    public AsyncDelegateCommand(
        Action<AsyncDelegateCommand> executeMethod,
        Func<bool> canExecuteMethod)
    {
        _innerCommand = new RelayCommand(
            () => executeMethod(this),
            canExecuteMethod);
    }

    public void Execute(object parameter)
    {
        _innerCommand.Execute(null);
    }

    public bool CanExecute(object parameter)
    {
        return _innerCommand.CanExecute(null);
    }

    public void NotifyCompleted()
    {
        var handler = ExecuteCompleted;
        if (handler != null)
        {
            handler(this, EventArgs.Empty);
        }
    }
}

Sample application

The test harness application is fairly simple. The single button starts a ‘long-running’ asynchronous task. The button is disabled while the task is running.

public class MainViewModel : ViewModelBase
{
    private bool _isRunning;

    public bool IsRunning
    {
        get { return _isRunning; }
        set
        {
            if (_isRunning != value)
            {
                _isRunning = value;
                RaisePropertyChanged("IsRunning");
            }
        }
    }

    public AsyncDelegateCommand StartLongOperationCommand 
    { 
        get; 
        private set; 
    }

    public MainViewModel()
    {
        StartLongOperationCommand =
            new AsyncDelegateCommand(OnStartLongOperation,
                () => true);
    }

    private void OnStartLongOperation(AsyncDelegateCommand command)
    {
        IsRunning = true;

        Task.Factory
            .StartNew(() => Thread.Sleep(5000))
            .ContinueWith(task =>
            {
                // update UI

                IsRunning = false;
                command.NotifyCompleted();
            }, TaskScheduler.FromCurrentSynchronizationContext());
    }
}

The command handler starts the main task and specifies its continuation that updates the UI properties and notifies when the command is completed. Note that I used the UI task scheduler for the continuation. In this particular case it doesn’t make much sense because WPF automatically dispatches INotifyPropertyChanged events on the UI thread, but our ‘update UI’ logic could be more sophisticated (i.e. ObservableCollection modifications).

Unit Tests

[TestClass]
public class MainViewModelTests
{
    [TestMethod]
    public void CommandShouldNotifyAboutCompletion()
    {
        var context = new SynchronizationContext();
        SynchronizationContext.SetSynchronizationContext(context);
            
        bool notified = false;

        var sync = new ManualResetEvent(false);

        var target = GetTarget();

        target.StartLongOperationCommand.ExecuteCompleted += (s, e) =>
        {
            notified = true;
            sync.Set();
        };

        target.StartLongOperationCommand.Execute(null);
        sync.WaitOne();

        Assert.IsTrue(notified);
    }

    private MainViewModel GetTarget()
    {
        return new MainViewModel();
    }
}

Points of interest:

  • Using ManualResetEvent as a thread synchronization primitive. The main test thread awaits until the sync is set to signaled state on the delegate handler completion.
  • Setting current SynchronizationContext value. UI update continuation task is scheduled on the TaskScheduler, acquired from the current synchronization context. When TaskScheduler.FromCurrentSynchronizationContext is called in the test environment, it throws an exception by default (TaskScheduler.Current and TaskScheduler.Default are both set to null). So we explicitly create and set a new task scheduler.

Download sources (TaskCommands.zip)