Logo Subtitle Subtitle

Windows Phone 7 Mahjong Solitaire

Mahjong Solitaire is available for download at the Windows Marketplace

 

The last few weeks I’ve been spending every spare hour on my Mahjong Solitaire game
for the windows phone. A few days ago I submitted my first version to the Market Place and
this morning I received good news: Congratulations Loek Ouweland of Toverstudio!
Mahjong Solitaire has successfully passed certification for Windows Phone Marketplace!

I have recorded a screencast in the emulator so you can see what it’s all about.
Please excuse me for not showing where the screen has been clicked.

Mahjong Solitaire running in the emulator

Install Microsoft Silverlight

To try and or buy the game from the Zune-software, you need a US live ID. This is what you should see:

If you don’t have Zune, you can find some screenshots at the Microsoft Marketplace Browser.

Thank you Windows Phone developers in the Netherlands (#wp7nl) for answering my questions about submitting the game. And thank you Lars for testing and provide me with useful feedback!

WP7: Add User Controls (graphics) in background thread

Download demo project: MultiThreadGraphics

When time-consuming operations are executed on the Main Thread on WP7, the user interface can
stop responding to user input and even freeze so it won’t update bindings, visual states or progress bars.

Time-consuming operations

I’m working on a Mahjong game that adds 144 image-holding usercontrols to a canvas. I do this
in a loop and during the loop, my user interface freezes.
Not even the command progressbar.Visibility = System.Windows.Visibility.Visible; is executed until the operation finishes. Isn’t that weird?

Please download and run the MultiThreadGraphics demo project and click on the Sync button.
This code is being fired:

private void ButtonSync_Click(object sender, RoutedEventArgs e)
{
    progressbar.Visibility = System.Windows.Visibility.Visible;
    canvas.Children.Clear();
    for (int i = 0; i < 30; i++)
    {
        for (int j = 0; j < 40; j++)
        {
            var star=new Star();
            star.SetValue(Canvas.LeftProperty, i * 15d);
            star.SetValue(Canvas.TopProperty, j * 15d);
            canvas.Children.Add(star);
        }
    }
    progressbar.Visibility = System.Windows.Visibility.Collapsed;
}

This is the response:

When the loop is finished, all the stars are on my screen, but the user had no idea what happened.
We need a way to execute the code in a different thread, so the main thread can show the progress bar. Enter the background worker.

Background worker

.NET provides the very handy Background Worker Class to execute code in a different thread.
We will start by creating a new Background Worker. The DoWork event is called by the RunWorkerAsync method and the RunWorkerCompleted event is being fired when the operation has been completed.

private void ButtonAsync_Click(object sender, RoutedEventArgs e)
{
    var bw = new BackgroundWorker();
    bw.DoWork += (s, a) =>
        {
            for (int i = 0; i < 30; i++)
            {
                for (int j = 0; j < 40; j++)
                {
                    Dispatch(i, j);
                }
            }
        };
    bw.RunWorkerCompleted += (s, a) =>
        {
            progressbar.Visibility = System.Windows.Visibility.Collapsed;

        };

    progressbar.Visibility = System.Windows.Visibility.Visible;
    canvas.Children.Clear();
    bw.RunWorkerAsync();
}

We cannot access the Canvas from the background worker so we use Dispatcher.BeginInvoke.
In my case the Dispatcher.BeginInvoke must be in a different method outside the DoWork delegate.
If I place it inside the delegate all stars will be at the same position because every i and j will have the last value from the for-loop.

private void Dispatch(int i, int j)
{
    Dispatcher.BeginInvoke(() =>
        {

            var star = new Star();
            star.SetValue(Canvas.LeftProperty, i * 15d);
            star.SetValue(Canvas.TopProperty, j * 15d);
            canvas.Children.Add(star);
        });
}

If you do not use the Dispatcher.BeginInvoke but add the user controls directly to the canvas, the compiler will not complain but at run time you will get the error:
Invalid cross-thread access.

I hope this simple example shows you how you can use the Background worker to delegate time-consuming processes to different threads and inform the UI when the operation has been completed. In my demo project I have used Jeff Wilcox’ excellent PerformanceProgressBar for a better user experience.