2012-06-09 5 views
18

Ich habe eine portable Bibliothek, die ich für eine Windows Phone-Anwendung verwende. In derselben Portable Library habe ich ein paar Inhaltsdateien (Build Action = Inhalt).Wie lese ich eine Ressourcendatei in einer Portable Class Library?

Ich habe eine Klasse DataReader in der Portable Library erstellt, die mir einen Stream in die Inhaltsdatei zurückgeben soll. Mit dem unten stehenden Code komme ich jedoch immer wieder auf null von GetManifestResourceStream zurück. Was mache ich falsch?

Antwort

0

Wenn Sie Dateien als Ressourcen hinzugefügt haben, überprüfen Sie Ihre .Designer.cs, es wird eine Eigenschaft für jede Ressource geben. Sie können darauf zugreifen.

hier ist die automatische Proben Eigenschaft für dat Dateiressourcen erzeugen

internal static byte[] MyDatFile { 
     get { 
      object obj = ResourceManager.GetObject("MyDatFile", resourceCulture); 
      return ((byte[])(obj)); 
     } 

Sie die dat-Datei als

System.Text.UTF8Encoding enc = new System.Text.UTF8Encoding(); 
    var str = enc.GetString(Resource1.MyDatFile); 
+1

ich versuche, eine Datei mit der Build Action auf Inhalt festgelegt zu lesen. – Martin

-1

Sie benötigen GetManifestResource Strom mit Application.GetResourceStream Methode lesen kann zu verwenden, anstatt

Referenz: 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)); 
+2

Auf die statische Klasse der Anwendung kann nicht über die Klassenbibliothek zugegriffen werden. – Martin

2

Fügen Sie Ihre Datei zu tragbarer Ressource hinzu und setzen Sie die Build-Aktion auf Eingebettete Ressource. Zum Beispiel Dateien GB.png, US.png unter Ordner CountryFlags.

Fügen Sie eine Getter-Funktion mit Code wie diesem hinzu (hier ist es spezifisch für unser countryflag Getter-Bild).

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; 
    } 
} 

Hier Full.DLL.Name ist der Teil der erzeugten tragbaren Bibliothek, die vor .dll Erweiterung. (Hinweis:Anything.Resources.dll ist ein schlechter Name für eine Bibliothek, da es von Visual Studio zumindest beim Generieren von XAP usw. ignoriert wird; stattdessen wird beispielsweise Anything.PortableResource.dll funktioniert).

+0

'Assembly.GetExecutingAssembly()' ist nicht in einer PCL verfügbar (zumindest wenn es um die modernen Plattformen geht). –

+0

@AndrewArnott bist du sicher? PCL unter 4.5 ist markiert unterstützt: http://msdn.microsoft.com/en-us/library/system.reflection.assembly.getexecutingassembly%28v=vs.110%29.aspx Dieser Code hat auch auf .NET 4 gearbeitet, 4.5 und Silverlight (über PCL). –

+0

Ich denke, es ist nicht verfügbar, wenn Ziel Win8 + Net45 + WP80 –

2

Nur auf die Kopfgeldanfrage reagieren. Zunächst einmal, die Verwendung von Build Action = Content beeinflusst den Build überhaupt nicht. Es ist eine Projektartikeleigenschaft, die andere Werkzeuge lesen können. Ein Installer-Builder verwendet es beispielsweise, um herauszufinden, dass die Datei in das Setup-Programm aufgenommen und auf dem Computer des Benutzers bereitgestellt werden muss.

Die Verwendung von Build Action = Eingebettete Ressource, wie in der oben genannten Frage festgestellt, war die Aufsicht des OP. Dadurch wird MSBuild angewiesen, die Datei im Assemblymanifest als Ressource einzubetten, wobei Assembly.GetManifestResourceStream() sie zur Laufzeit abruft.

Aber es ist ziemlich klar aus dem Kopfgeld Kommentar, dass Sie das auch nicht wollen. Der Fallback besteht darin, die Datei einfach auf den Zielrechner zu kopieren. Wo es geduldig sitzen wird, bis Sie es brauchen. Bemerkenswert ist, dass dies die nicht in irgendeiner Weise ändert die Größe des Pakets, das der Benutzer aus dem Store herunterlädt. Es benötigt den gleichen Speicherplatz, egal ob es sich um eine Baugruppe oder eine separate Datei im Paket handelt.

Also scratch das als eine Möglichkeit, weiter zu kommen.

