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.
Scott, great project. I followed the previous one too.
However, I’m getting an
[ Invalid expression term ‘.’ ]
[ Syntax error, ‘:’ expected ]
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
For the PropertyChanged?.Invoke(… which has a red squiggly line under it.
I’ve followed the video through to conclusion but cannot see what has been changed to compensate for this error
Hi Gary,
I think the only place where this expects a “:” is on line 10 of the Player class, as a separator before the INotifyPropertyChanged interface. If that doesn’t help, can you please upload your code for these classes somewhere that I can look at it? https://gist.github.com is a place I like, but any place I can get the files will work.
I have uploaded the files to this location I hope that helps
https://gist.github.com/GaryRWoodward/fde69d856ef146460195c735a5e12792
Yours
Gary
Ps I have put the whole project into a zip file if that helps. could I email it to you?
I didn’t see an obvious source of the error in the gist files, so it might help to send me the zip file. Please send it to github@soscsrpg.com
Hi Scott
I got your email reply. Sent you a followup.
Having started from scratch, I realised that there was an error in session 3.5 which probably caused the issue. Have it all worked out now though.
On this session I am getting the error message:
[ CS1061 “‘MainWindow’…definition for ‘ButtonBase_OnClick’ and no accessible extension method…” Line 50.]
It runs great and the XP increments perfectly but.. ?
Is this error ignorable or is it an indicaton of more problems latter?
That probably would have happened if you accidentally double-clicked on a button while in the WPF design mode screen in Visual Studio.
Visual Studio tries to help you and will automatically create an “even handler” if you double-click on something in design mode. The event handler a way to communicate between things. It’s commonly called a “publish/subscribe” pattern. Something in the program “publishes” a message, and everything “subscribed” to the event can do something. In this case it would be the clicking of the button in the UI and the function to run.
In Visual Studio, in the Solution Explorer, look for MainWindow.xaml.cs (you may need to click the symbol next to MainWindow.xaml to see it). You can go in there, find the ButtonBase_OnClick (it’s probably on line 50) and delete that line.
Let me know if that doesn’t work. We go into event handlers in a later lesson.
I have a red underline in InitializeComponent();
CS0103: The name ‘InitializeComponent’ does not exist in the current context
Hello Yago,
Can you please 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?
If you haven’t used GitHub before, here is a video on how to upload your solution to GitHub and share it with me. https://youtu.be/0si9ElYQv8I
Here
https://github.com/gyn13/C-WPF-RPG
Hi Yago,
The GitHub repository is missing the Engine directory and its files. Can you add those to the repository, or put all the files some place I can download them?
enjoying the tutorial so far. out of curiosity why are we not using a constructor for the player class?
Thanks! We do use constructors in a future lesson.