C# - 从不同程序集加载图像

C# - Loading image from different assembly

我在不同的程序集中有一个图像:

我想将它粘贴到 window 内的应用程序程序集 (WPF) 中。我尝试了 2 种变体。

1.

<Image Source="pack://siteoforigin:,,,/Common/Images/CompanyLogo.png" />

界面中的图像在运行时可见。但是 VS 的 XAML 设计器在设计时没有显示图像并说这是一个错误:

Could not find a part of the path 'C:\Program Files (x86)\Microsoft Visual Studio17\Community\Common7\IDE\Common\Images\CompanyLogo.png'.

2.

<Image Source="pack://application:,,,/ResourcesAssembly;component/Common/Images/CompanyLogo.png" />

图像在运行时不可见,但在设计时一切正常。

Visual studio 2017 社区版 15.4.4.

所以第一个变体似乎适合我,但是那个奇怪的错误 - 为什么它试图在 Visual Studio 文件夹中找到图像? "siteoforigin" 选项与应用程序 exe 相关,与 Visual Studio exe 无关,不是吗?

更新

尝试使用构建操作作为 "Embedded resource" 的第二个变体(ResourcesAssembly 是一个 .NET Standard 1.4 项目)。甚至清理并重建了解决方案。结果与第二个变体相同:图像在运行时不可见,但在设计时可见。

"siteoforigin" option relates to the application exe, not to the Visual Studio exe, isn't it?

准确的说,siteoforigin指向执行程序集目录。但是当你使用 VS 的 XAML 设计器时,执行程序集是... XDesProc.exe 从你指定的目录 (C:\Program Files (x86)\Microsoft Visual Studio17\Community\Common7\IDE)。这就是图片搜索路径为...\IDE\Common\Images\CompanyLogo.png.

的原因

你可以问 - "Is there a way to correcly dispaly the image in both WPF Designer and application runtime?" 好吧,如果你想保留这两个要求(第一个 - 外部资源程序集中的图像,第二个 - 图像设置为 'Content' 的构建操作),那么可能不。至少我想不出解决方案。

包 URI 中具有 siteorigin 权限的选项不合适,因为图像应该在具有不同执行目录的不同应用程序中加载(见上文)。

包 URI 中具有 application 权限的选项不适用,因为它仅适用于编译到程序集(本地或引用)中的资源文件。这实际上就是为什么您在 运行 时间看不到第二个变体的图像的原因。

因此,如果第一个变体适合您,那么好吧,这是您在不违反您声明的要求的情况下所能拥有的最好的变体。允许在 Designer 和 运行-time 中正确查看图像的最接近的解决方案是将图像的 Build Action 设置为 Resource 并使用第二个源路径 application权限。

更新

"Resource" 构建操作不适用于 .Net Standard class 库。我还没有找到任何信息是否会支持它。目前,如果您的 ResourcesAssembly 必须以 .Net Standard 为目标,您最好的选择是使用您在问题中描述的第一个变体。

我是 WPF 的新手,最近在业余时间从事一个业余项目时,我遇到了一个非常相似的问题,具有相似的限制。

尽管如此,正如您已经想到的那样,application 在设计时工作正常但在运行时却不行,而 siteoforigin 反之亦然。因此,解决此问题的一种合乎逻辑的方法是在运行时同时使用 siteoforigin 和在设计时使用 application

为此,您只需截取控件的 Source 属性 设置。一种方法是使用 inheritance.


以下是为 ResourceDictionary 执行此操作的示例:

实施

/// <summary>
/// A modified type of <see cref="ResourceDictionary"/> that translates "Resource" pack URIs to "Site of Origin"
/// pack URIs when in runtime.
///
/// i.e. This allows you to declare pack URIs as "pack://application:,,,", which will be resolved
/// as such in design mode while at runtime, actually using "pack://siteoforigin:,,,".
/// </summary>
public class SiteOfOriginResourceDictionary : ResourceDictionary
{
    private const string SiteOfOriginPrefix = "pack://siteoforigin:,,,";
    private const string ApplicationPrefix = "pack://application:,,,";

    private const string UseRedirectSource = "Please use RedirectSource instead of Source";
    private string _originalUri;

    /// <summary>
    /// Gets or sets the design time source.
    /// </summary>
    public string RedirectSource
    {
        get => _originalUri;

        set
        {
            this._originalUri = value;
            bool isInDesignMode = (bool) DesignerProperties.IsInDesignModeProperty.GetMetadata(typeof(DependencyObject)).DefaultValue;

            if (! isInDesignMode)
            {
                if (value.Contains(ApplicationPrefix))
                    value = value.Replace(ApplicationPrefix, SiteOfOriginPrefix);
            }

            base.Source = new Uri(value);
        }
    }

    /// <summary>
    /// Please use <see cref="RedirectSource"/> instead.
    /// </summary>
    public new Uri Source
    {
        get => throw new Exception(UseRedirectSource);
        set => throw new Exception(UseRedirectSource);
    }
}

用法示例

<ResourceDictionary.MergedDictionaries>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <SiteOfOriginResourceDictionary RedirectSource="pack://application:,,,/Theme/Default/Root.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</ResourceDictionary.MergedDictionaries>

在实践中,这个解决方案对我很有效,我积极使用这个代码。我很确定您可以对 Image and/or 其他 WPF 组件执行相同的操作。

也就是说,就我而言,我只需要在 ResourceDictionary 上支持它。在您的情况下,此解决方案可能不是最佳解决方案,因为您可能(最终)需要为以后的多个控件执行此操作。

因此,我建议作为一个潜在的解决方案来进一步研究 Attached Properties 的使用,制作一个附加的 属性 来设置控件的 Source,在两者之间切换application 在设计时,siteoforigin 在运行时。

可以 re-used 跨越多个控件,而不必每次都继承每个新的 control/class。


在相关说明中,我不能说我知道为什么当资源的构建操作设置为 Content 时,在设计器中使用 application 有效。根据文档,当资源 "is compiled into a referenced assembly" (Source) 时将使用此 URI,但这对我来说仍然有效。

这是我第一次 post 在 Whosebug 上,我是一个业余爱好者,所以请放轻松。