Geeks With Blogs
Freestyle Coding Programming in the Real World

This post should be considered part two of the series of posts called "Recreating XNA in Windows Store Apps." I'm basically creating a DrawableGameComponent. However, let's face it, I've discovered that titles like the about gets better traffic.

When we last left, we had an object that would update itself on a background thread at a periodic rate that vaguely resembled the functionality of the game loop. Now, we need to draw the object. The good news is that WinRT will automatically update the object on the screen at 60fps, assuming you don't bog down the UI thread. The bad news is that you can't directly update objects created on or controlled by the UI thread from a background thread. This also means, from all my experimentation, if we were to use data binding, raising the PropertyChanged event from a background thread silently throws an exception.

Given this limitation, we have to pull some real tomfoolery. We would like our object to update itself. However, we need to marshal the call to the UI thread. This is done through the Dispatcher property on a UIElement. This type of call is WELL documented online. As such, I'm not going into the ins and outs of this property. It is worth noting that I store the reference to the UIElement as it's superclass, a FrameworkElement. This will be explained in the next post, where I create new elements from a background thread.

To move our sprite, I need to update it's position on the screen. Once I update the position, the default WinRT draw, that defaults to 60fps, will do the actual update. To do this, in my setup, I need to make a call to Canvas.SetTop and Canvas.SetLeft. We dispatch our update to the UI thread as we always would.

Now comes my wandering into green, squiggly lines. I intentionally do not await my call to RunAsync. This forces execution to immediately resume on my background thread. All this comes down to timing.

Let's assume I have a 150 sprites on the screen. Let us also assume that I'm trying to update all 150 of these sprites in 150 background threads that wake up every 1/60th of a second. I'm going to end up stacking up a large number of small calls to the UI thread. If I were to wait my turn, there would be a strong chance that I would miss my update call. However, since I'm closing on the reference to the screen position, it should grab the newest version when I finally gets called. This does cause a scenario where the screen doesn't necessarily match reality of the position. However, if we miss an update, we still only are 1/60th of a second behind.

Once I get the screen FULL of self-updating sprites, I do want to try to move all the unblocking asynchronous calls to a low priority. Right now, with my low item count sample code, normal priority is working well. However, I don't want to flood the UI thread so much that I can't get the needed calls in due to a metric bazillion queued tasks. In theory, it should work well. However, I haven't done it yet, so I don't want to say it still behaves as suggested. Remember, we're learning some of these things together....

The next thing I want to point out is not strange, but it is a little obscure. I am maintaining a ReaderWriterLockSlim. If you've never seen this guy, it's a special kind of lock. It will allow as many simultaneous read as possible. However, once there is a call to write, it will let all the queued reads complete and lock the object for write until it is released.

public abstract class Drawable : Updatable {
private Rect Position;
private ReaderWriterLockSlim PositionLock;
 
protected Rect ScreenBounds;
protected FrameworkElement ScreenObject;
 
protected Drawable( FrameworkElement p_ScreenObject, Rect p_ScreenBounds ) : base() {
ScreenObject = p_ScreenObject;
ScreenBounds = p_ScreenBounds;
PositionLock = new ReaderWriterLockSlim();
}
 
protected async void CallUIThread( Action p_Task ) {
await ScreenObject.Dispatcher.RunAsync( CoreDispatcherPriority.Normal, new DispatchedHandler( p_Task ) );
}
 
private double Clamp( double p_Value, double p_Min, double p_Max ) {
if( p_Value < p_Min )
return p_Min;
 
if( p_Value > p_Max )
return p_Max;
 
return p_Value;
}
 
public double X {
get {
PositionLock.EnterReadLock();
try {
return Position.X;
} finally {
PositionLock.ExitReadLock();
}
}
set {
PositionLock.EnterWriteLock();
try {
Position.X = Clamp( value, 0, ScreenBounds.Width - Position.Width );
} finally {
PositionLock.ExitWriteLock();
}
 
ScreenObject.Dispatcher.RunAsync( Windows.UI.Core.CoreDispatcherPriority.Normal, () => {
Canvas.SetLeft( ScreenObject, Position.X );
} );
}
}
 
public double Y {
get {
PositionLock.EnterReadLock();
try {
return Position.Y;
} finally {
PositionLock.ExitReadLock();
}
}
set {
PositionLock.EnterWriteLock();
try {
Position.Y = Clamp( value, 0, ScreenBounds.Height - Position.Height );
} finally {
PositionLock.ExitWriteLock();
}
 
ScreenObject.Dispatcher.RunAsync( Windows.UI.Core.CoreDispatcherPriority.Normal, () => {
Canvas.SetTop( ScreenObject, Position.Y );
} );
}
}
}

And that, as they say, is that. As always, there is a comment section to let me know what I did poorly, either in explanation or coding. Now, go forth and abuse the thread pool.

Posted on Thursday, February 7, 2013 10:03 AM .NET , C# , XNA , Win8 , WinRT | Back to top


Comments on this post: Binding to a UI Element From a Background Thread in Windows Store Apps

No comments posted yet.
Your comment:
 (will show your gravatar)


Copyright © Chris Gardner | Powered by: GeeksWithBlogs.net