存储WPF图像资源

456

对于一个需要 10-20 个小图标和图片进行说明的 WPF 应用程序,将它们存储在程序集中作为嵌入式资源是正确的选择吗?

如果是这样,请问我该如何在 XAML 中指定 Image 控件应该从嵌入式资源中加载图片?

11个回答

516

如果您将在多个位置使用图像,则值得将图像数据仅加载一次到内存中,然后在所有Image元素之间共享它。

要实现这一点,请在某个地方创建一个BitmapSource资源:

<BitmapImage x:Key="MyImageSource" UriSource="../Media/Image.png" />

然后,在您的代码中,使用类似以下的内容:

<Image Source="{StaticResource MyImageSource}" />
在我的情况下,我发现我必须将Image.png文件的构建操作设置为Resource,而不仅仅是Content。这将导致图像被包含在编译后的程序集中。

8
能否动态地实现这个功能?如果我有不同数量的图片需要在启动时加载,我能否为每张图片创建一个BitmapSource,并以与上述相同的方式引用它们? - Becky Green
2
@Becky - 是的,你可以这样做,但是如果你想在Xaml中引用它们,那么你可能需要使用DynamicResource标记扩展而不是StaticResource,假设你在编译时知道键。在WPF中,你可以在运行时创建资源字典。实际上,当你加载一个Xaml文档时,就会发生这种情况,只是你看不到等效的C#代码。 - Drew Noakes
10
如果你将图片资源添加到资源字典中,请不要忘记在组件的XAML中引用该图片字典,类似于下面的代码:
<UserControl.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Dictionary1.xaml" /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </UserControl.Resources>
- Dan Mitchell
5
我通常会在Image中添加 Width="{Binding Source.PixelWidth, RelativeSource={RelativeSource Self}}",否则我经常看到图像因某种原因变得非常扭曲(例如将16x16的图标拉伸到看起来像200x200像素)。 - O. R. Mapper
6
发现如果BitmapImage在引用程序集的ResourceDictionary中声明,那么UriSource需要是packURI才能正常工作。否则,在VS的xaml编辑器中可以看到图像,但在调试时却看不到图像。Pack URIs: https://msdn.microsoft.com/zh-cn/library/aa970069(v=vs.100).aspx - failedprogramming
显示剩余2条评论

206

我发现使用图片、视频等的最佳实践是:

  • 将您的文件“构建操作”更改为“内容”。请务必勾选“复制到生成目录”。
    • 在解决方案资源管理器窗口上右键单击菜单中找到。
  • 图像源格式如下:
    • "/«YourAssemblyName»;component/«YourPath»/«YourImage.png»"

示例

<Image Source="/WPFApplication;component/Images/Start.png" />

好处:

  • 文件未嵌入到程序集中。
    • 资源管理器在资源过多时(在编译时)会引发一些内存溢出问题。
  • 可以在不同程序集之间调用。

40
如果您将资源嵌入程序集中,同样的方法也适用,但您需要将“Build Action”设置为“Resource”。 - Ashley Davis
5
感谢您的工作。提醒其他人注意一点:要求按原样使用“component”,“Images”是项目中png文件的相对路径。例如,放置在根目录下的图像将为“<Image Source="/WPFApplication;component/Start.png" />”。 - Badiboy
4
在C#中如何实现这一点的例子会很不错。(这不是一个有效的URI,因此在构造BitmapImage时无法使用它。) - Vaccano
6
如果文件被设置为嵌入式资源,你该怎么做?这个方法似乎行不通。而且我不想在我的项目中重复包含这张图片。(因为我已经将它作为嵌入式资源使用了。) - BrainSlugs83
2
“组件”是如何进入路径的?这是某些规范的一部分吗? - Triynko
显示剩余5条评论

49
在代码中加载执行程序集中的资源,其中我的图片 Freq.png 存储在 Icons 文件夹中,并被定义为 Resource:
this.Icon = new BitmapImage(new Uri(@"pack://application:,,,/" 
    + Assembly.GetExecutingAssembly().GetName().Name 
    + ";component/" 
    + "Icons/Freq.png", UriKind.Absolute)); 

我还编写了一个函数:
/// <summary>
/// Load a resource WPF-BitmapImage (png, bmp, ...) from embedded resource defined as 'Resource' not as 'Embedded resource'.
/// </summary>
/// <param name="pathInApplication">Path without starting slash</param>
/// <param name="assembly">Usually 'Assembly.GetExecutingAssembly()'. If not mentionned, I will use the calling assembly</param>
/// <returns></returns>
public static BitmapImage LoadBitmapFromResource(string pathInApplication, Assembly assembly = null)
{
    if (assembly == null)
    {
        assembly = Assembly.GetCallingAssembly();
    }

    if (pathInApplication[0] == '/')
    {
        pathInApplication = pathInApplication.Substring(1);
    }
    return new BitmapImage(new Uri(@"pack://application:,,,/" + assembly.GetName().Name + ";component/" + pathInApplication, UriKind.Absolute)); 
}

