wpf 将 URI 打包到嵌入在 resx 文件中的图像
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16409819/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Pack URI to image embedded in a resx file
提问by slugster
How do I construct a pack URI to an image that is in a resource file?
如何为资源文件中的图像构建包 URI?
I have an assembly called MyAssembly.Resources.dll, it has a folder called Images, then in there is a resource file called Assets.resx. This resource file contains my image (called MyImage.png). The line of code I have is:
我有一个名为 的程序集MyAssembly.Resources.dll,它有一个名为Images的文件夹,然后有一个名为Assets.resx的资源文件。此资源文件包含我的图像(称为MyImage.png)。我的代码行是:
uri = new Uri("pack://application:,,,/MyAssembly.Resources,Culture=neutral,PublicKeyToken=null;component/Images/Assets/MyImage.png");
However when I try to supply this URI to the constructor of a new BitmapImageI get an IOExceptionwith the message
但是,当我尝试将此 URI 提供给新BitmapImage的构造函数时,我收到一个带有消息的IOException
Cannot locate resource 'images/assets/myimage.png'.
无法找到资源“images/assets/myimage.png”。
Note that I have other loose images in the same assembly which I can retrieve fine using a pack URI, those images have their build action set to Resource but they are not embedded in a resx file. Should I be including the name of the resx file in the path?
请注意,我在同一个程序集中还有其他松散的图像,我可以使用包 URI 很好地检索它们,这些图像的构建操作设置为 Resource但它们没有嵌入到 resx 文件中。我应该在路径中包含 resx 文件的名称吗?
(I am looking to embed images in resx files so that I can leverage UI culture settings to retrieve the right image (the image contains text)).
(我希望在 resx 文件中嵌入图像,以便我可以利用 UI 文化设置来检索正确的图像(图像包含文本))。
采纳答案by Simon Mourier
I don't think it's possible using the "pack" protocol scheme. This protocol is related to normalized Open Packaging Conventions specs (http://tools.ietf.org/id/draft-shur-pack-uri-scheme-05.txtfor pointers). So the pack uri points to the application package's resources (or parts in OPC terms), not to .NET embedded resources.
我认为使用“pack”协议方案是不可能的。该协议与规范化的开放打包约定规范有关(http://tools.ietf.org/id/draft-shur-pack-uri-scheme-05.txt的指针)。所以包 uri 指向应用程序包的资源(或 OPC 术语中的部分),而不是 .NET 嵌入资源。
However, you can define your own scheme, for example "resx" and use it in WPF component uris. New Uri schemes for such usages can be defined using WebRequest.RegisterPrefix.
但是,您可以定义自己的方案,例如“resx”并在 WPF 组件 uris 中使用它。可以使用WebRequest.RegisterPrefix定义用于此类用途的新 Uri 方案。
Here is an example based on a small Wpf application project named "WpfApplication1". This application has a Resource1.resx file defined (and possibly other localized corresponding Resource1 files, like Resource1.fr-FR.resx for french for example). Each of these ResX files define an Image resource named "img" (note this name is not the same as the image file name the resource is based on).
这是一个基于名为“WpfApplication1”的小型 Wpf 应用程序项目的示例。该应用程序定义了一个 Resource1.resx 文件(可能还有其他本地化的相应 Resource1 文件,例如法语的 Resource1.fr-FR.resx)。这些 ResX 文件中的每一个都定义了一个名为“img”的图像资源(请注意,该名称与资源所基于的图像文件名不同)。
Here is the MainWindow.xaml:
这是 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>
The uri format is this:
uri格式是这样的:
resx://assembly name/resource set name/resource name
and assembly name is optional, so
和程序集名称是可选的,所以
resx:///resource set name/resource name
is also valid and point to resources in the main assembly (my sample uses this)
也是有效的并指向主程序集中的资源(我的示例使用了这个)
This is the code that supports it, in App.xaml.cs or somewhere else, you need to register the new scheme:
这是支持它的代码,在App.xaml.cs或其他地方,需要注册新的scheme:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
ResXWebRequestFactory.Register();
base.OnStartup(e);
}
}
And the scheme implementation:
以及方案实现:
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;
}
}
}
回答by Walt Ritscher
There are two ways to "embed" a resource in an assembly. Windows Forms uses the Embedded ResourceBuild Action.
WPF expects resources contained in assemblies to be marked with the ResourceBuild Action.
有两种方法可以在程序集中“嵌入”资源。Windows 窗体使用Embedded Resource构建操作。WPF 期望程序集中包含的资源使用Resource构建操作进行标记。
When you use the Resx editor in Visual Studio to add an image, it marks it as an Embedded Resource. Also, it stores it as type System.Drawing.Bitmap. WPF expect a System.Windows.Media.ImageSourcetype.
在 Visual Studio 中使用 Resx 编辑器添加图像时,它会将其标记为嵌入式资源。此外,它将其存储为 type System.Drawing.Bitmap。WPF 需要一个System.Windows.Media.ImageSource类型。
If you have a dissembler (like ILSpy) you can look at impact of setting different build actions on the files.
如果您有反汇编程序(如 ILSpy),您可以查看对文件设置不同构建操作的影响。
Sample ImagesLib project
示例 ImagesLib 项目
Here is a screenshot of a project with two images. It's obvious from the names, the cat_embedded.jpgis using the Embedded ResourceBuild action and the cat_resource.jpgis using the ResourceBuild action.
这是一个包含两个图像的项目的屏幕截图。从名称中可以明显看出,cat_embedded.jpg正在使用Embedded Resource构建操作和cat_resource.jpg正在使用Resource构建操作。


