In this lesson, we will finish connecting the Model (Player.cs) to the View (MainWindow.xaml). With this change, when a property value changes on the Model or ViewModel, the View will automatically update.
Summary
- An “Interface” defines the properties and functions that must exist in any class that “implements” the interface.
- It also lets other classes know how the classes with the interface will work, and how they can be used.
- Databinding does not automatically know when a property value changes in the DataContext object.
- The View can know about changes to properties, if the ViewModel (or Model) classes implement the INotifyPropertyChanged interface.
- When a class implements INotifyPropertyChanged, its properties “raise” a PropertyChanged “event”. The View “listens” for that event, and updates the UI, when it receives notification of the change.
- To make the property raise the PropertyChanged event, when it gets a new value, they cannot be auto-properties.
- Add the “using System.ComponentModel;” using directive
- We need to add extra code to the property “set”, to raise the Property Changed event, when the property is set to a new value.
- To add this extra code, we need to add a “backing variable” for the property – a variable the property uses to store its value.
- Then, we need to add a code to raise the PropertyChanged event, for anything that may be subscribed to the eventhandler, such as the View.
Source Code
Player.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Engine.Models
{
public class Player : INotifyPropertyChanged
{
private string _name;
private string _characterClass;
private int _hitPoints;
private int _experiencePoints;
private int _level;
private int _gold;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("Name");
}
}
public string CharacterClass
{
get { return _characterClass; }
set
{
_characterClass = value;
OnPropertyChanged("CharacterClass");
}
}
public int HitPoints
{
get { return _hitPoints; }
set
{
_hitPoints = value;
OnPropertyChanged("HitPoints");
}
}
public int ExperiencePoints
{
get { return _experiencePoints; }
set
{
_experiencePoints = value;
OnPropertyChanged("ExperiencePoints");
}
}
public int Level
{
get { return _level; }
set
{
_level = value;
OnPropertyChanged("Level");
}
}
public int Gold
{
get { return _gold; }
set
{
_gold = value;
OnPropertyChanged("Gold");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
MainWindow.xaml
<Window x:Class="WPFUI.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPFUI"
mc:Ignorable="d"
Title="Scott's Awesome Game" Height="768" Width="1024">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="225"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Content="Menu" Background="AliceBlue"/>
<Grid Grid.Row="1" Grid.Column="0" Background="Aquamarine">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Name:"/>
<Label Grid.Row="0" Grid.Column="1" Content="{Binding CurrentPlayer.Name}"/>
<Label Grid.Row="1" Grid.Column="0" Content="Class:"/>
<Label Grid.Row="1" Grid.Column="1" Content="{Binding CurrentPlayer.CharacterClass}"/>
<Label Grid.Row="2" Grid.Column="0" Content="Hit points:"/>
<Label Grid.Row="2" Grid.Column="1" Content="{Binding CurrentPlayer.HitPoints}"/>
<Label Grid.Row="3" Grid.Column="0" Content="Gold:"/>
<Label Grid.Row="3" Grid.Column="1" Content="{Binding CurrentPlayer.Gold}"/>
<Label Grid.Row="4" Grid.Column="0" Content="XP:"/>
<Label Grid.Row="4" Grid.Column="1" Content="{Binding CurrentPlayer.ExperiencePoints}"/>
<Label Grid.Row="5" Grid.Column="0" Content="Level:"/>
<Label Grid.Row="5" Grid.Column="1" Content="{Binding CurrentPlayer.Level}"/>
<Button Grid.Row="6" Grid.Column="1" Content="Add XP" Click="ButtonBase_OnClick"></Button>
</Grid>
<Label Grid.Row="1" Grid.Column="1" Content="Game Data" Background="Beige"/>
<Label Grid.Row="2" Grid.Column="0" Content="Inventory/Quests" Background="BurlyWood"/>
<Label Grid.Row="2" Grid.Column="1" Content="Combat/Movement Controls" Background="Lavender"/>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Engine.ViewModels;
namespace WPFUI
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private GameSession _gameSession;
public MainWindow()
{
InitializeComponent();
_gameSession = new GameSession();
DataContext = _gameSession;
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
_gameSession.CurrentPlayer.ExperiencePoints = _gameSession.CurrentPlayer.ExperiencePoints + 10;
}
}
}
NEXT LESSON: Lesson 04.1: Creating the Location class
PREVIOUS LESSON: Lesson 03.5: Displaying the Player Object
Hey Scott,
I am getting an error in the MainWindow.xaml of “MC3000 Name cannot begin with the < character, hexadecimal value 0x3C. Line 24, position 13. XML is not valid"
I tried deleting all of my code and re copying yours verbatim and it still throws this error. It runs but that error won't go away. Any tips would be appreciated!
Hi Tony,
First, please try doing a “Clean Solution” then “Build Solution”. That might eliminate the error.

