Author Archives: alexfeinberg

Solving cross-assembly WPF Resource problems once and for all

I wrote several posts about WPF weakness when it comes to sharing resources. Our use case, at my at work, where we load multiple versions of an assembly into a single app domain this problem has been felt most acutely. As I wrote previously, using the most documented way of loading resource dictionaries, unless you specify a version of an assembly when loading a resource dictionary you are running the risk of loading a wrong resource dictionary because you may have multiple versions of same library loaded into an app domain. And such problems are notoriously difficult to catch and understand.

At least now, it’s easy to solve. I am glad to announce that I found a definitive way to share resources across assembly boundaries without running into versioning issues. The key to doing this is to never load resource dictionary from another assembly using cross-assembly Uris. I did not know that there was another way until recently. All the examples of loading resource dictionaries, that I can recall, use the Source=”{PackUri}” method of loading, but there’s a way to reference resource dictionary using code.

A ResourceDictionary, just like any other class that can be defined in Xaml, can also contain the “code behind” portion, a source code file associated with the main Xaml file. If you already have resources defined in a resource dictionary somewhere in your assembly, you don’t have to change it. Instead you’ll create a brand new resource dictionary which you would use to export the resource for sharing. For example, let’s say you have a resource dictionary defining the colors of the stoplight. It lives in an assembly called ResourceSharing, in a namespace ResourceSharing.Resources in a file called Resources.xaml.


<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
    <SolidColorBrush x:Key="Go" Color="Green"/>
    <SolidColorBrush x:Key="Stop" Color="Red"/>
    <SolidColorBrush x:Key="PrepareToStop" Color="Yellow"/>
    
</ResourceDictionary>

To export this ResourceDictionary, create a new Resource Dictionary for export. Let’s call it ResourceSharing.Resources.ExportResourceDictionary. I’ll consist of the xaml and the C# portions

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
                    x:Class="ResourceSharing.Resources.ExportResourceDictionary"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Resources.xaml"/>
    </ResourceDictionary.MergedDictionaries>
    
</ResourceDictionary>

Notice the Class definition on line 2.

The code behind class looks like this

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ResourceSharing.Resources
{
    public partial class ExportResourceDictionary
    {
        //Expose it as singleton to avoid multiple instances of this dictionary
        private static readonly ExportResourceDictionary _instance = new ExportResourceDictionary();

        public static ExportResourceDictionary Instance
        {
            get { return _instance; }
        }

        public ExportResourceDictionary()
        {
            InitializeComponent();
        }
    }
}

Let’s also create a namespace we can use to make it easier to reference these resources from another assembly using namespaces

using System.Windows.Markup;

[assembly:XmlnsDefinitionAttribute("http://my.schemas.com/web/resources", "ResourceSharing.Resources")]

How to Share

Here’s how to consume the resources exported from the ResourceSharingAssembly. For this example, I had an executable assembly called ResourceSharingClient, where I am referencing exported resource right in the Main Window. Notice the use of the exported namespaces in line 2 and the way that I am referencing a static instance of the ExportResourceDictionary. Such references are version-proof because .NET knows the version of the dependent assemblies. There’s no need to dynamically discover the dependent assembly and the code actually looks a lot neater too. I know that such approach requires a bit more work, but the final result is a lot safer. I only wished that such approach had been advertised by Microsoft WPF evangelists from the beginning.

<Window
        x:Class="ResourceSharingClient.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:rs="http://my.schemas.com/web/resources"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        mc:Ignorable="d"
        Title="MainWindow" d:DesignWidth="111.5" d:DesignHeight="109.5" Width="120" Height="130">
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <x:Static Member="rs:ExportResourceDictionary.Instance"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
    <Border BorderBrush="Black" BorderThickness="2" HorizontalAlignment="Center" VerticalAlignment="Center" Padding="2" CornerRadius="2" Background="Silver">
        <StackPanel Orientation="Vertical">
            <Ellipse Width="20" Height="20" Fill="{DynamicResource Stop}" Stroke="Black" StrokeThickness="1"/>
            <Ellipse Width="20" Height="20" Fill="{DynamicResource PrepareToStop}" Stroke="Black" StrokeThickness="1"/>
            <Ellipse Width="20" Height="20" Fill="{DynamicResource Go}"  Stroke="Black" StrokeThickness="1"/>
        </StackPanel>
    </Border>
