2013-05-07 12 views
15

Come si costruisce un URI di pacchetto in un'immagine che si trova in un file di risorse?URI di pacchetto per l'immagine incorporata in un file resx

ho un'assemblea chiamata MyAssembly.Resources.dll, ha una cartella chiamata Immagini, poi c'è un file di risorse chiamato Assets.resx. Questo file di risorse contiene la mia immagine (denominata MyImage.png). La riga di codice che ho è:

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

Tuttavia quando provo a fornire questo URI al costruttore di un nuovo BitmapImage ricevo un IOException con il messaggio

non può individuare risorse 'immagini/attività /myimage.png'.

Nota che ho altre immagini sciolti nella stessa assemblea che posso recuperare bene si utilizza un gruppo URI, quelle immagini hanno la loro azione di costruzione impostata su Resource ma non sono incorporato in un file resx. Dovrei includere il nome del file resx nel percorso?

(Sto cercando di incorporare immagini in file resx in modo da poter sfruttare le impostazioni della cultura dell'interfaccia utente per recuperare l'immagine giusta (l'immagine contiene testo)).

+1

Cosa ti fa pensare che sia possibile utilizzando 'pacchetto:' schema? Questo è legato alle specifiche di Open Packaging Conventions normalizzate. Quindi l'uri punta alle risorse del pacchetto dell'applicazione, non alle risorse incorporate .NET. Potrebbe essere possibile con un altro schema/protocollo (si potrebbe reindirizzare questo protocollo personalizzato alle risorse incorporate, qualcosa come 'new Uri (" resx: // blabla ")'). –

+0

@SimonMourier Stavo pensando che ci deve essere un modo per farlo perché è possibile collegarsi a stringhe in file resx da XAML, quindi sarebbe improbabile che non ci sarebbe un modo per raggiungere altri tipi di risorse incorporati nello stesso file, e un URI è un modo di descrivere un percorso verso una risorsa. Se hai un suggerimento riguardo l'utilizzo di un protocollo personalizzato, sarei felice di sentirlo. Le immagini sciolte (non testuali) sono tutte referenziate usando un URI di pacchetto quindi volevo mantenere la coerenza se possibile. – slugster

risposta

15

Non credo che sia possibile utilizzando lo schema di protocollo "pacco". Questo protocollo è correlato alle specifiche delle convenzioni Open Packaging normalizzate (http://tools.ietf.org/id/draft-shur-pack-uri-scheme-05.txt per i puntatori). Quindi il pacchetto uri punta alle risorse del pacchetto dell'applicazione (o parti nei termini OPC), non alle risorse incorporate .NET.

Tuttavia, è possibile definire il proprio schema, ad esempio "resx" e utilizzarlo in uris componente WPF. I nuovi schemi Uri per tali usi possono essere definiti utilizzando WebRequest.RegisterPrefix.

Ecco un esempio basato su un piccolo progetto di applicazione Wpf denominato "WpfApplication1". Questa applicazione ha un file Resource1.resx definito (ed eventualmente altri file Resource1 corrispondenti localizzati, come Resource1.fr-FR.resx per il francese, ad esempio). Ciascuno di questi file ResX definisce una risorsa Immagine chiamata "img" (notare che questo nome non è lo stesso del nome del file immagine su cui si basa la risorsa).

Ecco la 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> 

Il formato URI è questo:

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

e il nome di montaggio è opzionale, quindi

resx:///resource set name/resource name 

è anche valida e il punto alle risorse dell'assembly principale (il mio esempio lo utilizza)

Questo è il codice che lo supporta, in App.xaml.cs o da qualche altra parte, è necessario registrare il nuovo schema:

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

e l'implementazione schema:

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

impari qualcosa ogni giorno :) – NSGaga

+0

Grazie Simon. Ho avuto diverse opzioni di fallback, ma la tua opzione mi dà più o meno quello che speravo - usando questo posso mantenere la coerenza nel bit di codice che lo richiede, non devo complicare il codice a seconda che si tratti di un immagine sfaccettata o incorporata nella resx. – slugster

4

Esistono due modi per "incorporare" una risorsa in un assieme. Windows Form utilizza l'azione di compilazione Embedded Resource. WPF si aspetta che le risorse contenute negli assiemi vengano contrassegnate con l'azione di creazione Resource.

Quando si utilizza l'editor Resx in Visual Studio per aggiungere un'immagine, questa viene contrassegnata come Risorsa incorporata. Inoltre, lo memorizza come tipo System.Drawing.Bitmap. WPF si aspetta un tipo System.Windows.Media.ImageSource.

Se si dispone di un simulatore (come ILSpy), è possibile osservare l'impatto dell'impostazione di diverse azioni di creazione sui file.

Esempio progetto ImagesLib

Ecco uno screenshot di un progetto con due immagini. È ovvio dai nomi, lo cat_embedded.jpg utilizza l'azione di creazione Embedded Resource e lo cat_resource.jpg utilizza l'azione di creazione Resource Build.

enter image description here

Questo è quello che sembrano in ILSpy.

enter image description here

vedere come il file di cat_resource.jpg è all'interno della sezione ImageLib.g.resources? È qui che WPF cerca risorse. Il percorso del file fa parte del nome della risorsa (images/cat_resource.jpg). Così, quando si utilizza un percorso simile:

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

di specificare il percorso corrispondente dopo la parola ;component.

L'altro file jpg si trova in una posizione diversa nell'assieme e utilizza i punti nel nome (ImageLib.Images.cat_embedded.jpg).

È possibile provare molte permutazioni di quella stringa per cercare di ottenere l'immagine cat_embedded.jpg, ma WPF non la troverà.

RESX Editor

Ecco un altro progetto, che ha due immagini, una contrassegnata come una risorsa e uno aggiunti dal curatore resx.

enter image description here

Ed ecco lo screenshot smontato.

enter image description here

Come si può vedere, l'immagine resx sta usando la stessa posizione URI come l'esempio immagine incorporata in precedenza. Appare nel tuo caso, non sarai in grado di ottenere le immagini dal file resx usando l'URI del pacchetto.

Localizzazione

Da quello che hai detto nella tua domanda, ciò che si sta cercando di realizzare è la localizzazione delle immagini giuste?

Hai guardato questo articolo MSDN?

WPF Globalization and Localization Overview

+0

+1 per la spiegazione dettagliata degli interni. –

3

Come ha giustamente affermato Walt, ciò che si ottiene da un file resx è System.Drawing.Bitmap. Quindi questo deve essere convertito in un System.Windows.Media.ImageSource o sottotipo.

Non sono sicuro se questo cade sotto il tempo perduto per te perché non impiega un URI, ma ecco come ottengo le immagini dai file resx in un'altra libreria. Io uso a simple proxy perché il file di progettazione resx espone solo un costruttore interno (anche se la classe è pubblica), quindi definisce un ValueConverter che fornirà ImageSource.

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 alla conversione ImageSource:

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