Lesson 14.3: Read World (Location) data from an XML file

I originally wanted to convert all the factories to use XML files in this lesson. However, there is a lot of extra work to handle the location image files. So, this lesson will cover the locations.

The next lesson will cover the monsters. There are some extra changes we’ll need to make for the loot items. A third lesson will handle all the other factory classes – their changes are simple.

 

 

 

Lesson Steps

Step 1: Create Engine\GameData\Locations.xml

This is the XML file for the Location object data.

Notice there is an attribute “RootImagePath” at the top-level “Locations” node. Because all the images will be in this one directory, I put this attribute here. We’ll read it once and add it to all the image file names in the “Location” nodes.

Because the Description value will be so long, I made it a child node, instead of making it an attribute. This is only a personal preference, so the “Location” node would not be an extremely long line.

Because “Description” is a child node, I put the text in between its opening and closing tags, instead of putting the description in another attribute. The text is surrounded with “<![CDATA[” and “]]>”. This is in case the description text contains anything that might look like valid XML. We want the text to be treated as text, and not as XML.

Each “Location” node has the “Description” child node. It can have a “Trader” node, and “Monsters” or “Quests” children.

 

NOTE: Remember to right-click on Locations.xml and set its “Copy to Output Directory” property to “Copy Always”.

 

Locations.xml

 

 

Step 2: Modify Engine\Models\World.cs

The original AddLocation function took several parameters, constructed a Location object, and added the Location object to _locations. Now, we’re going to completely construct the Location object in WorldFactory. So, I changed AddLocation to accept the completely-built Location object.

 

World.cs

 

 

Step 3: Modify Engine\Factories\WorldFactory.cs

The changes here are like what we did to the ItemFactory class.

We read the XML file, get the RootImagePath value, and parse the Location nodes.

One thing to notice is the ImageName value in the call to the Location constructor (line 51). Before, the image files were included as resources in the compiled program. The value looked like this:

$”/Engine;component/Images/Locations/{imageName}”

Now, it uses a directory path, relative to where the game is running (because of the “.” at the beginning of the file name string. In the next step, we’ll change the project to read the images from external files, instead of program resources.

This will let you send out a new Locations.xml file, or new images, to expand the game world – without needing to rebuild the program and have the player completely re-install the game.

 

WorldFactory.cs

 

 

Step 4: Modify the .png files in Engine\Images\Locations

Right-click on these files, change “Build Action” to “None” and “Copy to Output Directory” to “Copy Always” – just like we do with the XML data files for the factories.

 

NOTE: You can shift-click to select all the image files and change all their “Build Action” and “Copy to Output Directory” values at the same time.

 

Step 5: Create a new folder WPFUI\CustomConverters and a new class WPFUI\CustomConverters\FileToBitmapConverter.cs

When the location images were project resources, it was simple to display them in an UI Image control. Now that they are external files, we need to do an extra step to get them into the Image control.

With WPF, it is common to create custom converter classes when you want to use data differently from how it is stored. A common converter is converting a Boolean property to a Visibility value (Visible, Hidden, or Collapsed). This way, you can hide or show different parts of the UI, based on a property value.

Another common one is to convert a bound Boolean property to display a text value of “Yes” or “No”.

This converter class will let us bind the Image control to a file path (the location image file), read the file from disk, and convert it to a BitmapImage object – which can be displayed in a WPF Image control.

 

Custom converter class must implement the “IValueConverter” interface (line 9), which requires the class to have Convert and ConvertBack functions.

We are only going to convert from a file path and name to a BitmapImage, so we can have the ConvertBack function (lines 31-34) just return null.

 

In the Convert function, we try to cast the “value” parameter to a string and store it in the “filename” variable (line 16). The “value” parameter is the value of the bound property in the UI’s Image control, for this program.

 

If the value is a string, we’ll see if we have a DIctionary entry whose key is the filename.

Notice the “_locations” dictionary (lines 11-12) is static. That means it will be shared by everything that calls this FileToBitmapConverter class. This is a way to cache the images, so we don’t need to re-read the file from disk each time the player returns to a location.

If the location (image file path and name) is not already in the Dictionary, we create a new BitmapImage object from the file (lines 24-25) and add the location file name and BitmapImage to “_locations”, so it will be available the next time the player moves to this location.

Finally, on line 28, we get the BitmapImage object from the “_locations” cache, to populate the Image control’s Source property.

 

FileToBitmapConverter.cs

 

 

Step 6: Modify WPFUI\App.xaml

To use the FileToBitmapConverter in a WPF window, we’ll add it as an Application Resource (lines 7-9). Now, we can use this converter in any WPF control in this project.

On line 4, we create “converters” as the XML namespace for our customer converters. Then, on line 8, we say, “In the ‘converters’ namespace, we have the FileToBitmapConverter. Create a key (name) we can use when we want to use this converter in a WPF window/control.”

 

App.xaml

 

 

Step 7: Modify WPFUI\MainWindow.xaml

Finally, add the converter to the Image control (lines 115-116). Now, when the program tries to bind the CurrentLocation.ImageName value to the Image control, it will first convert it with FileToBitmapConverter.Convert().

This is how we go from a file on disk to a graphic displayed in the UI.

 

MainWindow.xaml (lines 110-116)

 

 

Step 8: Test the game

 

Return to main page

2 thoughts on “Lesson 14.3: Read World (Location) data from an XML file

  1. I’m getting a wierd xaml error,I wondered if anyone else is seeing it.

    Error Could not find file ‘C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\ImageName’. WPFUI MainWindow.xaml 1

    the images are showing up and it seems to run fine. May just be an erroneous xaml error.

    VS 2017

    Thanks

    1. When do you see the error? When you’re building the program, or when you’re running it?

      If you want, you can upload your solution (including the directories under it, and all the files in those directories) to GitHub or Dropbox, so I can look at it?

Leave a Reply

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