</Window>
Advertisements

ASUS Product Support is Crap (continued)

Last Friday, I took a day to do run some errands and to tie up some lose ends including the old ASUS laptop that was returned to be the 3rd time, still broken.  As you may recall, from the previous post (https://alexfeinberg.wordpress.com/2015/02/07/asus-is-crap/), I was sending the laptop back to them the 3rd time after having it returned to me in the original broken condition 2 times.  Well, 3 times was *not* the charm for me.

Since I bought the laptop at Costco, I thought of using their Costco Concierge service to attempt to return the defective laptop back to them, but the best they could do for me was to contact ASUS to resolve it.  So back on the phone with ASUS, their customer service manager could, again, only offer me to have it sent in to them, but this time, he would request a replacement.  Progress?  Maybe.  He stressed to me that there is no guarantee that the laptop would be replaced, but at least he would initiate the process.  He also offered me an overnight shipping to them this time around.  Probably another benefit of having Costco on the phone with me.

In addition, I tweeted about my experience and to @ASUSUSA , who replied, offering to help:

asustweet

So I mustered confidence to finally send it in the 4th time.  Needless to say, I was very skeptical and unfortunately, a little pessimist in me turned out to be correct.

They received the laptop on Monday, the 20th of April 2015.  On Tuesday, the 21st, I decided to check the status.  This is what it looked like:

asusstatus

On one hand, the word swap was now appearing in the status, a positive indicator that they are, indeed, looking to replace the laptop with another one. But, it also said something about waiting on the customer confirmation…  Hmm… Confimation of what?    I had to call to find out.

The tech on the other end politely put me on hold and spent a good 5 mins figuring out what that meant.  He came back and told me that I should have gotten an email asking me to confirm the specs of the proposed replacement.  Needless to say, I had not gotten such email.  I mentioned it to him, but he promised that it would come in 24 hours.

On Wednesday afternoon, a day after speaking with the tech, and after checking my spam folders and finding no email from ASUS, I decided to call them again.   They could not help me, because their manager could not access the system for some reason and they did not want to do a 3-way call with me.  They insisted in calling me back within 24 hours.  I even tried to go the Costco Concierge route, but those guys are beginning to sense a losing battle as well and starting to disengage from this toxic case.  They too said to wait a day.

Well, there’s always tomorrow, right?  As of right now, I don’t even have the broken laptop until I confirm an email that I never received.  Back to twitter I go.

Solving the WPF Resource Key Collision

In my previous post about WPF Resource Keys, I talked about how WPF handles resource resolution when multiple resources with same key end up in loaded into same ResourceDictionary via merging.  In this post, I will present a solution.

So, apparently, Microsoft thought about it and provided a solution in a form of a class class called System.Windows.ComponentResourceKey.  This class is derives from a base class called System.Windows.ResourceKey, which is used inside the WPF resource resolution code.

So what does it do and how does work?

Normally, the WPF resource resolution logic assumes that all keys are objects used to identify resources in a ResourceDictionary.  Unlike a regular dictionary, a ResourceDictionary, technically, supports having multiple resources added with same key, but during the lookup operation, only the last resource added for a specific key will be returned.  So during the ‘merge’ operation, the new resources will overlay the existing ones if the keys clash, and during the ‘unmerge’ operation (ResourceDIctionary.MergedDictionaries.Remove()) the previously covered resource will emerge as ‘active’, so all the resources referenced via DynamicResouce will update.

The above gets more interesting if the key is of type System.Windows.ResourceKey.  If so, the resource resolution logic will only look at the resources which live in a dictionaries defined in the assembly referenced by the ResouceKey.  Moreover, when such set of dictionaries is found, the instance of the key is compared against other instances of the key to see if there’s a match, and because the System.Windows.ComponentResourceKey class overrides Equals and GetHashCode, the rules of equality take into consideration a bunch of parts, not just string value.

Here’s the code of the Equals and GetHashCode methods:

public override bool Equals(object o)
{
    var componentResourceKey = o as ComponentResourceKey;
    if (componentResourceKey == null)
    {
        return false;
    }
    // Ether neither objects define the TypeInTargetAssembly, 
    // or if both are defined, then make sure they are same
    if (!((componentResourceKey._typeInTargetAssembly != null) ? 
              componentResourceKey._typeInTargetAssembly.Equals(this._typeInTargetAssembly) :
             (this._typeInTargetAssembly == null)))
    {
        return false;
    }
    //If resourceId is null make sure they are both null
    if (componentResourceKey._resourceId == null)
    {
        return this._resourceId == null;
    }
    //otherwise do a simple equals
    return componentResourceKey._resourceId.Equals(this._resourceId);
}

public override int GetHashCode()
{  
   //use both, the type and the resouceid to generate a complex hashcode
   return ((this._typeInTargetAssembly != null) ? this._typeInTargetAssembly.GetHashCode() : 0) 
               ^ ((this._resourceId != null) ? this._resourceId.GetHashCode() : 0);
}

So when creating a reusable WPF component make sure you use the ComponentResourceKey class for your resource keys, do not just use strings and, obviously, refer to such resources using the ComponentResourceKey as well.  You can designate a single marker type (an empty public class) that lives in your assembly as a TypeInTargetAssembly to make it easier to reference your assembly when both, defining and referencing a resource.  This StackOverflow question showcases the use: http://stackoverflow.com/questions/337803/how-do-i-get-the-actual-resource-from-a-componentresourcekey.


//Exposing
public class Resources
{
    public static ComponentResourceKey BaseControlStyleKey
    {
        get
        {
            return new ComponentResourceKey(typeof(Resources), "BaseControlStyle");
        }
    }
}


//Using
myTextBox.Style = 
        Application.Current.TryFindResource(Resources.BaseControlStyleKey)
        as Style;



<!--Exposing -->
<Style x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:Resources}, ResourceId=BaseControlStyle}" TargetType="{x:Type FrameworkElement}">
    <Setter Property="Margin" Value="4,4,0,0" />
