Pack URI для изображения, встроенного в файл resx
Как создать URI пакета для изображения, которое находится в файле ресурсов?
У меня есть сборка под названием MyAssembly.Resources.dll
, у нее есть папка "Изображения", а затем есть файл ресурсов, называемый Assets.resx. Этот файл ресурсов содержит мое изображение (MyImage.png). Строка кода у меня:
uri = new Uri("pack://application:,,,/MyAssembly.Resources,Culture=neutral,PublicKeyToken=null;component/Images/Assets/MyImage.png");
Однако, когда я пытаюсь предоставить этот URI конструктору нового BitmapImage, я получаю IOException с сообщением
Невозможно найти ресурс 'images/assets/myimage.png'.
Обратите внимание, что у меня есть другие свободные изображения в той же сборке, которые я могу получить с использованием URI пакета, эти изображения имеют действие сборки, установленное в Resource , но они не встроены в файл resx. Должен ли я включать имя файла resx в путь?
(Я хочу вставлять изображения в файлы resx, чтобы я мог использовать настройки культуры пользовательского интерфейса для получения правильного изображения (изображение содержит текст)).
Ответы
Ответ 1
Я не думаю, что это возможно с использованием схемы протокола "pack". Этот протокол относится к нормализованным спецификациям условных обозначений Open Packaging (http://tools.ietf.org/id/draft-shur-pack-uri-scheme-05.txt для указателей). Таким образом, пакет uri указывает на ресурсы пакета приложений (или части в терминах OPC), а не на встроенные ресурсы .NET.
Однако вы можете определить свою собственную схему, например, "resx" и использовать ее в uris-компоненте WPF. Новые схемы Uri для такого использования могут быть определены с помощью WebRequest.RegisterPrefix.
Вот пример, основанный на небольшом проекте приложения Wpf под названием "WpfApplication1". Это приложение имеет файл Resource1.resx(и, возможно, другие локализованные соответствующие файлы Resource1, например Resource1.fr-FR.resx для французского языка). Каждый из этих файлов ResX определяет ресурс изображения с именем "img" (обратите внимание, что это имя не совпадает с именем файла изображения, на котором основан ресурс).
Вот 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>
Формат uri таков:
resx://assembly name/resource set name/resource name
и имя сборки необязательно, поэтому
resx:///resource set name/resource name
также действителен и указывает на ресурсы в основной сборке (мой пример использует это)
Это код, который его поддерживает, в App.xaml.cs или где-то еще, вам нужно зарегистрировать новую схему:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
ResXWebRequestFactory.Register();
base.OnStartup(e);
}
}
И реализация схемы:
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;
}
}
}
Ответ 2
Существует два способа "встраивания" ресурса в сборку. Windows Forms использует Embedded Resource
Действие сборки.
WPF ожидает, что ресурсы, содержащиеся в сборках, будут отмечены с помощью Resource
Действия по созданию.
Когда вы используете редактор Resx в Visual Studio для добавления изображения, он отмечает его как встроенный ресурс. Кроме того, он сохраняет его как тип System.Drawing.Bitmap
. WPF ожидает тип System.Windows.Media.ImageSource
.
Если у вас есть демонстрант (например, ILSpy), вы можете посмотреть влияние установки различных действий сборки на файлы.
Пример проекта ImageLib
Вот скриншот проекта с двумя изображениями. Это очевидно из имен, cat_embedded.jpg
использует действие Embedded Resource
Build, а cat_resource.jpg
использует действие Resource
Build.
![enter image description here]()
Вот как они выглядят в ILSpy.
![enter image description here]()
Посмотрите, как файл cat_resource.jpg находится в разделе ImageLib.g.resources? Именно там WPF ищет ресурсы. Путь к файлу является частью имени ресурса (images/cat_resource.jpg
). Поэтому, когда вы используете путь, например:
var uri = new Uri("pack://application:,,,/ImageLib;component/Images/cat_resource.jpg");
вы указываете путь соответствия после слова ;component
.
Другой файл jpg находится в другом месте сборки и использует периоды в имени (ImageLib.Images.cat_embedded.jpg
).
Вы можете попробовать много перестановок этой строки, чтобы попытаться получить изображение cat_embedded.jpg, но WPF его не найдет.
Редактор RESX
Здесь другой проект, который имеет два изображения, один, помеченный как ресурс, и один, добавленный редактором resx.
![enter image description here]()
И вот разобранный снимок экрана.
![enter image description here]()
Как вы можете видеть, образ resx использует то же местоположение URI, что и предыдущий пример встроенного изображения.
Он появляется в вашем случае, вы не сможете получить изображения из файла resx с помощью URI пакета.
Локализация
Из того, что вы сказали в своем вопросе, то, что вы пытаетесь выполнить, - это локализация изображений?
Вы просмотрели эту статью MSDN?
Обзор глобализации и локализации WPF
Ответ 3
Как правильно сказал Уолт, вы получаете из файла resx System.Drawing.Bitmap
. Поэтому это нужно преобразовать в System.Windows.Media.ImageSource
или подтип.
Я не уверен, что это попадает под ваше время, потому что в нем не используется URI, но вот как я получаю изображения из файлов resx в другой библиотеке. Я использую простой прокси, потому что файл конструктора resx предоставляет только внутренний конструктор (даже если класс является общедоступным), а затем определите ValueConverter, который предоставит 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 в 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;
}
}
}
Ответ 4
Я описал компонент для использования изображений resx в WPF в этом сообщении в блоге: http://wpfglue.wordpress.com/2012/05/31/localization-revisited/. Вы найдете больше сообщений об использовании ресурсов resx в WPF под http://wpfglue.wordpress.com/category/localization/
В этих сообщениях я не использую пакет uris, но расширения разметки.