Data Binding to Attached Properties
- by Chris Gardner
Originally posted on: http://geekswithblogs.net/freestylecoding/archive/2013/06/14/data-binding-to-attached-properties.aspx
When I was working on my C#/XAML game framework, I discovered I wanted to try to data bind my sprites to background objects. That way, I could update my objects and the draw functionality would take care of the work for me. After a little experimenting and web searching, it appeared this concept was an impossible dream.
Of course, when has that ever stopped me?
In my typical way, I started to massively dive down the rabbit hole. I created a sprite on a canvas, and I bound it to a background object.
<Canvas Name="GameField" Background="Black">
<Image Name="PlayerStrite" Source="Assets/Ship.png" Width="50" Height="50" Canvas.Left="{Binding X}" Canvas.Top="{Binding Y}"/>
</Canvas>
Now, we wire the UI item to the background item.
public MainPage() {
this.InitializeComponent();
this.Loaded += StartGame;
}
void StartGame( object sender, RoutedEventArgs e ) {
BindingPlayer _Player = new BindingPlayer();
_Player.X = Window.Current.Bounds.Height - PlayerSprite.Height;
_Player.X = ( Window.Current.Bounds.Width - PlayerSprite.Width ) / 2.0;
}
Of course, now we need to actually have our background object.
public class BindingPlayer : INotifyPropertyChanged {
private double m_X;
public double X {
get { return m_X; }
set {
m_X = value;
NotifyPropertyChanged();
}
}
private double m_Y;
public double Y {
get { return m_Y; }
set {
m_Y = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged( [CallerMemberName] string p_PropertyName = null ) {
if( PropertyChanged != null )
PropertyChanged( this, new PropertyChangedEventArgs( p_PropertyName ) );
}
}
I fired this baby up, and my sprite was correctly positioned on the screen. Maybe the sky wasn't falling after all.
Wouldn't it be great if that was the case?
I created some code to allow me to move the sprite, but nothing happened. This seems odd. So, I start debugging the application and stepping through code. Everything appears to be working. Time to dig a little deeper.
After much profanity was spewed, I stumbled upon a breakthrough. The code only looked like it was working. What was really happening is that there was an exception being thrown in the background thread that I never saw. Apparently, the key call was the one to PropertyChanged. If PropertyChanged is not called on the UI thread, the UI thread ignores the call.
Actually, it throws an exception and the background thread silently crashes. Of course, you'll never see this unless you're looking REALLY carefully.
This seemed to be a simple problem. I just need to marshal this to the UI thread. Unfortunately, this object has no knowledge of this mythical UI Thread in which we speak. So, I had to pull the UI Thread out of thin air.
Let's change our PropertyChanged call to look this.
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged( [CallerMemberName] string p_PropertyName = null ) {
if( PropertyChanged != null )
Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync( Windows.UI.Core.CoreDispatcherPriority.Normal, new Windows.UI.Core.DispatchedHandler( () => {
PropertyChanged( this, new PropertyChangedEventArgs( p_PropertyName ) );
} ) );
}
Now, we raised our notification on the UI thread. Everything is fine, people are happy, and the world moves on.
You may have noticed that I didn't await my call to the dispatcher. This was intentional. If I am trying to update a slew of sprites, I don't want thread being hung while I wait my turn. Thus, I send the message and move on.
It is worth nothing that this is NOT the most efficient way to do this for game programming. We'll get to that in another blog post. However, it is perfectly acceptable for a business app that is running a background task that would like to notify the UI thread of progress on a periodic basis.
It is worth noting that this code was written for a Windows Store App. You can do the same thing with WP8 and WPF. The call to the marshaler changes, but it is the same idea.