5

I have an image in different assembly:

  • (.NET Standard 1.4) ResourcesAssembly/Common/Images/CompanyLogo.png - mandatory requirement
  • Build Action: Content - mandatory requirement
  • Copy to output directory: Copy if newer (I checked after a compilation - the required image is presented in output directory where my exe is located - for example, Debug/Common/Images/CompanyLogo.png. So there should be no problem to get it from there.)

I want to paste it in my app's assembly (WPF) inside a window. I try 2 variants.

1.

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

Image in interface is visible at runtime. But VS's XAML designer doesn't show the image at design time and says it is an error:

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

2.

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

Image is not visible at runtime, but at design time all are OK.

Visual studio 2017 Community Edition 15.4.4.

So the first variant seems suitable for me, but that strange error - why it tries to find the image in Visual Studio folder? "siteoforigin" option relates to the application exe, not to the Visual Studio exe, isn't it?

UPDATE

Tried the second variant with build action as "Embedded resource" (ResourcesAssembly is a .NET Standard 1.4 project). Even cleaned and rebuilt the solution. Result is the same as in the second variant: image is not visible at runtime, but at design time it is visible.

Alex34758
  • 396
  • 4
  • 14
  • Build Action should be *Resource*, not *Embedded Resource*. – Clemens Feb 11 '18 at 22:33
  • There is no "Resource" option in .NET Standard 1.4 project type. Only "Embedded resource" which I consider as "Resource" in usual .NET project types. Am I not right? – Alex34758 Feb 12 '18 at 12:03
  • No, you're not right. *Resource* is something different than *Embedded Resource* (at least in a WPF project). No idea if you can have an assembly resource at all in a .NET Standard project (i.e. one which is resolvable by a Resource File Pack URI). – Clemens Feb 12 '18 at 12:11
  • I have read that "Resource" is for WPF only, so "Embedded resource" should be suitable for both WPF and .NET Standard. Isn't it? – Alex34758 Feb 12 '18 at 12:12
  • No, I don't think so. A Resource File Pack URI won't resolve an Embedded Resource. – Clemens Feb 12 '18 at 12:13
  • So it seems that the only way for me is to use first variant and pay no attention to XAML designer doesn't show the image. – Alex34758 Feb 12 '18 at 12:15

2 Answers2

4

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

Exactly, siteoforigin points to executing assembly directory. But when you use VS's XAML designer, executing assembly is ... XDesProc.exe from the directory you specified (C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE). That's the reason why the image search path is ...\IDE\Common\Images\CompanyLogo.png.

You could ask - "Is there a way to correcly dispaly the image in both WPF Designer and application runtime?" Well, if you want to keep both requirements (1st - image in external resource assembly, 2nd - build action for the image set to 'Content'), then probably no. At least I can't figure out the solution.

Option with siteorigin authority in pack URI is not suitable because the image should be loaded in different applications with different executing directory (see above).

Option with application authority in pack URI is not suitable because it works only for resource files that are compiled into assembly (local or referenced). That's actually the reason why you don't see an image at runtime with your 2nd variant.

So if the 1st variant suits you, then OK, it's the best you could have while not violating requirements you declared. The closest solution that allows to correctly see an image both in Designer and run-time is to set Build Action to Resource for an image and to use the 2nd source path with application authority.

UPDATE

"Resource" build action is not available for .Net Standard class library. I haven't found any info whether it will be ever supported. For this moment, if your ResourcesAssembly must target .Net Standard, your best option is to use first variant you described in the question.

CodeFuller
  • 30,317
  • 3
  • 63
  • 79
  • Setting Build Action to Resource would make the image file an actual "assembly resource" and best fit the question about *loading an image **from** an assembly* or an *image **in** [a] different assembly*. – Clemens Feb 11 '18 at 16:49
  • @CodeFuller Added update in my question - tried to do as you advice - still can't get the visible image in XAML designer and WPF app at runtime. Although for me the first variant is most suitable for now, it is just interesting what is the problem. – Alex34758 Feb 11 '18 at 21:49
  • @Clemens is correct in his comment to your question, choose Resource, not Embedded Resource. – CodeFuller Feb 12 '18 at 04:42
  • @CodeFuller There is no "Resource" option in .NET Standard 1.4 project type. Only "Embedded resource" which I consider as "Resource" in usual .NET project types. Am I not right? – Alex34758 Feb 12 '18 at 12:03
  • I specified my question that resource library is of type .NET Standard 1.4. – Alex34758 Feb 12 '18 at 12:09
  • I'm totally get the answer clearly from your explanation @CodeFuller. I was stomp at "But when you use VS's XAML designer, executing assembly is ... XDesProc.exe" where I frequently terminating this process when my designer properties become not responsive to my click or entry. Now, I understand about siteoforigin – Luiey Jul 18 '18 at 08:17
0

I am quite new to WPF, and quite recently in my spare time while working on a hobby project I came across a very similar problem, with similar constraints.

Nonetheless, as you already figured, application works fine at design time but not at runtime while siteoforigin is the vice versa. As such one logical way to solve this problem is to simply use both, siteoforigin at runtime and application at design time.

To do this, you will simply need to intercept the setting of Source property of your control. One way to do this is to use inheritance.


The following is an example which does this for a ResourceDictionary:

Implementation

/// <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);
    }
}

Usage Example

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

In practice, this solution has worked for me and I make active use of this code. I'm quite certain you can do the same with an Image and/or other WPF components.

That said, in my case, I only needed to support this on a ResourceDictionary. This solution would be unoptimal in your case as you would probably (eventually) require to do this for multiple controls down the road.

As such, I would propose as a potential solution to further investigate the use of Attached Properties, making a an attached property that sets the Source of a control, switching between application at design time and siteoforigin at runtime.

That instead could be re-used across multiple controls without having to inherit each time for each new control/class.


On a related note, I can't say I know why using application works in the designer when the build action for a resource is set to Content. According to the documentation this URI is to be used when a resource "is compiled into a referenced assembly" (Source), but nonetheless this works for me.

This is my first post on StackOverflow and I'm a hobbyist, so please take it easy on me .

Sewer56
  • 1
  • 1
  • 1