How to force WPF to use resource URIs that use assembly strong name? Argh!

风流意气都作罢 提交于 2019-11-27 07:52:10

I have experienced this same problem and this might be a possible solution

each time a control is created using a .xaml page, on the attached .cs file constructor, before the InitializeComponent() call, add the following lines:

contentLoaded = true;
var assemblyName = GetType().Assembly.GetName();
System.Windows.Application.LoadComponent(GetType(), new Uri(
                string.Format("/{0};v{1};component{2}/{3}.xaml",
                assemblyName.Name,
                assemblyName.Version,
                [[[namespace]]],
                type.Name
                ), UriKind.Relative))

where as [[[namespace]]] enter the full namespace of the class, except the visual studio project default namespace

(Note: there is an open a ticked on connect https://connect.microsoft.com/VisualStudio/feedback/details/668914/xaml-generated-code-uses-resource-uri-without-assembly-strong-name)

You can set the following in your project file to change the URI's in the generated code:

<PropertyGroup>
  <AssemblyVersion>1.0.0.0</AssemblyVersion>
  <AssemblyPublicKeyToken>[YOUR_PUBLIC_KEY_TOKEN]</AssemblyPublicKeyToken>
</PropertyGroup>
Kent Boogaart

I tend to agree that this is probably a bug, or at least a deficiency in the XAML tooling. Perhaps you should report it on Connect.

I haven't tried, but here are a couple of potential workarounds:

  1. Inject a pre-build step to automatically modify the .g.cs files to use pack URIs that specify the full assembly information (AssemblyShortName[;Version][;PublicKey];component/Path)
  2. Attach to AppDomain.AssemblyResolve to help the CLR find the right assembly

I have been grappling with this in VS2012. I couldn't get Riccardo's solution to work in this environment. This variant of his code ...

_contentLoaded = true;
var assemblyName = GetType().Assembly.GetName();
Application.LoadComponent(this, new Uri(String.Format("/{0};v{1};component/CustomersFrame.xaml", assemblyName.Name, assemblyName.Version), UriKind.Relative));

... did resolve the 'cannot locate resource' issue but then I hit the following error a bit further along in a child element: 'Could not register named object. Cannot register duplicate name 'search' in this scope.'

Aaron Marten's solution does work for me. Sorry I can't comment or upvote but I haven't got the rep.

You can also pass the /p:AssemblyVersion=$version parameter to the msbuild process if your builds are automated.

This code, based on Riccardo's answer, worked for me in VS2010.

First I defined a loader method that I can call from my XAML constructor.

namespace Utility
{
    public class Utility
    {
        public static void LoadXaml(Object obj)
        {
            var type = obj.GetType();
            var assemblyName = type.Assembly.GetName();
            var uristring = string.Format("/{0};v{1};component/{2}.xaml",
                assemblyName.Name,
                assemblyName.Version,
                type.Name);
            var uri = new Uri(uristring, UriKind.Relative);
            System.Windows.Application.LoadComponent(obj, uri);
        }
    }
}

Then in the constructor for each XAML control, I replaced InitializeComponent() with:

        _contentLoaded = true;
        Utility.Utility.LoadXaml(this);
        InitializeComponent();

I did notice that some of my RelativeSource bindings stopped working, but I was able to work around this.

We also had the same problem, but we had to set the assembly version for some specific projects in our solution only.

Because I liked the idea of setting the version number for the build like user195275 recommended, I did some research of how do do it for a single csproj file.

So in combination with the following thread How to read the assemblyversion from assemblyInfo.cs? We came up with the following solution:

<Target Name="BeforeBuild">
    <ReadLinesFromFile File="$(MSBuildProjectDirectory)\Properties\AssemblyInfo.cs">
        <Output TaskParameter="Lines"
                ItemName="ItemsFromFile"/>
    </ReadLinesFromFile>

    <PropertyGroup>
        <Pattern>\[assembly: AssemblyVersion\(.(\d+)\.(\d+)\.(\d+)\.(\d+)</Pattern>
        <In>@(ItemsFromFile)</In>
        <Out>$([System.Text.RegularExpressions.Regex]::Match($(In), $(Pattern)))</Out>
    </PropertyGroup>

    <CreateProperty Value="$(Out.Remove(0, 28))">
        <Output TaskParameter="Value" PropertyName="AssemblyVersion"/>
    </CreateProperty>
</Target>

what it does: It parses the version number out of the AssemblyInfo.cs and set it as Property as in Aaron Martens answer. Which leads to a single point of maintenance for the version number for us.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!