</Style>

<!-- Using -->
<TextBlock Style="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type local:Resources}, ResourceId=BaseControlStyle}}"/>

<!-- Using in xaml by relying on the static property defined above -->
<TextBlock Style="{DynamicResource {x:Static local:Resources.BaseControlStyleKey}}"/>

ASUS products support is crap

About a year an a half ago, in September 2013, I bought an ASUS laptop which came with a 2-year warranty. It’s an OK laptop that’s been used as the family PC and did what was expected.

Recently it broke. It would not boot up and occasionally the screen would show something of a static at start up. Taking advantage of the warranty, I contacted ASUS, explained them my situation and was offered to ship the laptop to them for free for a repair. The shipping process was straightforward and in about a week-and-a-half, I got the laptop back. I quickly turned it on and realized that they did not fix it. At first, I thought that I was doing something wrong, because having a just-repaired product behave broken, just as though no-one touched it would probably raise some concerns from people whose job it is to verify that a product works before sending it back after repair.

After fiddling with it for about 20 minutes I realized that they simply did not fix it. I called back started to complain about the failure of this repair exercise. The person on the phone would not confirm the failure, but could only offer me to have the product shipped to them again so that they will “fix it for sure“. I wish I had known better to have asked to have it fixed for sure the first time. I did not expect to have to have been so specific. Who knew that the magic “fix it for sure” requirement is required when sending broken products for repair.

I tried to escalate the situation to someone who can adjust the process, such as by sending me a working replacement product first, so that I can send in the broken product to them later, but no matter who picked up the phone, they would not agree to this. The best they could offer me was to give me a faster shipping label (down to 3 days from 5 originally) so that the broken laptop would reach them faster. The promise to “fix it for sure” was repeated several times and I finally resolved to follow their process again, to send their laptop to them again, and to wait for it to be fixed and sent it back to me again.