This is what they look like in ILSpy.
这就是它们在 ILSpy 中的样子。


See how the cat_resource.jpg file is within the ImageLib.g.resources section? That is where WPF looks for resources. The path to the file is part of the resource name (images/cat_resource.jpg). So when you use a path like:
看看 cat_resource.jpg 文件是如何在 ImageLib.g.resources 部分中的?这就是 WPF 寻找资源的地方。文件路径是资源名称 ( images/cat_resource.jpg) 的一部分。因此,当您使用以下路径时:
var uri = new Uri("pack://application:,,,/ImageLib;component/Images/cat_resource.jpg");
you specify the matching path after the word ;component.
您在单词后指定匹配路径;component。
The other jpg file is located in a different location in the assembly, and uses periods in the name (ImageLib.Images.cat_embedded.jpg).
另一个 jpg 文件位于程序集中的不同位置,并在名称 ( ImageLib.Images.cat_embedded.jpg) 中使用句点。
You can try many permutations of that string to try and get the cat_embedded.jpg image, but WPF won't find it.
您可以尝试该字符串的许多排列来尝试获取 cat_embedded.jpg 图像,但 WPF 找不到它。
RESX Editor
RESX 编辑器
Here's another project, that has two images, one marked as a resource and one added by the resx editor.
这是另一个项目,它有两个图像,一个标记为资源,一个由 resx 编辑器添加。


And here is the disassembled screenshot.
这是分解后的屏幕截图。


As you can see, the resx image is using the same URI location as the earlier embedded image example. It appears in your case, you are not going to be able to get the images from the resx file using the Pack URI.
如您所见,resx 图像使用与早期嵌入图像示例相同的 URI 位置。在您的情况下,您将无法使用 Pack URI 从 resx 文件中获取图像。
Localization
本土化
From what you said in your question, what you are trying to accomplish is localization of the images right?
根据您在问题中所说的,您想要完成的是图像的本地化,对吗?
Have you looked at this MSDN article?
你看过这篇 MSDN 文章吗?
回答by Mike Fuchs
As Walt has correctly stated, what you get out of a resx file is a System.Drawing.Bitmap. So this needs to be converted to a System.Windows.Media.ImageSourceor subtype.
正如 Walt 正确指出的那样,您从 resx 文件中得到的是System.Drawing.Bitmap. 所以这需要转换为aSystem.Windows.Media.ImageSource或子类型。
I'm not sure if this falls under time wasters for you because it does not employ an URI, but here is how I get images from resx files in another library. I use a simple proxybecause the resx designer file only exposes an internal constructor (even if the class is public), then define a ValueConverter that will provide the ImageSource.
我不确定这是否会浪费您的时间,因为它不使用 URI,但这是我从另一个库中的 resx 文件获取图像的方法。我使用一个简单的代理,因为 resx 设计器文件只公开一个内部构造函数(即使类是公共的),然后定义一个将提供 ImageSource 的 ValueConverter。


<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 to ImageSource conversion:
位图到 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;
}
}
}
回答by hbarck
I described a component for using resx images in WPF in this blog post: http://wpfglue.wordpress.com/2012/05/31/localization-revisited/. You will find more posts about using resx resources in WPF under http://wpfglue.wordpress.com/category/localization/
我在这篇博文中描述了在 WPF 中使用 resx 图像的组件:http: //wpfglue.wordpress.com/2012/05/31/localization-revisited/。您将在http://wpfglue.wordpress.com/category/localization/下找到更多关于在 WPF 中使用 resx 资源的帖子
In these posts, I don't use pack uris, but markup extensions.
在这些帖子中,我不使用包 uri,而是使用标记扩展。