If that doesn’t work, can you upload your solution (including the directories under it, and all the files in those directories) to GitHub, Dropbox, or some other file-sharing location so I can look at it?
Hello! Thank you for your tutorial. I stumbled on a very strange behavior. I have noticed that when you bind your data in XAML, Intellisense doesnt work. There are three dots under the property name and in the tooltip message box it is said that there is no context for binding. After googling how to solve a problem (many ppl have it as it seems) I found a workaround. you just put this
Inside the grid where you bind the property names.
BUT! if you leave these lines there. The update on property change isnt reflected in MainWindow. Maybe it is minor issue but I’ve spent 5 hours trying to understand where i had done wrong. Im still new to delegates and events concepts.
So if someone doesnt have Intellisense working in XAML and have lots of property name to bind. This workaround helps dont forget to remove or comment out it,
Sorry the code I posted in original message did not go through perhaps due to special symbols. After words “you just put this”
<Grid.DataContext>
<eng:GameSession>
<Grid.DataContext>
Dont forget the angular brackets
Thank you for sharing that Alex. I fixed the code part, since the website has to remove less-than and greater-than signs (it’s a potential way that hackers could inject code into the site).
Hi again, I did some additional testings on Intellisense and XAML workaround. Though original sources stated that it doesnt work. It did work for me. Instead of putting Grid.DataContext (or rather AnyContainer.DataContext) inside the immediate container. You can put it globally using Window.DataContext. You have to include xmlns key and define namespace for the class you want.
and I used (angular brackets where needed)
<Window.DataContext>
<eng:GameSession>
<Window.DataContext>
It may be underlined by compiler and it can keep saying that there is no context for that namespace, but Intellisense will work in Designer Window. To remove the namespace error, you have to fix other possible errors in your solution and build the project. When the project is built successfully the namespace error for DataContext goes away. It was all done on VS 2022.
Thanks Alex,
In one of the future lessons, we do set the DataContext in MainWindow’s XAML. The link below shows how to add that as attributes for the Window (lines 6 and 7).
https://gist.github.com/ScottLilly/c469e20d98e63cf06766463a84918df6
Again dont forget to remove or comment out Window.DataContext. Or it can make UI not updating after INotifyPropertyChange implementation in the next chapters.
I was confused at first, I was not able to implement “INotifyPropertyChanged” and I realized its because in the video it updated with a new using directive but I did not notice it, all good now. For anyone else who is lost just add it
using System.ComponentModel;
Thank you for mentioning that. I updated the instructions to include adding the “using” directive.
I had this too and was confused by it – I thought VS22 (which is what i’m running) auto-updated the include files? It did on an earlier lesson anyway.
BTW thanks Scott, I’m enjoying the lessons, it’s a fun project 🙂
You’re welcome!
I’m not sure I totally understand the purpose of backing variables, they seem sort of redundant when we already have the property declarations. Granted, I’m more used to Java where you would do something like:
private int property;
public int getProperty() {
return property;
}
public void setProperty(int value) {
property = value;
}
i.e. A private variable with public getter and setter methods that change the variable itself. Is there a reason we don’t/can’t replicate this in C#? Thanks.
We are doing the same thing as the Java example – with one difference. In our setters, we also Call the OnPropertyChanged function, so anything binding to the property (like the UI) is notified the value change and they need to deal with the change.
There are a couple syntactic differences in C# example. For the setter and getter, we don’t need to have “set” and “get” in front of the property name, and the setter does not require defining the “(int value)”, since the datatype is already declared in “public string Name”. But, it’s the same basic idea for both Java and C# – there’s a way to get and to set a property, and the value is stored in a backing variable.
man this lesson went over my head…
You might want to look on YouTube for other videos on C# PropertyChanged notifications. It’s a very common, and important, thing to understand, since many programs use it.