Another week-and-a-half later, I received the laptop back. Without waiting too long, I plugged it in and was pleasantly surprised to see the screen light up with a familiar logo. Soon, it booted up to a brand new installation of Windows. I felt delighted and a little sorry for having raised so much hell about it the first time. Perhaps, on occasion, even the good guys screw up and let things fall through the cracks.

I began the process of re-configuring the OS to my needs and upgrading and installing stuff that we need, but soon the system asked me to activate Windows. At this point, any shred of remorse I may have had left quickly dissipated. I was left with an unusable laptop again and feeling helpless because ASUS’s promise to fix it for sure was not, really, a promise one could rely on.

I decided to give them another chance. I called up their support team and was told that they, actually, have a systematic way of handling this problem without having to send the laptop in again. Seems like the routinely ‘forget’ to activate windows during their repair process. WTF? The support tech opened another ticket and pretty soon I had an email instructing me to login to their website, register the laptop and click the special button to get access to the Windows Activation Key. I began feeling better again. I quickly followed the procedure, but after clicking the magic button I saw this message instead

asus

So I called them back again and was told that they would do something special for me – they would send me another email with an actual activation key later same day.  I hung up and waited, waited and waited some more, but such email never arrived.

So a few days later I followed up with them only to find out that they cannot have promised me that.  The guy on the phone did, actually, note that I was promised such email, but he was at a loss about how one would make such promise.  I can only imagine is was done to get me off the phone.  Again, the best they could do was to offer me to have my laptop sent to them again and to wait for it to be “fixed it for sure” again.

I dropped it with fedex for the 3rd time earlier today.  I hope that 3 times is the charm.  I don’t know why anyone would accept such treatment from any vendor.  I will never buy anything ASUS again and strongly recommend against anyone buying their products.

WPF Resource Key Collision

Our framework allows developers to create add-ins (modules), which are loaded at run time.  It’s possible for each developer to create custom resources and merge them into a Resource Dictionary on the Application object.  So what happens when two different add-ins define a resource with the same key?  How does WPF handle such key collision?

To answer this question, II created a small application which tests the ability of a single resource dictionary to load multiple merged dictionaries which have objects with same key. The code is below but I did discover that

  1. the app does not crash – WPF handles redifinition of the key gracefully
  2. the last one wins – whichever dictionary defining the entry with the same key loads last is the one that’s going to be resolved when app asks for such resource using the DynamicResource
  3. the original definition becomes effective when the override is removed – removing the duplicate definition makes the original value effective

It’s important to note that if the key already exists in the primary dictionary, the one into which other dictionaries are merged, the discovery rules will ignore any re-definition of the key, according to docs here.

Resources in a merged dictionary occupy a location in the resource lookup scope that is just after the scope of the main resource dictionary they are merged into. Although a resource key must be unique within any individual dictionary, a key can exist multiple times in a set of merged dictionaries. In this case, the resource that is returned will come from the last dictionary found sequentially in the MergedDictionaries collection. If the MergedDictionaries collection was defined in XAML, then the order of the merged dictionaries in the collection is the order of the elements as provided in the markup. If a key is defined in the primary dictionary and also in a dictionary that was merged, then the resource that is returned will come from the primary dictionary. These scoping rules apply equally for both static resource references and dynamic resource references.

<Application x:Class="Test.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="rd1.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
    <sys:String x:Key="MyString">Hello</sys:String>
    
</ResourceDictionary>
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
    <sys:String x:Key="MyString">Hello</sys:String>
    
</ResourceDictionary>
<Window x:Class="Test.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">
    <Grid>
        <Button Click="HandleClick" Content="{DynamicResource MyString}"/>
    </Grid>
</Window>
using System;
using System.Windows;

namespace Test
{
    public partial class MainWindow : Window
    {
        private bool _remove;
        public MainWindow()
        {
            InitializeComponent();
        }

