The simple answer is that the file you are trying to load must not actually exist in the location you are trying to load it from!
And yet people sometimes get stuck on this error, unable to open their file and with no idea how to figure out why this is failing. I suspect this is a side effect of the Content Pipeline being so automated in XNA. When the usual experience is to just drop an image into Visual Studio, then ContentManager.Load it into your game, there is no need to learn the details of what happens to the file in between. But if you do not know this, you will not have the tools to debug when things go wrong, or to understand the differences between files built by the Content Pipeline versus deployed some other way.
In fact, the Content Pipeline is just an optional layer over the top of a simple file deployment mechanism. In order to load a file, exactly three things must take place:
- The file must be copied to your build output folder
- The file must be deployed from build output to the target device
- You must specify the right name and path when you load it
Copy to build output folder
Your Visual Studio Solution Explorer contains source files, not build outputs. These source files are not directly available to your game at runtime. To make them available, we must copy them to the build output folder.
The build output folder is located inside whatever directory contains your project. This will typically be called something like bin\x86\Debug. There is a separate output folder for each target platform and build configuration (x86 vs. Xbox 360, Debug vs. Release, etc.)
There are three main ways to arrange for files to end up in this folder:
- Add files to your content project, so the Content Pipeline will compile them, creating .xnb format outputs which can be loaded using ContentManager.Load
- Add files to your main game project and set their Copy to Output Directory property to Copy if newer, so they will be copied directly to the output, from where they can be loaded using TitleContainer.OpenStream
- Customize your MSBuild project XML to add additional file copying tasks
To make sure all your files have been correctly copied, just open up the build output folder in Windows Explorer and take a look at what is there.
Deploy to the target device
When you debug a game on Windows, it runs directly from the build output folder, so no additional deployment is necessary.
When you run on Xbox or package as a .ccgame, the packaging tool gathers all the files from your build output folder (skipping only a few known-to-be-irrelevant formats such as .pdb), so all the same files are sure to be available even though the game actually runs elsewhere.
But on Windows Phone, or if you distribute a Windows game using ClickOnce, the deployment process only includes files that were declared as outputs by MSBuild. This includes all files created by the Content Pipeline or the Copy to Output Directory property, but will leave out anything you manually copied to the output folder, or if you incorrectly customized your MSBuild XML to copy files without also declaring them as build outputs (which is a topic for another day).
To make sure all your files have been correctly packaged for Windows Phone, rename the output .xap package to a .zip extension, so you can open it in Windows Explorer and see what it contains.
Specify the right name and path
TitleContainer.OpenStream paths are relative to the game executable. If the build output folder is bin\x86\Debug:
- To load bin\x86\Debug\cats.txt, call TitleContainer.OpenStream("cats.txt")
- To load bin\x86\Debug\Content\Levels\cats.txt, call TitleContainer.OpenStream("Content/Levels/cats.txt")
ContentManager.Load internally calls TitleContainer.OpenStream, but first it modifies the path in two ways:
- It automatically adds the current value of ContentManager.RootDirectory in front of the supplied path
- It automatically adds the .xnb file extension
So you should not include the .xnb extension when using ContentManager.Load (and also do not include the extension of whatever source file was used to create this .xnb - remember you are loading the compiled output file, not the source asset that was added to Visual Studio Solution Explorer).
The default game template sets ContentManager.RootDirectory to "Content", so:
- To load bin\x86\Debug\Content\cat.xnb, call Content.Load<Texture2D>("cat")
- To load bin\x86\Debug\Content\Levels\cat.xnb, call Content.Load<Texture2D>("Levels/cat")
You can create multiple ContentManager instances with whatever RootDirectory you like. For instance, to load bin\x86\Debug\Foo\Bar\cat.xnb, you could:
- Set RootDirectory to String.Empty, then call Content.Load<Texture2D>("Foo/Bar/cat")
- Set RootDirectory to "Foo/Bar", then call Content.Load<Texture2D>("cat")
Note: this article is about immutable content which is deployed as part of your game package. If you are looking to save and load data at runtime, you want Isolated Storage or StorageContainer instead.