Es macht einen Unterschied zur Laufzeit, die gesamte Baugruppe wird beim Laden in den virtuellen Speicher gemappt. Eine Assembly mit einer Ressource benötigt also mehr virtuellen Speicherplatz. Aber das Wort "virtual" ist sehr wichtig, es braucht nur sehr wenige Ressourcen des Telefons. Nur ein paar Bytes in den Seitenzuordnungstabellen für alle 4096 Byte in der Ressource. Sie beginnen nicht, für den virtuellen Speicher zu bezahlen, bis auf ihn zugegriffen wird. An diesem Punkt muss das Betriebssystem des Telefons es tatsächlich vom virtuellen in den physischen Speicher umwandeln. Oder, mit anderen Worten, laden Sie die Bytes der Ressource in den RAM. Dies unterscheidet sich nicht vom Laden einer Datei, sie wird auch beim Öffnen in den RAM geladen.

Also scratch das als eine Möglichkeit, weiter zu kommen.

Wir haben keine guten Gründe mehr, dies zu tun, Microsoft hat die Standardmethode für den Umgang mit Ressourcen als Best-Practice gewählt. Es ist. Aber manchmal haben Sie, um Inhalt als eine Datei bereitzustellen, einfach weil es zu groß ist. Einer, der 2 Gigabyte oder mehr treibt und den gesamten virtuellen Speicher auf einem 32-Bit-Betriebssystem verbraucht, so dass er unmöglich einer VM zugeordnet werden kann. Das Programm kann einfach nicht starten. Dies ist nicht die Art von Programm, mit der ein Telefonbenutzer wirklich sehr zufrieden sein wird.

Sie müssen sich dann auf die Packungsaufbauphase der Lösung konzentrieren, den letzten Schritt beim Erstellen einer Telefonanwendung. Die Datei, in der alle Projekte in der Lösung kompiliert wurden und die einzige Datei, die in den Store hochgeladen wurde und vom Benutzer heruntergeladen wurde, wurde erstellt.

Und ja, es gibt ein Problem dort, MSBuild ist nicht schlau genug, um die PCL-Bibliothek mit der Ressource zu sehen. Die Build-Aktion = Inhalt sollte gut genug sein, wie es für ein Installationsprogramm ist, aber das funktioniert nicht. Es wird nur Paket die DLL, nicht die Ressource. Es wurde davon ausgegangen, dass Sie es eingebettet haben, die Best-Practice-Lösung.

Was Sie tun müssen, ist das Paketmanifest zu überschreiben. Beschrieben in this MSDN article. Sehr, sehr hässlich, Sie suchen einen leeren blinkenden Cursor. Wo mir der gute Rat ausgeht, wurde das nicht getan.

+1

Ich denke, ich bin mir nicht sicher, was ich mit dieser Antwort mache. Es könnte entweder eine brillante Dissertation sein oder gar nichts, was ich brauche. Vielleicht könnte ich das einfacher machen - ich habe eine WP8 App, eine WinRT und eine PCL. Die App hat Tonnen von Video-, Audio-, Bild- und XML-Dateien, die beide RT/WP8-Anwendungen regelmäßig verwenden müssen. Etwa 700 MB. Warum nicht in PCL stecken? Frage ich mich? Aber wird die DLL für die PCL wirklich jetzt 700 MB sein, wenn ich sie einbette?Ich brauche diese DLL die ganze Zeit für andere Dinge. Was ist zu tun? Als Build hinzufügen Aktion = Inhalt hält sie aus der PCL heraus, bis auf sie zugegriffen wird. Hoppla, kein Leerzeichen mehr zum eingeben ... –

+0

Das Problem ist, dass Sie wollen, dass Ihr PCL etwas tut, was nicht von einem PCL erledigt werden soll, der Dateizugriff ist nicht plattformübergreifend, also wenn Sie Ihre Dateien als Inhalt markieren Sie müssen auf sie wie jede andere Datei zugreifen. Eine Umgehung ist, sie einzubetten. Wenn Sie dies jedoch tun, wird Ihre DLL aufgebläht und alle Anwendungen, die Ihre DLL verwenden, werden ausgeblendet. Das ist es, was Hans Ihnen zu sagen versucht. – Rafael

8

Von 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

Dateizugriff kann nicht portabel zwischen Windows Store Apps und Windows Phone 8 Anwendungen durchgeführt werden. Sie müssen plattformspezifischen Code verwenden, um die Datei zu öffnen und einen Stream zu erfassen. Sie können dann den Stream in die PCL weiterleiten.