        private void HandleClick(object sender, RoutedEventArgs e)
        {
            if (_remove)
                Application.Current.Resources.MergedDictionaries.Remove(Application.Current.Resources.MergedDictionaries[1]);
            else
                Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary() { Source = new Uri("rd2.xaml", UriKind.Relative) });

            _remove = !_remove;
        }
    }
}

Launching ClickOnce Application with parameters from a shortcut

ClickOnce is a handy technology from Microsoft that makes it easy to deploy desktop Windows applications using the web browser. IE is naturally supported out of the box, Firefox and Chrome have add-ons and apps respectively to make ClickOnce work.

When installed, ClickOnce created shortcuts for the application under user’s Start Menu (Windows 7 and 10) in the folder whose name matches the name of the company specified in the manifest of the application. But the shortcuts to ClickOnce applications are not your typical *.lnk files, they are .appref-ms files. An appref-ms file is a text file saved in UTF-16 or “UCS2 Little Endian” format. Format is important getting it wrong will make the file unrecognizable to the system.

Here’s a typical text from the appref-ms file.

https://www.application.com/MyApplication.application#MyApplication.app, Culture=neutral, PublicKeyToken=f89fd69c4bd8bc85, processorArchitecture=x86

It consists of 4 parts separated by comas:

  1. Url#Name – I noticed that the URL is not important, but the bame is – it has to match the name of the application in the manifest
  2. Culture – in the format “Culture=”
  3. PublicKeyToken – in the format “PublicKeyToken=”
  4. Target CPU – in the format “processorArchitecture=”
  5. To pass parameters to the clickonce application you must invoke the appref.ms file either from the command line with parameters. I was hoping to create a real shortcut (lnk) file with the appref.ms file as a target along with arguments, but Windows does not make it easy. It’s a lot easier to manage shortcuts to normal executable files, so that’s the approach I would recommend in this solution.

    You can add a second windows executable file to your solution. It’s job would be to generate a temporary appref.ms file on the fly and launch it with parameters using .NET’s Process.Start command. When your real app starts, make sure to copy this helper executable to user’s %appdata%\YourAppName location or ClickOnce’s ApplicationDeployment.CurrentDeployment.DataDirectory (will only be available once properly deployed). Then you can create a shortcut to the executable anywhere you like and even add command line arguments to it. Your executable then will generate the appref.ms file on the fly somewhere in temp directory, and launch it along with the arguments you specified. You can get the parts necessary to generate this file from the ApplocationIdentity object: http://msdn.microsoft.com/en-us/library/system.applicationidentity.fullname%28v=vs.110%29.aspx

    Hope this works for you.

    PS. Don’t forget to delete the dummy appref.ms file you generate after your app launches.

Microsoft mechanism to load WPF resources is broken. (looking for a better title)

I’ve been using WPF along with .NET and have been a staunch supported of the technology for years now. The pattern of separating presentation from logic gained a lot of traction. Technologies such as iOS’s cocoa touch, JavaFX and others follow same model. Even HTML5 forced all the pure presentation/layout markup out from HTML and into CSS. So the idea is sound. But what about execution.

WPF implements this pattern using XAML. XAML markup is used to design the visual this xaml is stored in the xaml file, whose name usually matches the name of the class. For example, MyWindow.xaml. During compilation, an auto-generated file marries the code that defines the visual (MyWindow.xaml.cs) and the xaml file itself (MyWindow.xaml.cs). The autogenerate file would be named MyWindow.g.i.cs and it would contain the implementation of the InitializeComponent() method that’s referenced in the main MyWindow.xaml.cs class. The contents of the method looks something like this:

[System.Diagnostics.DebuggerNonUserCodeAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute(&quot;PresentationBuildTasks&quot;, &quot;4.0.0.0&quot;)]
public void InitializeComponent() {
	if (_contentLoaded) {
		return;
	}
	_contentLoaded = true;
	System.Uri resourceLocater = new System.Uri(&quot;/MyLibrary;component/mainwindow.xaml&quot;, System.UriKind.Relative);
	
	#line 1 &quot;..\..\MainWindow.xaml&quot;
	System.Windows.Application.LoadComponent(this, resourceLocater);
	
	#line default
	#line hidden
}

