Falle in XAML: “Payload file does not exist” compile error

29. Mai 2013

Entwicklung für Windows Store Apps (ehem. Metro), basiert auf XAML und sollte damit für jeden Entwickler, der schon mal WPF oder Silverlight gemacht hat, einen schnellen Einstieg bieten. Das kann man so auch durchaus unterschreiben.

Allerdings ist XAML nicht gleich XAML, und manchmal tauchen die Unterschiede an Stellen auf, an denen man nicht damit gerechnet hätte. Die Compiler-Fehlermeldung "Payload file does not exist" ist so ein Fehler, der die grundlegenden Unterschiede deutlich macht.

Es ist eine typische Situation für Entwickler: Man hat eine Komponente, die nicht anwendungsspezifisch ist, und die man daher in ein eigenes Assembly auslagern will, um sie ggf. auch in anderen Projekten wiederzuverwenden. Hat man hier Controls, etwa einen ImageButton, dann verwendet man als Template für die Klasse ein “Templated Control”, das sein Layout als XAML-Template in der Datei .ThemesGeneric.xaml mitbringt.

Die XAML-Datei wird als Embedded Resource in das Assembly gepackt und dort zur Laufzeit ausgelesen.

Auf diese Art kann man auch eigene Ressourcen bereitstellen, etwa vordefinierte Styles. Diese werden dann einfach per Referenz auf das andere Assembly in die eigene Ressourcen aufgenommen, etwa:

   1: <Application.Resources>

   2:     <ResourceDictionary>

   3:         <ResourceDictionary.MergedDictionaries>

   4:             <ResourceDictionary Source="SDX.Common.UI;component/Assets/SDXStyles.xaml" />

   5:             […]

   6:         </ResourceDictionary.MergedDictionaries>

   7:     </ResourceDictionary>

   8: </Application.Resources>

So arbeiten neben bei auch alle 3rd-Party-Controls und Bibliotheken.

Nicht so unter Windows 8… Trauriges Smiley

 

Versucht man, dieses Verfahren auf eine AppStore-Anwendung zu übertragen, begrüßt einen der Compiler möglicherweise mit folgender Fehlermeldung:

4>C:Program Files (x86)MSBuildMicrosoftVisualStudiov11.0AppxPackageMicrosoft.AppXPackage.Targets(921,9): error APPX0702: Payload file ‘D:TFSPrivatbilanzSDX.Common.UIinWinRTDebugSDX.Common.UIThemesGeneric.xaml’ does not exist.

Interessanterweise tritt der Fehler nur auf, wenn die besagte DLL in einer separaten Solution gepflegt wird, nicht wenn sie in der gleichen Solution enthalten ist.

Wenn man sich auf der Suche nach der Ursache und einer Lösung macht, stolpert man womöglich über folgende Aussage im Forum:

“2) You’re going to hate this – this is the ultimate solution.  Metro apps do not allow for embedded content, and that includes XAML pages.  Distributing classes is going to have to be done as a package of dlls and other resources, not a single dll.  Those responsible for this behavior have heard all of the issues around this, and it’s not going to change.” (link)

Anders ausgedrückt: It’s not a bug, it’s a feature. And it’s not going to change!

Damit ist zunächst klar, dass ein (durchaus wichtiges) XAML-Feature in XAML nicht zur Verfügung steht. Was unmittelbar die Frage aufwirft, warum das so ist, und welche Alternativen man hat.

Warum?

Die Frage nach dem Warum erschließt sich relativ schnell, wenn man sich folgenden Unterschied klarmacht:

  • XAML in WPF und Silverlight ist in .NET implementiert. Damit stehen dieser Implementierung auch alle .NET Features zur Verfügung.
  • XAML in WinRT ist eine native Implementierung, der “nur” die WinRT-Features zur Verfügung stehen.

Und hier kommt die Krux: Embedded Resources – als solche werden auch die besagten XAML-Files in WPF und Silverlight abgelegt und angesprochen – sind ein .NET-Feature! Sie sind als Manifest Resources Teil des .NET-Assembly-Formats, nachzulesen hier.

Anmerkung: In der nativen Welt gibt es ebenfalls Ressourcen, in Form von .rc-Dateien bereitgestellt, nachzulesen hier. Diese haben jedoch nicht das geringste mit .NET-Ressourcen zu tun, sondern dienen der Beschreibung von klassischen Win32 Fenstern, Menüs, etc., vgl. hier.

Und da Microsoft in WinRT zwar die .NET Metadaten in die native Welt übernommen hat (in Form von separaten.winmd-Dateien), aber eben nicht das DLL-Format geändert hat, müssen wir eben auf .NET-Ressourcen verzichten. Das ist der Preis dafür, dass WinRT-Komponenten auch von nativen C++-Anwendungen und aus JavaScript heraus nutzbar sind.

Alternativen?

Wer sich auch in Windows 8 in einer reinen .NET-Welt bewegt, dem steht es natürlich frei (mit überschaubaren Aufwand) das Laden von .NET Ressourcen selbst durchzuführen, und so den gewohnten Mechanismus nachzubauen.

Wer das nicht tun will oder kann, der muss die XAML-Dateien wohl oder übel separat ausliefern. Tim Heuer hat dafür die Optionen zusammengetragen:

  • Bauen eines Extension SDKs (VSIX package)
  • Bauen eines NuGet packages

Die Details zu diesen Ansätzen beschreibt er in seinem Beitrag Building a deployable custom control for XAML Metro style apps.

Übrigens nutzt das WinRT XAML Toolkit beide Wege, wie auch von Tim empfohlen:

“I also think that creating an Extension SDK *and* a NuGet package are the best ways of thinking about it as a producer.  This enables your consumers to have the greatest flexibility in how they want to consume your control. […]”