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

12 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?

    2. I am getting the same issue as Gary Smith:

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

      Before starting the program, I had 3 errors regarding this line ‘ converters:FileToBitmapConverter x:Key=”FileToBitmapConverter” ‘ in App.Xaml. It seems that it couldn’t find FileToBitmapConverter inside of WPFUI.CustomConverters. Although, the 3 errors disappear upon running the program, and are replaced only with the error message above.

      I hope this information is helpful.

      1. Thank you. In Visual Studio, the XAML files sometimes show errors when you add something new, but have not built (or run) the program. Sometimes, the XAML editor is not smart enough to recognize new classes or properties.

        1. Hey Scott, it looks like the error must be a Visual Studio issue.  I’m using VS 2017 Community and I noticed that when I pulled in the latest updates and built and run the project that the error was gone.  I suspected that might be the problem because I knew I had followed that lesson correctly and everything built and run correctly.

          So, everyone using VS 2017 Community should probably take a moment to download any updates before they start a lesson.

          If that doesn’t fix it Scott, I’ll upload it on GitHub and let you take a look.

           

          Thanks for a really great tutorial!

           

      2. At first thanks Scott for this tutorial.

        The issue is at design time. In this case the converter try to get the images from the ide directory and that fails.
        A quick fix is to set the datacontext at design time to nothing.

  2. Hi Scott. I just wanted to say thanks for this great tutorial. I’m only on lesson 5.2 but wanted to look ahead to make sure you were still going. The tutorial is a great introduction to WPF, XAML, and C# (not to mention general programming logic). It is much appreciated!

    1. You’re welcome. I am still working on it, but haven’t been able to record the next lesson yet. I recently started a new job and have been re-adjusting my schedule (especially trying to wake up at 5:00 in the morning). I’m going to try to get the next lesson out this week.

  3. Hey Scott.

    Sorry for being a bit incompetent here, but I can’t really figure out which new item I’m supposed to add or where when creating the App.xaml , so I’m slightly stuck right now.

    Also wanted to say thanks for the great lessons – I’ve learned a lot thanks to you!

    1. You’re welcome, Martin!

      The App.xaml file should exist in the WPFUI project. Visual Studio should have automatically created it when it created the WPFUI project. If you edit that file, you should just need to add line 8.

      App.xaml file, in WPFUI project

      Let me know if that didn’t answer your question.

  4. I figured it out with the help of a friend (sometimes I just don’t see what’s right in front of me!).

    And you’re 100% correct. It was there all along!

    Looking forward to the next lesson!
    Until then I’ll keep trying to expand the game and put my own personal touch on it.

Leave a Reply

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