Databinding to CLR Objects

I'm sure everyone else runs into these issues all the time, but every time I have to do this, I end up pulling up MSDN and looking all of this up. It's not too efficient, so I'm going to drop a few points here for reference.

First: Binding a basic type (String, Int, etc...)

For starters, if the CLR object is supposed to have properties that you're going to bind to, then it needs to implement INotifyPropertyChanged. VS makes it easy to implement the interface, just right click and tell it to implement, and it puts the default methods in.

Then, add this code:

private void NotifyPropertyChanged(string info)

{

      if (PropertyChanged != null)

      {

            PropertyChanged(this, new PropertyChangedEventArgs(info));

      }

}

Now, with this code in place, whenever you change the value of any property that you're going to be databinding to, you just need to call this.NotifyPropertyChanged(property). So, for instance, if the player gets 5 more points, just do score += 5 and this.NotifyPropertyChanged("Score");

Second: Binding a List

Then, in the game I'm working on, there is a list of words that the user has already found. I want to display that list in a list box. I thought I could just do a public property the looks like:

public List<string> MyList

{

      get

      {

            return this.myList;

      }

}

This seemed straightforward, as I bound the ItemsSource of my ListBox to this property. Problem was, when I updated this.myList, and even when I called PropertyChanged, my ListBox didn't update. I dug around for a while, and finally checked with a Dev, and discovered that there is another class ObservableCollection<string> that fixes everything. In fact, you don't even need to do the PropertyChanged when you bind a ListBox to an ObservableCollection. So, in the above case, I just used:

public ObservableCollection<string> MyList

{

      get

      {

            return this.myList;

      }

}

Third: Connecting the application's DataContext

For this one, I've found two different ways to do the same thing, and I'm not sure which is correct (or more 'proper').

So, here's the scenario. I've got this engine for my "WordGame", which is a class. I want to databind controls on my application to the fields in the WordGame object, and I need to call methods on it. The best way to do this is to bind the DataContext to an instance of WordGame, and then bind everything through ExplicitDataContexts. But, the tricky part comes when I want to call a method on the WordGame object itself. In the event handler for my Button, I don't have direct access to the object. For sake of conversation, the method is called "StartNewGame()" on WordGame to begin the whole process.

Option 1:

In Blend, Add the CLR Object as a data source, and bind the DataContext of the LayoutRoot to the datasource. Then, I can drop a TextBlock down and data bind the text to the ExplicitDataContext, and I see all the fields that I want to bind to. Ok, great. Now, in my Window1.xaml.cs, I have to add this property:

private WordGame myGame = null;

public WordGame MyGame

{

      get

      {

            if (this.myGame == null)

            {

                  this.myGame = this.LayoutRoot.GetValue(Grid.DataContextProperty) as WordGame;

            }

            return this.myGame;

      }

}

Now, in any of my event handlers, I can access this.MyGame and get the desired behavior.

Option 2

In the Constructor for my Window, I added this code:

private WordGame myGame = null;

public Window1()

{

      this.InitializeComponent();

      this.myGame = new WordGame();

      this.LayoutRoot.DataContext = this.myGame;

      // Insert code required on object creation below this point.

}

This looks simpler at first, but the BIG problem is that since the actual DataBinding happens in the constructor, Blend doesn't recognize it as design time, so I can't do any of my databinding using the Property Markers and such.

So, in this case, I'm happiest with option 1.

I also want to include my Timer code, but I'll do that in a seperate post.