Pack URI для изображения, встроенного в файл resx - программирование
Подтвердить что ты не робот

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, чтобы я мог использовать настройки культуры пользовательского интерфейса для получения правильного изображения (изображение содержит текст)).

4b9b3361

Ответ 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, но расширения разметки.