Wenn Sie es mit der Inhaltserstellungsaktion erstellen, befindet sich das XML nicht in der DLL. Es ist auf dem Dateisystem, und es gibt keine Möglichkeit, es von innerhalb der PCL zu bekommen. Aus diesem Grund setzen alle Antworten die Build-Aktion auf Embedded Resource. Es platziert die Datei innerhalb .

jedoch, wenn Sie setzen die Build-Aktion Inhalt und die Kopie Typ Kopie gesetzt, wenn neuere, wird sie Ihre Dateien im selben Verzeichnis wie die ausführbare Datei platzieren.

Solution Explorer, Properties, and Windows Explorer

Deshalb können wir nur eine Schnittstelle legen Sie die Datei in unserem PCL zum Lesen. Beim Start unseres nicht portablen Codes injizieren wir eine Implementierung in die PCL.

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); 
     } 
    } 
} 

Auf Antrag der Inbetriebnahme injizieren wir die Implementierung und Laden Inhalt zeigen:

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"); 
     } 
    } 
} 

Nun, da die PCL oben definiert ist, können wir unsere Schnittstellen-Implementierung in nonportable Code (siehe unten) erstellen.

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: Ich hielt es anstelle von Streams Strings für Einfachheit.

+0

Das ist interessant. Ich bin mit dieser Art von Injektion vertraut. Aber hier ist das Problem - ich brauche weder die WinRT- noch die WP8-App, um die PCL aufzurufen und eine Ressource zu erhalten. Ich brauche die PCL selbst, um auf die Ressource "Build Action = Content" zuzugreifen, die sich selbst enthält. Sobald es vorhanden ist, wird es etwas damit tun (wie zum Beispiel eine Eigenschaft in einem Ansichtsmodell auffüllen). –

+1

@ToddMain, Ich wurde irgendwie in dem Beispiel weggetragen, aber es war nur ein Beispiel. Die PCL ist eine DLL. Das bedeutet, dass Sie es sowieso vom nicht portablen Code aus aufrufen. Sobald Sie die Schnittstellenimplementierung injiziert haben, kann die PCL die Dateien intern laden und mit ihnen machen, was sie wollen. –

+0

@ ToddMain, hast du es herausgefunden? –

31

Ihr Pfad stimmt nicht. Sie verwenden Schrägstriche, aber in den eingebetteten Manifest-Ressourcennamen wurden Schrägstriche während des Builds in Punkte konvertiert. Abhängig von Ihren PCL-Zielplattformen können Sie möglicherweise nicht einmal Assembly.GetExecutingAssembly() anrufen. Hier

ist das, was Sie tun können:

var assembly = typeof(AnyTypeInYourAssembly).GetTypeInfo().Assembly; 

// Use this help aid to figure out what the actual manifest resource name is. 
string[] resources = assembly.GetManifestResourceNames(); 

// Once you figure out the name, pass it in as the argument here. 
Stream stream = assembly.GetManifestResourceStream("Some.Path.AndFileName.Ext"); 
+0

+1 für 'GetTypeInfo' –

+0

Dies funktioniert definitiv, wünschte immer noch, er Inhalt Art der Dinge würde funktionieren, obwohl –

+0

Was ist AnyTypeInYourAssembly? Niemand erklärt, was diese Option ist. Nicht einmal auf Xamarin-Dokumenten. – Dpedrinha

0
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); 
} 
+0

Erklären Sie Ihren Code. Just put Code ist nicht genug. –

+0

Was ist PegHelper? Niemand erklärt, was diese Option ist. Nicht einmal auf Xamarin-Dokumenten. – Dpedrinha

0

Zunächst einmal abrufen Ihre Assembly wie folgt aus (Dataloader eine Klasse in Ihrer PCL Montage ist):

var assembly = typeof(DataLoader).GetTypeInfo().Assembly; 

hinzufügen Datei zu portabler Ressource und Build-Aktion zu Embedded Resource setzen.

Dann Sie Ihre Ressource wie folgt abrufen:

string resourceNam= "to be filled"; 
var assembly = typeof(DataLoader).GetTypeInfo().Assembly; 
var compressedStream = assembly.GetManifestResourceStream(resourceName)); 

Zum Beispiel, wenn ich eine Datei logo.png in einem Ordner „Assets/Logo“ in einer Baugruppe „TvShowTracker.Helpers“ habe, werde ich diese verwenden Code:

string resourceNam= "TvShowTracker.Helpers.Assets.Logos.logo.png"; 
var assembly = typeof(DataLoader).GetTypeInfo().Assembly; 
var compressedStream = assembly.GetManifestResourceStream(resourceName)); 

Glücklich Codierung :)