I have a Portable Library which I am using for a Windows Phone application. In that same Portable Library, I have a couple of content files (Build Action = Cont
You need to use Application.GetResourceStream method instead of using GetManifestResource stream
Reference: http://msdn.microsoft.com/en-us/library/ms596994%28v=vs.95%29.aspx
var albumArtPlaceholder =
Application.GetResourceStream(
new Uri("Images/artwork.placeholder.png", UriKind.Relative));
Add your file to portable resource and set build action to Embedded Resource. For example files GB.png
, US.png
under folder CountryFlags
.
Add a getter function with code like this (here it's specific for our countryflag getter image).
public class CountryFlags {
public static Stream GetFlagStream(string countryIsoCode2ch)
{
var flagname = "Full.DLL.Name.CountryFlags.{0}.png";
var rs = Assembly.GetExecutingAssembly().GetManifestResourceStream(
string.Format(flagname, countryIsoCode2ch));
return rs;
}
}
Here Full.DLL.Name
is the part of generated portable library that is before .dll
extension. (Note: Anything.Resources.dll
is a bad name for a library because it gets ignored by Visual Studio at least when generating XAP etc.; instead for example Anything.PortableResource.dll
will work).
From http://social.msdn.microsoft.com/Forums/windowsapps/en-US/386eb3b2-e98e-4bbc-985f-fc143db6ee36/read-local-file-in-portable-library#386eb3b2-e98e-4bbc-985f-fc143db6ee36
File access cannot be done portably between Windows Store apps and Windows Phone 8 apps. You will have to use platform specific code, to open the file and acquire a stream. You can then pass the stream into the PCL.
If you build it with the Content build action, the XML is not inside of the DLL. It's on the filesystem, and there's no way to get it from inside of the PCL. That is why all of the answers set the build action to Embedded Resource. It places the file inside MyPCL.DLL\Path\To\Content.xml
.
However, if you set the build action to Content and set the copy type to Copy if newer, it will place your files in the same directory as the executable.
Therefore, we can just place an interface for reading the file in our PCL. On startup of our nonportable code, we inject an implementation into the PCL.
namespace TestPCLContent
{
public interface IContentProvider
{
string LoadContent(string relativePath);
}
}
namespace TestPCLContent
{
public class TestPCLContent
{
private IContentProvider _ContentProvider;
public IContentProvider ContentProvider
{
get
{
return _ContentProvider;
}
set
{
_ContentProvider = value;
}
}
public string GetContent()
{
return _ContentProvider.LoadContent(@"Content\buildcontent.xml");
}
}
}
Now that the PCL is defined above, we can create our interface implementation in nonportable code (below):
namespace WPFBuildContentTest
{
class ContentProviderImplementation : IContentProvider
{
private static Assembly _CurrentAssembly;
private Assembly CurrentAssembly
{
get
{
if (_CurrentAssembly == null)
{
_CurrentAssembly = System.Reflection.Assembly.GetExecutingAssembly();
}
return _CurrentAssembly;
}
}
public string LoadContent(string relativePath)
{
string localXMLUrl = Path.Combine(Path.GetDirectoryName(CurrentAssembly.GetName().CodeBase), relativePath);
return File.ReadAllText(new Uri(localXMLUrl).LocalPath);
}
}
}
On application startup, we inject the implementation, and demonstrate loading contents.
namespace WPFBuildContentTest
{
//App entrance point. In this case, a WPF Window
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
ContentProviderImplementation cpi = new ContentProviderImplementation();
TestPCLContent.TestPCLContent tpc = new TestPCLContent.TestPCLContent();
tpc.ContentProvider = cpi; //injection
string content = tpc.GetContent(); //loading
}
}
}
EDIT: I kept it strings instead of Streams for simplicity.
Just responding to the bounty request. First off, using Build Action = Content does not actually affect the build at all. It is a project item property that other tooling can read. An installer builder uses it for example to figure out that the file needs to be included in the setup program and deployed to the user's machine.
Using Build Action = Embedded Resource as noted in the upvoted question was the OP's oversight. That actually instructs MSBuild to embed the file as a resource in the assembly manifest, using Assembly.GetManifestResourceStream() retrieves it at runtime.
But it is pretty clear from the bounty comment that you don't want that either. The fallback is to just copy the file onto the target machine. Where it will sit patiently until you need it. Notable about that is this does not in any way alter the size of the package that the user downloads from the Store. It takes the same amount of space, whether it it inside the assembly or a separate file in the package.
So scratch that as a way to get ahead.
It does make a difference at runtime, the entire assembly gets mapped into virtual memory when it gets loaded. So an assembly with a resource will take more virtual memory space. But the word "virtual" is very important, it takes very few resources of the phone. Just a few bytes in the page mapping tables for every 4096 bytes in the resource. You don't start paying for virtual memory until it gets accessed. At which point the phone operating system needs to actually turn it from virtual into physical memory. Or in other words, load the bytes of the resource into RAM. This is not different from loading a file, it also gets loaded into RAM when you open it.
So scratch that as a way to get ahead.
We're running out of good reasons to actually do this, Microsoft certainly did pick the default way to handle resources as a best-practice. It is. But sometimes you have to deploy content as a file, simply because it is too large. One that's pushing 2 gigabytes or more, consuming all virtual memory on a 32-bit operating system so cannot possibly be mapped to VM. The program simply won't be able to start. This is not the kind of program a phone user is going to be very pleased with, really.
You then need to focus on the packing build phase of the solution, the last step when a phone app gets built. The one where all of the projects in the solution have been compiled and the one-and-only file that's uploaded to the Store, and is downloaded by the user, is created.
And yes, there's a problem there, MSBuild is not smart enough to see the PCL library using the resource. The Build action = Content ought to be good enough, like it is for an installer, but that doesn't work. It will only package the DLL, not the resource. It was made to assume you'd embed it, the best practice solution.
What you have to do is to override the package manifest. Described in this MSDN article. Very, very ugly, you're looking at a blank blinking cursor. Which is where I'm running out of good advice, this was made to not do.
if you have added files as resources, check your .Designer.cs, there will be a property for each resource. you can access from that.
here is the sample auto generate property for dat file resources
internal static byte[] MyDatFile {
get {
object obj = ResourceManager.GetObject("MyDatFile", resourceCulture);
return ((byte[])(obj));
}
you can read the dat file as
System.Text.UTF8Encoding enc = new System.Text.UTF8Encoding();
var str = enc.GetString(Resource1.MyDatFile);
var assembly = typeof(PegHelper).GetTypeInfo().Assembly;
using (var stream = assembly.GetManifestResourceStream("Parsers.Peg.SelfDef.xml"))
using (var reader = new StreamReader(stream))
{
string xmlText = reader.ReadToEnd();
return XDocument.Parse(xmlText);
}