Mobile Zone is brought to you in partnership with:

Den is a DZone Zone Leader and has posted 460 posts at DZone. You can read more from them at their website. View Full User Profile

Making of - The first version of the presentation layer for Windows Phone 7

01.25.2011
| 7245 views |
  • submit to reddit

There are tons of question floating around the Internet of users asking how to take screenshots from a Windows Phone 7 application or how to capture the system screen. The sad (or, let's say - not so good) news is that there is no such functionality available now for regular developers and the actual tool you've probably seen at PDC (or any other presentation from a Microsoft official, for that reason) with a WP7 device connected to the computer and the screen being shown on the desktop machine is an internal application that is not distributed at the moment.

While it is not possible to track the system screen, it is possible to do this in a separate Windows Phone 7 application, assuming that you are the actual developer of that application. I briefly mentioned this idea on my blog with a video that shows the actual prototype working. In this article I am actually describing the code side of it.

The basic architecture was pretty obvious to me - I needed a Windows Phone 7 application, preferably Silverlight-based for now, a web service (used WCF) and a client desktop application that will actually receive the graphic content. However, there was an interesting moment - how do I organize a constant loop that will not hang the application on the phone? My initial thoughts were linked to some of the events that are triggered on application startup, however there isn't an event that is running once the application is started for the first time, so this was crossed out.

DISCLAIMER: The approach I used below is the first draft and is not completely optimized.

I could always use the application constructor for this task, but I would need to actually trigger the sending process somehow. Starting the action instantly in the constructor isn't that good of an idea because it will hang the UI. In this case, I will start the action in the Page_Loaded event handler for the main page.

The actual method that will start the sending process looks like this:

public static void StartSending()
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerAsync();
}

Obviously I cannot keep the sending process on the main thread because there will be a performance hit - the UI will become unresponsive and there will be one constant image sent.

Here is what I had for DoWork:

static void worker_DoWork(object sender, DoWorkEventArgs e)
{
IScheduler sched = Scheduler.ThreadPool;

while (true)
{
sched.Schedule(new Action(() =>
{
MemoryStream s = new MemoryStream();
d.BeginInvoke(new Action(() =>
{
WriteableBitmap b = new WriteableBitmap(RootFrame, null);

b.SaveJpeg(s, 480, 800, 0, 20);

MainServiceRef.Service1Client c = new MainServiceRef.Service1Client();
c.SetDataAsync(s.GetBuffer());
}));
}));

Thread.Sleep(1000);
}

}

Even inside a background thread, I am using the scheduler to put new threads in the thread pool. This will reduce the effects on the application activity - initially I had an annoying response delay but I seem to have fixed it (or at least reduced it to a level where the user won't be able to notice it).

To make the loop constant, there is while (true). For each separate iteration I am scheduling a new action that is invoked on the local dispatcher. By the way, the dispatcher is initialized on the main page loading. In the App class I have this:

public static Dispatcher d;

Now here is what I have in MainPage_Loaded:

void MainPage_Loaded(object sender, RoutedEventArgs e)
{
App.d = Dispatcher;
App.StartSending();
}

That way, I can invoke UI-related actions on a non-UI thread. 

So what I am getting is a WriteableBitmap instance based on the current frame (represented as RootFrame). You might ask - why not work with separate pages? The frame itself is a UIElement that hosts pages; therefore it makes sense to simply grab the frame image with whatever page is in it - that way you won't have to check the current page and so on.

I thought about passing the WriteableBitmap directly to the service, or even the PhoneApplicationFrame instance, but both these types are not serializable therefore it would be an overkill to try and push those directly. Instead, I am getting the JPEG-encoded representation in the application and then send the binary array to the service. Notice that I am reducing the encoding quality to 20% in the SaveJpeg method to avoid sending HQ images that are not needed. Of course you can do that, but it works much faster with a lower quality image, and, as you've probably noticed in the video I showed, you won't see a big difference.

Also, there is a one second thread sleep to avoid a complete thread freeze. That way, there is a time reserve that helps resume the input process so the user can actually do something in the app.

The service itself is as basic as it can be:

public class Service1 : IService1
{
public static byte[] b { get; set; }

public void SetData(byte[] stream)
{
b = stream;
}

public byte[] GetData()
{
return b;
}

}

It all abides to one interface:

[ServiceContract]
public interface IService1
{
[OperationContract]
void SetData(byte[] element);

[OperationContract]
byte[] GetData();
}

The client application is a sample WinForms project that has a Timer component that has a Tick event handler set to this:

private void timer1_Tick(object sender, EventArgs e)
{
byte[] set = (new ServiceReference1.Service1Client()).GetData();
MemoryStream stream = new MemoryStream(set);

Image mainImage = Image.FromStream(stream);

if (mainImage != null)
{
pictureBox1.Image = mainImage;
}
}

Binary data transformed to an Image instance. Simple as that. Via the same service. Congratulations! Now you know how my first implementation looks like.