So far so good right. OK, let’s talk about some of the advanced uses of .Net. Specifically support for having multiple versions of same assembly (dll) to be loaded in a single AppDomain. Such use case comes handy in a framework use case. A framework supports multiple plug-in modules, each can be developed by other developers who may rely on different versions of common libraries. I am not talking about things like log4net, I am talking about shared assemblies that define common logic and can be reused by each module. If 2 modules are loaded into a single app-domain and both depend on their ‘private’ version of the common assembly, a good framework should not force both modules to share latest version of the library, because

  • ‘latest’ does not mean backwards-compatible
  • ‘latest’ may have bugs that ‘earlier’ version did not
  • …I can go on for a while on this one. It’s 2014, dll hell is well known to software developers of the 20th century.

Now, let’s say that 2 different versions of the shared library called MyLibrary are loaded and one version is used by Module1 and another version is used by Module2. During compilation, assembly name along with the version are recorded in modules’ manifests so that when .NET encounters request to execute code defined in MyLibrary the source module will tell .Net which version of MyLibrary contains the code it wants executed. This is a basic building block of .NET and a very attractive feature.

Now, what about WPF? Well that’s not that clear is it? Look at Line #8 above: System.Uri resourceLocater = new System.Uri(“/MyLibrary;component/mainwindow.xaml”, System.UriKind.Relative). It defines what it calls a resourcelocator which the system will use to find the xaml resouce in an assembly called MyLibrary. I don’t know how WPF resolves the assembly, but if I were to write the logic for resolution, I would not have enough information to find a proper assembly MyLibrary, because I don’t know which version to load. I would have to guess. And as we know in any science guessing is bad. Computer Science is an exact… well… science. Guessing, hoping, holding fingers crossed or other techniques that do not produce determinate result will break at the worst possible moment! I can see how developers armed with inaccurate knowledge or with little experience make mistakes like this, but when Microsoft’s tools generated code that’s faulty, and make it impossible to intercept and fix, there is no other way to call it – they fucked up! How can they expect developers to trust their tooling if they, themselves, auto-generate code that sometimes breaks.

This breaks in a most bizarre way, too. MyLibrary version 2 was loading the MainWindow class, which then was running the InitializeComponent() method above, encountered the LoadComponent call, which asked to load the resource from the MyLibrary assembly without a version, and the code picked the wrong version of the MyLibrary assembly, which did not have the MyWindow class, nor the resource. What a way to blow up!

Such nonsense was reported to them too! This connect ticket marked as “Wont’t Fix” describes the problem: https://connect.microsoft.com/VisualStudio/feedback/details/668914/xaml-generated-code-uses-resource-uri-without-assembly-strong-name. StackOverflow also has references to this problem: http://stackoverflow.com/questions/1453107/how-to-force-wpf-to-use-resource-uris-that-use-assembly-strong-name-argh/6341678#6341678. Luckily one of the suggestions led me to the best solution.

The Fix


We were lucky to have our builds automated via scripts which rely on MSBuild to build the solutions. All I needed to do is to modify the script and pass the /p:AssemblyInfo=$version parameter to MSBuild. (http://stackoverflow.com/a/26689750/195275). This would tell the build system to add assembly version to the Resource reference in line 8. It now looks like the code below. Problem solved, but could have been avoided on the first place!

Shame on you, Microsoft!

[System.Diagnostics.DebuggerNonUserCodeAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute(&quot;PresentationBuildTasks&quot;, &quot;4.0.0.0&quot;)]
public void InitializeComponent() {
	if (_contentLoaded) {
		return;
	}
	_contentLoaded = true;
	System.Uri resourceLocater = new System.Uri(&quot;/MyLibrary;V1.0.23;component/mainwindow.xaml&quot;, System.UriKind.Relative);
	
	#line 1 &quot;..\..\MainWindow.xaml&quot;
	System.Windows.Application.LoadComponent(this, resourceLocater);
	
	#line default
	#line hidden
}