2013-05-07 3 views
15

Wie baue ich eine Pack-URI zu einem Bild, das in einer Ressource-Datei ist?Pack URI zu Bild in eine Resx-Datei eingebettet

Ich habe eine Baugruppe MyAssembly.Resources.dll genannt, es einen Ordner Bilder, dann genannt hat in einer Ressourcendatei Assets.resx genannt wird. Diese Ressourcendatei enthält mein Bild (MyImage.png). Die Codezeile ich habe, ist:

uri = new Uri("pack://application:,,,/MyAssembly.Resources,Culture=neutral,PublicKeyToken=null;component/Images/Assets/MyImage.png"); 

aber wenn ich versuche, diesen URI an den Konstruktor eines neuen BitmapImage ich eine IOException mit der Nachricht

kann nicht gefunden Ressource ‚images/Vermögen erhalten liefern /myimage.png '.

Bitte beachte, dass ich andere lose Bilder in derselben Anordnung, die ich fein mit einem Paket-URI abrufen kann, haben diese Bilder ihre Build-Aktion gesetzt auf Resource aber sie sind in einer resx Datei nicht eingebettet. Sollte ich den Namen der Resx-Datei in den Pfad aufnehmen?

(Ich bin auf der Suche nach Bildern in Resx-Dateien einbetten, so dass ich UI Kultureinstellungen nutzen kann, um das richtige Bild (das Bild enthält Text)) zu erhalten.

+1

Was macht Sie denken, es ist möglich, mit 'Pack:' -Regelung? Dies hängt mit den standardisierten Open Packaging Conventions-Spezifikationen zusammen. Die URL verweist also auf die Ressourcen des Anwendungspakets und nicht auf eingebettete .NET-Ressourcen. Es könnte jedoch mit einem anderen Schema/Protokoll möglich sein (Sie könnten dieses benutzerdefinierte Protokoll auf eingebettete Ressourcen umleiten, etwa "new Uri" ("resx: // blabla")). –

+0

@SimonMourier Ich dachte, es muss einen Weg geben, es zu tun, weil Sie mit Strings in ResX-Dateien von XAML verlinken können, so dass es unwahrscheinlich wäre, dass es keine Möglichkeit gibt, andere Ressourcentypen innerhalb der gleichen Datei zu erreichen Ein URI ist eine Möglichkeit, einen Pfad zu einer Ressource zu beschreiben. Wenn Sie einen Vorschlag bezüglich der Verwendung eines benutzerdefinierten Protokolls haben, würde ich mich freuen, es zu hören. Die losen (nicht textlichen) Bilder werden alle mit einem Pack-URI referenziert, so dass ich die Konsistenz möglichst beibehalten wollte. – slugster

Antwort

15