用法(假设将函数放在ResourceHelper类中):

this.Icon = ResourceHelper.LoadBitmapFromResource("Icons/Freq.png");

注意:请参阅WPF中的MSDN包URI:
pack://application:,,,/ReferencedAssembly;component/Subfolder/ResourceFile.xaml


新的Uri抛出了Invalid URI: Invalid port specified异常。 - Steve
你有错误的URI吗? - Eric Ouellet
1
返回翻译后的文本:我的程序与你的类似,只不过我的是在一个WinForm托管的WPF中运行。当我调用new Uri时,“pack”模式尚未注册。 - Steve
1
哎呀...这可能与Winform托管的WPF有关。很抱歉,我不会尝试修复它,因为我认为这不是一个非常常见的用法。祝你好运! - Eric Ouellet
在我的情况下,使用 new Uri(@"pack://application:,,,/" + pathInApplication) 也解决了问题。 - Adam Calvet Bohl
显示剩余3条评论

49

有些人在询问如何在代码中执行此操作,但并未得到答案。

经过多小时的搜索,我发现了一种非常简单的方法,我没有找到任何例子,因此在这里分享我的方法。该方法适用于图像(我的是.gif格式)。

概述:

它返回一个BitmapFrame,ImageSource“目标”似乎很喜欢。

使用:

doGetImageSourceFromResource ("[YourAssemblyNameHere]", "[YourResourceNameHere]");

方法:

static internal ImageSource doGetImageSourceFromResource(string psAssemblyName, string psResourceName)
{
    Uri oUri = new Uri("pack://application:,,,/" +psAssemblyName +";component/" +psResourceName, UriKind.RelativeOrAbsolute);
    return BitmapFrame.Create(oUri);
}

经验教训:

根据我的经验,打包字符串不是问题所在,检查流是否正确,特别是如果首次读取设置了指针到文件结尾,需要重新将其设置为零后再次读取。

希望这可以帮助你节省我曾经浪费的许多时间!


48

是的,这是正确的方法。

你可以使用资源文件中的图片,只需要使用路径即可:

<Image Source="..\Media\Image.png" />

您必须将图像文件的构建操作设置为“资源”。


1
谢谢。有没有一种方法可以使用ImageSource做类似的事情,将图像加载到资源字典中一次。我担心这种方法会在内存中多次加载图像数据。 - Drew Noakes
2
当你需要重构代码时,这将会是一场灾难。如果你的XAML文档发生命名空间变化,你将不得不手动更改所有图像引用。Drew Noakes所描述的方法更加流畅和可维护。 - Kasper Holdum

14

该链接似乎是有效的(尽管它显示“此文档已存档,不再维护。”)。 - Peter Mortensen

8
  1. Visual Studio 2010 专业版 SP1。
  2. .NET Framework 4 客户端配置文件。
  3. PNG 图像添加为项目属性的资源。
  4. 自动创建了 Resources 文件夹下的新文件。
  5. 构建操作设置为资源。

这对我很有效:

<BitmapImage x:Key="MyImageSource" UriSource="Resources/Image.png" />

4

如果你正在使用Blend,为了更加方便,不会遇到获取“源”属性正确路径的麻烦,只需从项目面板拖放图像到设计器中即可。


2

是的,这是正确的方式。您可以使用路径在资源文件中使用图像:

<StackPanel Orientation="Horizontal">
    <CheckBox  Content="{Binding Nname}" IsChecked="{Binding IsChecked}"/>
    <Image Source="E:\SWorking\SharePointSecurityApps\SharePointSecurityApps\SharePointSecurityApps.WPF\Images\sitepermission.png"/>
    <TextBlock Text="{Binding Path=Title}"></TextBlock>
</StackPanel>

1

在Drew Noakes的回答基础上,以下是我遵循的完整步骤,用于创建资源字典、向其中添加BitmapImage资源以及在用户控件中引用BitmapImage资源。

  1. 在项目根目录下添加一个名为Images的文件夹。
  2. Images文件夹下添加MyImage.png文件。
  3. MyImage.png的属性窗口中,将Build Action设置为Resource
  4. 在项目根目录下创建一个名为MainResourceDictionary.xaml的资源字典:
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <BitmapImage x:Key="MyImageSource" UriSource="Images/MyImage.png" />
</ResourceDictionary>
  1. 在控件中添加对资源字典的引用:
<UserControl ...>
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="MainResourceDictionary.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </UserControl.Resources>
    ...
  1. 在控件中引用图片资源:
<UserControl ...>
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="MainResourceDictionary.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </UserControl.Resources>
    ...
    <Image Source="{DynamicResource ResourceKey=ServiceLevel1Source}" />
    ...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接