Press "Enter" to skip to content

Lesson 19.4: Replace BaseNotificationClass with Fody PropertyChanged Notification

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:

  1. Fody
  2. 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:

  1. Add “using System.ComponentModel;” to the using directives
  2. Replace BaseNotificationClass inheritance with INotifyPropertyChanged implementation
  3. Add “public event PropertyChangedEventHandler PropertyChanged;”
  4. 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

12 Comments

  1. Manuel Ketisch
    Manuel Ketisch 2022-06-13

    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?

    • Manuel Ketisch
      Manuel Ketisch 2022-06-13

      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.

      • Manuel Ketisch
        Manuel Ketisch 2022-06-13

        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

        • SOSCSRPG
          SOSCSRPG 2022-06-13

          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.

          • Manuel Ketisch
            Manuel Ketisch 2022-06-14

            Thanks, thats good to know.

      • SOSCSRPG
        SOSCSRPG 2022-06-13

        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.

    • Josh Folsom
      Josh Folsom 2022-12-06

      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.

  2. Mike Kenig
    Mike Kenig 2022-07-31

    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.

    • SOSCSRPG
      SOSCSRPG 2022-07-31

      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.

  3. Christophe Kowarski
    Christophe Kowarski 2023-02-01

    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.

Leave a Reply to SOSCSRPG Cancel reply

Your email address will not be published. Required fields are marked *