Ich glaube nicht, es ist möglich, das „Pack“ Protokollschema verwenden. Dieses Protokoll bezieht sich auf normalisierte Open Packaging Conventions-Spezifikationen (http://tools.ietf.org/id/draft-shur-pack-uri-scheme-05.txt für Zeiger). Daher verweist das Pack-URI auf die Ressourcen des Anwendungspakets (oder Teile in OPC-Bedingungen), nicht auf eingebettete .NET-Ressourcen.

Sie können jedoch Ihr eigenes Schema definieren, zum Beispiel "resx", und es in der WPF-Komponente uris verwenden. Neue Uri-Schemata für solche Verwendungen können unter Verwendung von WebRequest.RegisterPrefix definiert werden.

Hier ist ein Beispiel basierend auf einem kleinen Wpf-Anwendungsprojekt namens "WpfApplication1". Diese Anwendung hat eine Resource1.resx-Datei definiert (und möglicherweise andere lokalisierte entsprechende Resource1-Dateien wie Resource1.fr-FR.resx für Französisch zum Beispiel). Jede dieser ResX-Dateien definiert eine Bildressource namens "img" (beachten Sie, dass dieser Name nicht mit dem Bilddateinamen identisch ist, auf dem die Ressource basiert). Hier

ist die MainWindow.xaml:

<Window x:Class="WpfApplication1.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="350" Width="525"> 
    <Image Source="resx:///WpfApplication1.Resource1/img" /> 
</Window> 

Das uri-Format ist dies:

resx://assembly name/resource set name/resource name 

und Montage Name ist optional, so

resx:///resource set name/resource name 

gilt auch und Punkt zu Ressourcen in der Hauptbaugruppe (meine Probe verwendet dies)

Dies ist der Code, der es in App.xaml unterstützt.cs oder irgendwo anders, müssen Sie das neue System registrieren:

public partial class App : Application 
{ 
    protected override void OnStartup(StartupEventArgs e) 
    { 
     ResXWebRequestFactory.Register(); 
     base.OnStartup(e); 
    } 
} 

Und das Schema Umsetzung:

public sealed class ResXWebRequestFactory : IWebRequestCreate 
{ 
    public const string Scheme = "resx"; 
    private static ResXWebRequestFactory _factory = new ResXWebRequestFactory(); 

    private ResXWebRequestFactory() 
    { 
    } 

    // call this before anything else 
    public static void Register() 
    { 
     WebRequest.RegisterPrefix(Scheme, _factory); 
    } 

    WebRequest IWebRequestCreate.Create(Uri uri) 
    { 
     return new ResXWebRequest(uri); 
    } 

    private class ResXWebRequest : WebRequest 
    { 
     public ResXWebRequest(Uri uri) 
     { 
      Uri = uri; 
     } 

     public Uri Uri { get; set; } 

     public override WebResponse GetResponse() 
     { 
      return new ResXWebResponse(Uri); 
     } 
    } 

    private class ResXWebResponse : WebResponse 
    { 
     public ResXWebResponse(Uri uri) 
     { 
      Uri = uri; 
     } 

     public Uri Uri { get; set; } 

     public override Stream GetResponseStream() 
     { 
      Assembly asm; 
      if (string.IsNullOrEmpty(Uri.Host)) 
      { 
       asm = Assembly.GetEntryAssembly(); 
      } 
      else 
      { 
       asm = Assembly.Load(Uri.Host); 
      } 

      int filePos = Uri.LocalPath.LastIndexOf('/'); 
      string baseName = Uri.LocalPath.Substring(1, filePos - 1); 
      string name = Uri.LocalPath.Substring(filePos + 1); 

      ResourceManager rm = new ResourceManager(baseName, asm); 
      object obj = rm.GetObject(name); 

      Stream stream = obj as Stream; 
      if (stream != null) 
       return stream; 

      Bitmap bmp = obj as Bitmap; // System.Drawing.Bitmap 
      if (bmp != null) 
      { 
       stream = new MemoryStream(); 
       bmp.Save(stream, bmp.RawFormat); 
       bmp.Dispose(); 
       stream.Position = 0; 
       return stream; 
      } 

      // TODO: add other formats 
      return null; 
     } 
    } 
} 
+0

Sie lernen jeden Tag etwas :) – NSGaga

+0

Danke Simon. Ich hatte einige Fallback-Optionen, aber Ihre Option gibt mir ziemlich genau das, was ich mir erhofft habe. Indem ich das benutze, kann ich Konsistenz in dem Code halten, der es erfordert, ich muss den Code nicht komplizieren, je nachdem, ob es ein lose gepackte Bild oder eingebettet in der Resx. – slugster

4

Es gibt zwei Möglichkeiten, eine Ressource in eine Assembly einzubetten. Windows Forms verwendet die Embedded Resource Build Aktion. WPF erwartet, dass in Baugruppen enthaltene Ressourcen mit der Build-Aktion Resource markiert werden.

Wenn Sie den Resx-Editor in Visual Studio verwenden, um ein Bild hinzuzufügen, markiert es es als eine eingebettete Ressource. Außerdem speichert es es als Typ System.Drawing.Bitmap. WPF erwartet einen System.Windows.Media.ImageSource Typ.

Wenn Sie einen Disseblierer (wie ILSpy) haben, können Sie die Auswirkungen der Einstellung verschiedener Build-Aktionen auf die Dateien betrachten.

Probe ImagesLib Projekt

Hier ist ein Screenshot von einem Projekt mit zwei Bildern. Es ist offensichtlich von den Namen, die cat_embedded.jpg verwendet die Embedded Resource Build-Aktion und die cat_resource.jpg verwendet die Resource Build-Aktion.

enter image description here

Dies ist, was sie wie in ILSpy aussehen.

enter image description here

Sehen Sie, wie die cat_resource.jpg Datei innerhalb des ImageLib.g.resources Abschnitt ist? Hier sucht WPF nach Ressourcen. Der Pfad zu der Datei ist Teil des Ressourcennamens (images/cat_resource.jpg). Also, wenn Sie einen Pfad wie verwenden:

var uri = new Uri("pack://application:,,,/ImageLib;component/Images/cat_resource.jpg"); 

