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”.





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.





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:


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.





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.





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.”





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

27 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


    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.

  5. Hey Scott, first off this is one of the best C# tutorials I have found and I thank you for all you have done to help us all out.

    I am trying to add events in each room and dialog for some events. I have looked up the Observer pattern, which I see in some of the tutorials. I was just kind of curious about how I would go about implementing that. Do I need a different class for each room to handle these types of things? I guess I am just confused about how the flow would be able to handle the different dialog options and for example searching the room for a hidden recipe item or talking with a trader to have a hidden quest. Thank you for you time and I hope my question makes sense.

    1. You’re welcome, Matt.

      I’m thinking of two different types of events – room actions and dialogs (let me know if you are thinking of something different).

      For room actions (like finding hidden items when the player searches the room, or setting off a trap when the player enters a room), you could do something like how we added objects that implement IAction for GameItems. You could create a new IRoomAction interface. It could have an ActionType property (for example, Enter, Search, or Exit). Then, in the Location class, add a property that’s a list of IRoomAction objects (let’s call if “RoomActions”). In the player movement code, add some additional logic so when a player moves to a new location you check to see if that Location’s RoomActions has an “Enter” object. If so, perform that object’s Execute function. If the player clicks on a new “Search” button, and RoomActions has a “Search” object, you give the player an item (that was passed into the Search class’ constructor, like we pass in the weapon or potion for the IAction object for GameItems).

      Adding dialog is more complex – especially if you want to do things like not repeat conversation paths, or only have a particular conversation path available in special situations (for example, making a second quest available if the player has already completed that location’s NPC’s first quest). It gets especially difficult when the different conversation paths might have different end results (give the player an item, give the player a new quest, make the player gain or lose reputation with a group). I’ll have to think about that one.

      1. Hey Scott, im not sure if this is still relevant, but I have a idea how to implement a complex (and flexible) dialog system with different things like conditional text, dialog options, dialog branches and dialog “actions” (like add/remove player items, unlock quests etc).

        Im currently working on my own game which uses this dialog system and I could share the idea with you if you still need it.

        1. That would be great!

          I eventually (soon) want to change the SOSCSRPG project to be more of a traditional open-source project, with multiple contributors. Until I do that, if you want to share the code with me, or link to your GitHub repository (or wherever you host your code), I am sure everyone would appreciate it.

  6. 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 Studio2017CommunityCommon7IDEImageName’. WPFUI MainWindow.xaml 1
    the images are showing up and it seems to run fine. May just be an erroneous xaml error.
    VS 2017

  7. I’ve created XSD files for the XML files to enable IntelliSense when editing them:



    To use them add them to the GameData folder in the Engine project, set “Copy to Output Directory” to “Copy Always” and make the following changes to the XML files:

    Locations.xml (line 2)

    GameItems.xml (line 2)

    Next the code to read the XML files needs to be modified:

    Modified constructor of ItemFactory:

    Modified CreateWorld function of WorldFactory:

    And finally the modified LoadLocationsFromNodes function of WorldFactory:

    It should work as it did before if I didn’t forget anything…

    1. Thank you for sharing that!

      I’m finally getting over being burnt out and just recorded the next lesson (I’ll probably be able to edit and post it tomorrow). I’ve added a note to my “to do” list to include your XSD changes in an upcoming lesson.

  8. Hi Scott,

    I cannot express enough how great these tutorials have been in helping me understand OOP in c# whilst also doing something fun and interesting. I only discovered them a few days ago but I have learnt a lot in that time.

    I guess work is keeping you busy but just wanted to let you know I am looking forward to the next lesson, if you have time.

    Thanks again,


    1. Hi Tony,

      You’re welcome! I did get very busy at work, and then was burnt out for a while. But, I just finished recording the next lesson. I should be able to edit and post it Wednesday or Thursday.

  9. Scott,

    After making these changes I can no longer see the view in the XML viewer of the window.
    Program builds and runs fine as far as I can tell.

    This is the error from squiggly under <Window x:Class="WPFUI.MainWindow".
    FileNotFoundException: Could not find file 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\ImageName'.


      1. Scott,
        I’m at 16.1 now but the I still same issue as above. Grabbing the latest push will show you the issue. I put a tag when the issue started TAG: AddLocationDataToXMLFile

        1. I found a possible solution. It took a while, since I had the XAML Designer turned off in Visual Studio’s settings and in ReSharper’s settings. I normally write in the XAML text editor tab, instead of using the Design tab. The Design tab was previously extremely slow. So, most people I know turned it off.

          The error was happening because the Design mode was trying to run the FileToBitmapConverter on the Location’s and Monster’s image file – which don’t have values while designing.

          If you go to the top of Window.xaml, and change d:DataContext="{d:DesignInstance viewModels:GameSession}" to d:DataContext="{Binding GameSession}", that should fix it. This will instantiate a GameSession object (which has values in the properties) and bind to that. The “DesignInstance” just know what properties exist – it does have an object available.

          Let me know if it doesn’t.

          1. Scott,
            I agree. I collapsed the the view pane and did not notice this issue until later in the lessons and tracked back to this lesson.
            Your solution works but I loose the IntelliSense features from ViewModels which is extremely nice.
            I kept as is d:DataContext=”{d:DesignInstance viewModels:GameSession}”
            But at least now I know what the issue was.


Leave a Reply

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