Lesson 07.3: Sending Messages from the ViewModel to the View

When the player fights monsters, we will need to display many messages in the UI – if the player hits the monster, how many hit points of damage the player does, how many points of damage the monster does, etc.

In this lesson, we’re going to create a way to send messages from the GameSession (ViewModel), to the UI (View). We’ll do this by having the ViewModel “raise an event”, and have the View “subscribe” to that event.

NOTE: This technique of have an object raise an event is known as publish/subscribe, or the Observer design pattern.

 

 

Steps

 

Step 1: Edit \WPFUI\MainWindow.xaml

On the screen, in the left part of the Gameplay section (upper-right quadrant), we have space for the game messages. We’ll add a RichTextBox control, surrounded by a border. This is at lines 76-91, in the source code below.

A RichTextBox control is like a small word processor. We can add text to it, and modify how the text is displayed (size, color, if it is bolded, etc.). We’ll give it an x:Name attribute. This lets us access it through code, in the MainWindow.xaml.cs file.

The RichTextBox control has default formatting, and we want to override the space it adds between each paragraph. Normally, it has a large margin. Because we are only going to display our game messages one line at a time, we want to have a margin of 0 – without any extra space between the messages.

 

MainWindow.xaml

 

 

Step 2: Create a new “EventArgs” folder, in the Engine project, and create a new class in that folder – GameMessageEventArgs.cs

The ViewModel is going to communicate with the View by “raising events”. This will let the View know that something happened. But, the View also needs to know what text to display on the screen.

To send additional information with an event, you use an “event argument”. We’re going to create a custom event argument that will hold the text to display in the View.

To create a custom event argument, you only need to create a new class and make it inherit from System.EventArgs. Our custom event argument class will be GameMessageEventArgs.

Next, we’ll give the class a Message property, which will hold the message text to display.

Finally, we’ll add a constructor that accept the message as a parameter and sets the Message property’s value.

 

So, when we raise the message event, we’ll instantiate a new GameMessageEventArgs object, with the message text, and pass that object by the event.

 

GameMessageEventArgs.cs

 

 

Step 3: Edit \Engine\ViewModels\GameSession.cs

Now that we have a class to hold the message, we can modify the GameSession class to send out messages for the View to display.

The first step is to create a public EventHandler<GameMessageEventArgs> named OnMessageRaised (see line 11). This is how the View “subscribes” to the event – like you might subscribe to a weather alerts on a smartphone. The “<GameMessageEventArgs>” tells the subscribers what type of event arguments to look for, so they can use the data passed in the event.

 

Next, we will create the RaiseMessage function (line 160). When the ViewModel wants to raise the event, it will call this function.

The code checks if OnMessageRaised has any subscribers (it will be “null”, if there are no subscribers). If there are subscribers, it will invoke the event, passing a new GameMessageEventArgs object that has the desired message text.

 

To test the event, we’ll add a message when the player moves to a new location and encounters a monster.

We can do this by modifying the CurrentMonster setter (lines 49-53). If the CurrentMonster is not null (so, there is a Monster object in CurrentMonster), we will call the RaiseMessage function with an empty string (to put a blank line in the UI). Then, we’ll call RaiseMessage again, with a message that tells the player what type of monster is at the location.

 

So, the flow will work like this:

  • The player moves to a new location
  • The location has a monster
  • CurrentMonster is set to the location’s monster
  • The CurrentMonster setter call RaiseMessage
  • RaiseMessage sends the GameMessageEventArgs object to any objects subscribed to OnMessageRaised

 

GameSession.cs

 

 

Step 4: Edit \WPFUI\MainWindow.xaml.cs

Now that the ViewModel can send messages, we need the View to watch for these messages – “subscribe” to the eventhandler.

In the constructor (line 21), we have the View subscribe to the eventhandler. This line says, “when _gameSession raises an OnMessageRaised event, run the OnGameMessageRaised function (the View’s function to do something, when it sees this event raised)”.

 

On line 46, we have the new OnGameMessageRaised function. This automatically has two parameters: the sender (the object that raised the event), and the event arguments (in this case, its datatype is GameMessageEventArgs, since we defined that in GameSession, with “EventHandler<GameMessageEventArgs>”.

When this function notices an event was raised, it will take the GameMessageEventArgs object, get the value of its Message property, format it (adding a “run” of text to a new paragraph, and adding it to the RichTextBox document), and scroll to the bottom of the RichTextBox (so the user can always see the latest message.

 

MainWindow.xaml.cs

 

 

Step 5: Test the game

Run the game, and move to the locations that have monsters. When you see the image of a monster, you should now see a message in the RichTextBox that tells the player what type of monster they encountered.

In the next lesson, we will let the player fight the monster.

 

 

Return to main page

 

Leave a Reply

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