In this lesson, we’re going to get rid of our base class that handles PropertyChanged notifications. We’ll replace it with a NuGet package that automatically handles these notifications for all properties.
This change eliminates something I didn’t really like about the BaseNotificationClass.
Normally, inheritance is used to create child classes that are more-specific versions of the parent class. For example, the Player and Monster classes are more-specific versions of LivingEntity.
The model and viewmodel classes aren’t more-specific versions of BaseNotificationClass. They’re just classes where we want to implement this common property changed notification behavior. So, getting rid of BaseNotificationClass seems cleaner to me.
A big problem in programming is that many programmers suffer from Not-Invented-Here Syndrome – a refusal to use well-written and well-supported libraries.
Many of us like to solve the puzzles around programming and think the best decision is to always write our own code. I once worked on a project where a programmer spent a month writing a function that could have literally been done with two lines of code using a library that the project was already using. The worst part was that the hand-written code had several bugs.
So, instead of continuing to use our BaseNotificationClass, we’re going to use the Fody code weaver NuGet package. This package lets us add features that automatically are added when we compile our code. The most popular feature is to automatically raise PropertyChanged notifications.
Step 1: Add Fody NuGet packages
In the Engine, SOSCSRPG.Models, and SOSCSRPG.ViewModels projects, add these two NuGet packages:
- Fody
- PropertyChanged.Fody
Step 2: Create FodyWeavers.xml files
In the Engine, SOSCSRPG.Models, and SOSCSRPG.ViewModels projects, create a FodyWeavers.xml file with the contents below:
<?xml version="1.0" encoding="utf-8"?>
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<PropertyChanged />
</Weavers>
Step 3: Delete Engine\BaseNotificationClass.cs
Step 4: Fix classes that were using BaseNotificationClass
In the classes that were inheriting from BaseNotificationClass, make the following changes:
- Add “using System.ComponentModel;” to the using directives
- Replace BaseNotificationClass inheritance with INotifyPropertyChanged implementation
- Add “public event PropertyChangedEventHandler PropertyChanged;”
- Delete all calls to OnPropertyChanged from the setters
The files to change are:
Engine\Models\GroupedInventoryItem
Engine\Models\LivingEntity
Engine\Models\PlayerAttribute
Engine\Models\QuestStatus
Engine\ViewModels\CharacterCreationViewModel
Engine\ViewModels\GameSession
Engine\Models\Player only needs to have the call to OnPropertyChanged removed, since it’s getting the other behavior by inheriting from the LivingEntity class
Step 5: Test the game
NEXT LESSON: Lesson 19.5: Convert properties to auto-properties
PREVIOUS LESSON: Lesson 19.3: Move data and image files out of Engine project
Hi Scott,
a few lessons back, we deleted our empty (standard) constructors, leaving only the ones where we pass in parameters, like the GameSession constructor.
I don’t know if this is a .NET6 specific problem, but since i deleated these empty constructors, i get compile errors, telling me, that no standard empty constructor for a certain type (like in GameSession) was found.
We use GameSession as DataContext for our Data Binding in WPFUI, to get some intellisense. Not having an empty constructor leads to the above compile error.
Putting one back in results in NullReferenceExceptions for my GameSession Properties like “HasLocationToNorth” when starting a new game.
I am a bit confused on why this is happening, because your compiles seem to run fine without these problems.
Do you have any hints on what i could do to solve this issue?
Nevermind… i had some “stupid-code” in MainWindow.xaml that forced me to have a paremeterless constructor.
I used to have this code in there:
The resulting error:
Type ‘GameSession’ is not usable as an object element because it is not public or does not define a public parameterless constructor or a type converter.
Can’t really remember why i put it there… probably because i had some problems getting intellisense to work, before you started to implement it or show how to do it.
I am always annoyed from WPF not showing intellisense and allways having to guess if the property is typed correctly.
Ok for some reason unknown, my XAML code got swallowed by the webpage. xD
Doesn’t want me to inject code into your page i guess, lol
Lets try without the brackets…
Window.DataContext
viewmodel:GameSession
Window.DataContext
Any comments that have a less than sign in them get modified, since that’s a potential was for someone to do XSS (Cross-Site Script injection) in the site. The best way to share source code in a comment here is probably to post it at https://gist.github.com/, then share the link to the gist page.
Thanks, thats good to know.
I don’t think Visual Studio has ever been perfect with XAML editing – especially IntelliSense. That’s one area I wish it would improve. I haven’t tried VS Code or JetBrains Rider, but they might handle XAML editing better.
Interesting… coming up against my own issue in Main Window, also using .NET6 –
System.Windows.Markup.XamlParseException: ‘No Matching constructor found on type WPFUI.MainWIndow’ (Even though I do have a (player, x, y) constructor which should be valid.
I thought I was being clever choosing a LTS version in .NET6 but seems I may have shot myself in the foot.
Josh, have you tried this (https://stackoverflow.com/questions/24251244/wpf-and-unity-no-matching-constructor-found-on-type). I seem to remember encountering that error message in another app and removing the startupURI in App.xaml eliminated the error message.
What are your thoughts on the CS0067 warning messages in Visual Studio that the various PropertyChanged events are never used? It’s a false-positive because of switching to Fody, but it clutters up Error List.
One option is to wrap the various PropertyChanged with #pragma warning disable 0067 //Fody to leave a comment for our future selves that it’s being handled by Fody, but this seems to add code clutter.
Another option is to disable 0067 from Error List with Silent or None but this feels like more of a hidden change that could bite us later.
I’d go with setting it to Silent or None. I agree with you that the #pragma in every model class will start to clutter up the code – and that’s something I never like.
I generally don’t use a lot of events in my programs, and probably won’t run into too many places where that particular warning would be helpful. You’d also probably notice unused events when refactoring and seeing that the event has 0 references.
From what I understand, the people who originally came up with Object-Oriented Programming expected events to be used much more than they usually are nowadays. But, most programs I’ve seen that used events for communication turn into a mess, with the event orders almost becoming non-deterministic. One project I worked on had screens that were almost like a spreadsheet. If you changed a value in one textbox/property, it would fire an event that changed values in other properties – which would fire events that would change values for other properties, and so on. It was a nightmare trying to figure out what was happening, and when it was happening.
Hi Scott,
I had an issue at runtime with the XCOPY command, simple fix was to add /y at the end as apparently it *may* fail with exit code 2, and I thought maybe it was because the files already were there.
Thanks!