/ / Pack URI do obrazu osadzonego w pliku resx - wpf, image, internationalization, resources, uri

Pack URI do obrazu osadzonego w pliku resx - wpf, image, internationalization, resources, uri

Jak utworzyć identyfikator URI paczki dla obrazu znajdującego się w pliku zasobów?

Mam zestaw o nazwie MyAssembly.Resources.dll, ma folder o nazwie Obrazy, następnie znajduje się plik zasobów o nazwie Assets.resx. Ten plik zasobów zawiera mój obraz (nazywany MyImage.png). Linia kodu, którą mam, to:

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

Jednak gdy próbuję dostarczyć ten URI do konstruktora nowego Bitmapa Dostaję IOException z wiadomością

Nie można zlokalizować zasobu "images / assets / myimage.png".

Zauważ, że mam inne luźne obrazy w tym samym zespole, które mogę odzyskać dobrze przy użyciu identyfikatora URI opakowania, te obrazy mają ustawione działanie kompilacji na zasób ale nie są osadzone w pliku resx. Czy powinienem dołączyć nazwę pliku resx do ścieżki?

(Szukam osadzenia obrazów w plikach resx, aby móc wykorzystać ustawienia kultury UI do pobrania odpowiedniego obrazu (obraz zawiera tekst)).

Odpowiedzi:

15 dla odpowiedzi № 1

Nie sądzę, że jest to możliwe za pomocą schematu protokołu "paczka". Ten protokół jest powiązany ze znormalizowanymi specyfikacjami Open Packaging Conventions (http://tools.ietf.org/id/draft-shur-pack-uri-scheme-05.txt dla wskaźników). Tak więc pakiet uri wskazuje zasoby pakietu aplikacji (lub ich części w OPC), a nie zasoby osadzone .NET.

Można jednak zdefiniować własny schemat, na przykład "resx" i użyć go w uris komponentu WPF. Nowe schematy Uri dla takich zastosowań można zdefiniować za pomocą WebRequest.RegisterPrefix.

Oto przykład oparty na małym Wpfprojekt aplikacji o nazwie "WpfApplication1". Ta aplikacja ma zdefiniowany plik Resource1.resx (i prawdopodobnie inne zlokalizowane odpowiednie pliki Resource1, na przykład Resource1.fr-FR.resx, na przykład francuski). Każdy z tych plików ResX definiuje zasób obrazu o nazwie "img" (zauważ, że ta nazwa nie jest taka sama jak nazwa pliku obrazu, na którym opiera się zasób).

Oto plik 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>

Format uri jest następujący:

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

Nazwa zestawu jest opcjonalna, więc

resx:///resource set name/resource name

jest również poprawny i wskazuje zasoby w głównym zespole (moja próbka używa tego)

To jest kod, który go obsługuje, w App.xaml.cs lub gdzieś indziej, musisz zarejestrować nowy schemat:

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

I wdrożenie systemu:

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

4 dla odpowiedzi nr 2

Istnieją dwa sposoby "osadzenia" zasobu w zespole. Windows Forms używa Embedded Resource Zbuduj działanie. WPF oczekuje, że zasoby zawarte w złożeniach będą oznaczone znacznikiem Resource Zbuduj działanie.

Kiedy używasz edytora Resx w Visual Studio do dodania obrazu, oznacza to go jako Zasób osadzony. Ponadto zapisuje go jako typ System.Drawing.Bitmap. WPF oczekuje System.Windows.Media.ImageSource rodzaj.

Jeśli masz dissembler (np. ILSpy), możesz spojrzeć na wpływ ustawienia różnych akcji kompilacji na pliki.

Przykładowy projekt ImagesLib

Oto zrzut ekranu projektu z dwoma obrazami. To oczywiste z nazw, cat_embedded.jpg korzysta z Embedded Resource Zbuduj akcję i cat_resource.jpg korzysta z Resource Zbuduj akcję.

wprowadź opis obrazu tutaj

Tak wyglądają w ILSpy.

wprowadź opis obrazu tutaj

Zobacz, jak plik cat_resource.jpg znajduje się w sekcji ImageLib.g.resources? Właśnie tam WPF szuka zasobów. Ścieżka do pliku jest częścią nazwy zasobu (images/cat_resource.jpg). Więc kiedy używasz ścieżki takiej jak:

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

określasz pasującą ścieżkę po słowie ;component.

Drugi plik jpg znajduje się w innym miejscu w zespole i używa kropek w nazwie (ImageLib.Images.cat_embedded.jpg).

Możesz wypróbować wiele permutacji tego ciągu, aby spróbować uzyskać obraz cat_embedded.jpg, ale WPF nie może go znaleźć.

Edytor RESX

Oto kolejny projekt, który ma dwa obrazy, jeden oznaczony jako zasób i jeden dodany przez edytor resx.

wprowadź opis obrazu tutaj

A tutaj jest zdemontowany zrzut ekranu.

wprowadź opis obrazu tutaj

Jak widać, obraz resx używa tego samegoLokalizacja URI jako wcześniejszy przykład osadzonego obrazu. Pojawi się w twoim przypadku, nie będziesz mógł uzyskać obrazów z pliku resx za pomocą URI Pack.

Lokalizacja

Z tego, co powiedziałeś w swoim pytaniu, co próbujesz osiągnąć, to właściwa lokalizacja obrazów?

Czy obejrzałeś ten artykuł MSDN?

Omówienie globalizacji i lokalizacji w WPF


3 dla odpowiedzi nr 3

Jak słusznie stwierdził Walt, to, co dostajesz z pliku resx, to System.Drawing.Bitmap. Więc to musi zostać przekonwertowane na System.Windows.Media.ImageSource lub podtyp.

Nie jestem pewien, czy to nie jest kwestia marnowania czasu, ponieważ nie wykorzystuje identyfikatora URI, ale oto jak uzyskuję obrazy z plików resx w innej bibliotece. proste proxy ponieważ plik projektanta resx eksponuje tylko wewnętrzny konstruktor (nawet jeśli klasa jest publiczna), a następnie zdefiniuj ValueConverter, który zapewni źródło obrazu.

wprowadź opis obrazu tutaj

<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() { }
}
}

Bitmapa do konwersji 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;
}
}
}

2 dla odpowiedzi № 4

Opisałem komponent do używania obrazów resx w WPF w tym poście na blogu: http://wpfglue.wordpress.com/2012/05/31/localization-revisited/ . Więcej informacji o korzystaniu z zasobów resx w WPF znajduje się pod http://wpfglue.wordpress.com/category/localization/

W tych postach nie używam pakietów uris, ale rozszerzenia znaczników.