geben Sie den passenden Pfad nach dem Wort ;component. Die andere jpg-Datei befindet sich an einer anderen Position in der Assembly und verwendet Punkte im Namen (ImageLib.Images.cat_embedded.jpg).

Sie können viele Permutationen dieser Zeichenfolge versuchen, um das Bild cat_embedded.jpg zu erhalten, aber WPF wird es nicht finden.

RESX Editor

Hier ist ein weiteres Projekt, das zwei Bilder hat, eine als Ressource markiert und eine durch den resx Editor hinzugefügt.

enter image description here

Und hier ist der zerlegten Screenshot.

enter image description here

Wie Sie sehen können, ist das resx Bild mit der gleichen URI Stelle wie das früheren eingebetteten Bild Beispiel. In Ihrem Fall wird es nicht möglich sein, die Bilder aus der resx-Datei mit dem Pack-URI zu erhalten.

Lokalisierung

Aus dem Gesagten Sie in Ihrer Frage, was Sie versuchen zu erreichen ist die Lokalisierung der Bilder richtig?

Haben Sie sich diesen MSDN-Artikel angesehen?

WPF Globalization and Localization Overview

+0

+1 für detaillierte Erklärung der Interna. –

3

Wie Walt richtig gesagt hat, erhalten Sie aus einer Resx-Datei eine System.Drawing.Bitmap. Dies muss also in einen System.Windows.Media.ImageSource oder Untertyp konvertiert werden.

Ich bin nicht sicher, ob dies unter Zeitverschwendung für Sie fällt, weil es keinen URI verwendet, aber hier ist, wie ich Bilder von Resx-Dateien in einer anderen Bibliothek bekomme. Ich verwende a simple proxy, weil die ResX-Designer-Datei nur einen internen Konstruktor freigibt (selbst wenn die Klasse öffentlich ist), dann definiere einen ValueConverter, der die ImageSource bereitstellt.

enter image description here

<Window x:Class="WpfApplication1.MainWindow" 
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      xmlns:local="clr-namespace:WpfApplication1" 
      xmlns:resx="clr-namespace:MyAssembly.Resources;assembly=MyAssembly.Resources" 
      Title="MainWindow" Height="350" Width="525"> 

    <Window.Resources> 
     <resx:AssetsProxy x:Key="Assets" /> 
     <resx:BitmapToImageSourceConverter x:Key="BitmapConverter" /> 
    </Window.Resources> 

    <Image Source="{Binding myimage, Source={StaticResource Assets}, Converter={StaticResource BitmapConverter}}" /> 
</Window> 

AssetsProxy:

namespace MyAssembly.Resources 
{ 
    public class AssetsProxy : Images.Assets 
    { 
     public AssetsProxy() : base() { } 
    } 
} 

Bitmap Image Umwandlung:

using System; 
using System.Drawing.Imaging; 
using System.Globalization; 
using System.IO; 
using System.Windows.Data; 
using System.Windows.Media.Imaging; 

namespace MyAssembly.Resources 
{ 
    /// <summary> 
    /// Converts a BitmapImage, as provided by a resx resource, into an ImageSource/BitmapImage 
    /// </summary> 
    public class BitmapToImageSourceConverter : IValueConverter 
    { 
     public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
     { 
      BitmapImage bitmapImage = null; 
      if (value is System.Drawing.Image) 
      { 
       bitmapImage = ((System.Drawing.Image)value).ToBitmapImage(); 
      } 
      return bitmapImage; 
     } 

     public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
     { 
      throw new NotImplementedException(); 
     } 
    } 

    public static class BitmapExtensions 
    { 
     /// <summary> 
     /// Converts the System.Drawing.Image to a System.Windows.Media.Imaging.BitmapImage 
     /// </summary> 
     public static BitmapImage ToBitmapImage(this System.Drawing.Image bitmap) 
     { 
      BitmapImage bitmapImage = null; 
      if (bitmap != null) 
      { 
       using (MemoryStream memory = new MemoryStream()) 
       { 
        bitmapImage = new BitmapImage(); 
        bitmap.Save(memory, ImageFormat.Png); 
        memory.Position = 0; 
        bitmapImage.BeginInit(); 
        bitmapImage.StreamSource = memory; 
        bitmapImage.CacheOption = BitmapCacheOption.OnLoad; 
        bitmapImage.EndInit(); 
       } 
      } 
      return bitmapImage; 
     } 
    } 
}