Skip to content
This repository has been archived by the owner on Aug 30, 2023. It is now read-only.

Missing Documentation for Starting XAML Islands #151

Closed
ken30096 opened this issue Jul 25, 2019 · 9 comments
Closed

Missing Documentation for Starting XAML Islands #151

ken30096 opened this issue Jul 25, 2019 · 9 comments

Comments

@ken30096
Copy link

Xaml parsing in a Universal Windows Class Library via Xaml Islands fails in .Net Core

I'm submitting a Bug report

Exception thrown at 0x00007FF9123FA839 (KernelBase.dll) in TestWPFApp.exe: WinRT originate error - 0x80004005 : 'Cannot locate resource from 'ms-appx:///UWPClassLibrary/MyPage.xaml'.'.
onecore\com\combase\winrt\error\restrictederror.cpp(1014)\combase.dll!00007FF9136FB690: (caller: 00007FF9136F60BC) ReturnHr(3) tid(514c) 8007007E The specified module could not be found.
Exception thrown: 'Windows.UI.Xaml.Markup.XamlParseException' in TestWPFApp.dll
An exception of type 'Windows.UI.Xaml.Markup.XamlParseException' occurred in TestWPFApp.dll but was not handled in user code
XAML parsing failed.

Current behavior

The issue manifests upon exection of the application.

Expected behavior

Xaml parsing in a Universal Windows Class Library via Xaml Islands to succeed in .Net Core

Minimal reproduction of the problem with instructions

MINIMAL REPRO PROJECT

The repository is on GitHub.

STEPS TO REPRODUCE

Create a WPF App .Net Core project (TestWPFApp)

Set Configuration manager platform for debugging: 64x

Add NuGet Packages

  • Microsoft.Windows.SDK.Contracts v10.0.18362.2002-preview
  • Microsoft.Toolkit.Wpf.UI.XamlHost v6.0.0-preview6.4

Add "Windows Application Packaging Project C#".
  • Project Name: BuildWAP
  • Target version.: 1903
  • Minimum version: 1903
  • Set as StartUp Project
  • Right-Click on Applications and add a reference to the WPF .Net Core project

Unload and Edit .waproj and add the following before the last statement and Reload:
  <Target Name="_StompSourceProjectForWapProject" BeforeTargets="_ConvertItems">
    <ItemGroup>
      <!-- Stomp all "SourceProject" values for all incoming dependencies to flatten the package. -->
      <_TemporaryFilteredWapProjOutput Include="@(_FilteredNonWapProjProjectOutput)" />
      <_FilteredNonWapProjProjectOutput Remove="@(_TemporaryFilteredWapProjOutput)" />
      <_FilteredNonWapProjProjectOutput Include="@(_TemporaryFilteredWapProjOutput)">
      <!-- Blank the SourceProject here to vend all files into the root of the package. -->
      <SourceProject></SourceProject>
      </_FilteredNonWapProjProjectOutput>
    </ItemGroup>
  </Target>  

Add a Universal Windows Class Library project to the solution.
  • Project Name: UWPClassLibrary
  • Target version.: 1903
  • Minimum version: 1903

Add a UWP Blank Page to the UWP Class Library project
  • Page Name: MyPage

Open the code behind file called MyPage.xaml.cs and add the following field.
  • public string WPFMessage { get; set; }

Open the MyPage.xaml in the designer and add the following control:
<StackPanel Background="LightCoral">
    <TextBlock>This is a simple UWP XAML page</TextBlock>
    <Rectangle Fill="Blue" Height="100" Width="100"/>
    <TextBlock Text="{x:Bind WPFMessage}" FontSize="50"></TextBlock>
</StackPanel>

Unload the UWPClassLibrary project and edit as follows:
Locate the following statement:
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />

Before the located statement add the following:

<PropertyGroup>
  <EnableTypeInfoReflection>false</EnableTypeInfoReflection>
  <EnableXBindDiagnostics>false</EnableXBindDiagnostics>
</PropertyGroup>

After the located statement add the following:

<PropertyGroup>
  <HostFrameworkProject>YOUR_WPF_PROJECT_NAME</HostFrameworkProject>
</PropertyGroup>
<PropertyGroup>
<!-- Copy source and build output files to hostapp folders -->
<!-- Default Winforms/WPF projects do not use $Platform for build output folder -->
  <PostBuildEvent>
    xcopy "$(TargetDir)*.xbf"            "$(SolutionDir)$(HostFrameworkProject)\bin\$(Configuration)\$(ProjectName)\" /Y
    xcopy "$(ProjectDir)*.xaml"          "$(SolutionDir)$(HostFrameworkProject)\bin\$(Configuration)\$(ProjectName)\" /Y
    xcopy "$(ProjectDir)*.xaml.cs"       "$(SolutionDir)$(HostFrameworkProject)\$(ProjectName)\" /Y
    xcopy "$(ProjectDir)$(IntermediateOutputPath)*.g.*" "$(SolutionDir)$(HostFrameworkProject)\$(ProjectName)\" /Y
  </PostBuildEvent>
</PropertyGroup>
<Target Name="SpicNSpan" AfterTargets="Clean"> <!-- common vars https://msdn.microsoft.com/en-us/library/c02as0cs.aspx?f=255&MSPPError=-2147217396 -->
     <RemoveDir Directories="$(TargetDir)" /> <!-- bin -->
     <RemoveDir Directories="$(ProjectDir)$(BaseIntermediateOutputPath)" /> <!-- obj -->
</Target>

Exchange the text "YOUR_WPF_PROJECT_NAME" of the "HostFrameworkProject" element with the name of your WPF project (TestWPFApp) and Reload
Build the UWP Class Library
To keep the WPF application in sync with future changes to the UWP class library, add a build dependency by right clicking on the WPF project and select choose Build Dependencies, Project Dependencies and check the UWP class library.
In the WPF project open MainWindow.xaml and add the following:
xmlns:XamlHost="clr-namespace:Microsoft.Toolkit.Wpf.UI.XamlHost;assembly=Microsoft.Toolkit.Wpf.UI.XamlHost"
<XamlHost:WindowsXamlHost x:Name="xamlHost" InitialTypeName="UWPClassLibrary.MyPage" ChildChanged="MyUWPPage_ChildChanged"/>

Open MainWindow.xaml.cs and add the following:
    private void MyUWPPage_ChildChanged(object sender, EventArgs e)
    {
        // Hook up x:Bind source
        global::Microsoft.Toolkit.Wpf.UI.XamlHost.WindowsXamlHost windowsXamlHost = 
            sender as global::Microsoft.Toolkit.Wpf.UI.XamlHost.WindowsXamlHost;
        global::UWPClassLibrary.MyPage myUWPPage = 
            windowsXamlHost.GetUwpInternalObject() as global::UWPClassLibrary.MyPage;

        if (myUWPPage != null)
        {
            myUWPPage.WPFMessage = this.WPFMessage;
        }
    }

    public string WPFMessage
    {
        get
        {
            return "Binding from WPF to UWP XAML";
        }
    }

Save All and reset the WAP project as the start-up project.
Build the solution and run.

Environment

  • Microsoft Visual Studio Community 2019 Preview version 16.2.0 Preview 4.0.
  • WPF Using C#
  • Windows 10 Pro 1903 OS Build 18362.10005
  • Device Form Factor: Desktop
  • App min and target version
    • Target version: Windows 10. version 1903 (10.0; Build 18362)
    • Min version: Windows 10. version 1903 (10.0; Build 18362)
  • NuGet Packages
    • Microsoft.Windows.SDK.Contracts v10.0.18362.2002-preview
    • Microsoft.Toolkit.Wpf.UI.XamlHost v6.0.0-preview6.4
    • Microsoft.NETCore.App v3.0.0-preview7-27912-14
    • Microsoft.NETCore.UniversalWindowsPlatform v6.2.8
    • Microsoft.VCRTForwarders.140 v1.0.1-rc

Background

This application is an exercise titled Add a custom UWP control taken from the Microsoft webapage "WindowsXamlHost control for Windows Forms and WPF". It is a proof-of-concept attempt at using a custom UWP control via Xaml Islands.

@azchohfi
Copy link
Contributor

We are working on updating the documentation with new guidance. The process to host custom controls using Xaml Islands changed a little bit with new versions of the toolkit.

@ken30096
Copy link
Author

Thanks. If it's okay with you I would prefer to leave this opened so that I can be notified of the change.

@azchohfi
Copy link
Contributor

Let me know if these steps help:

  1. Add a Blank App (Universal Windows) project to the solution (UWP App, NOT class library or Windows runtime component)
  2. In the UWP project:
    • Install Microsoft.Toolkit.Win32.UI.XamlApplication package (preview 7).
    • In App.xaml, replace the default XAML with the following to derive the App class from XamlApplication.
<Toolkit:XamlApplication
    x:Class="MyUWPApp.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:Toolkit="using:Microsoft.Toolkit.Win32.UI.XamlHost"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MyUWPApp">
</Toolkit:XamlApplication>
  1. In App.xaml.cs, make App class public, remove the inheritance from Application, call this.Initialize() before this.InitializeComponent(), and delete OnLaunched override method.
  2. Add anything you want/need to share with the WPF project inside this UWP project.
  3. In the WPF project:
    • Install Microsoft.Toolkit.Wpf.UI.XamlHost package (preview 7).
    • Reference the UWP project.
    • Add the XAML Island

@hansmbakker
Copy link

Thank you for the help!

As addition, I believe that

  • remove the inheritance from Application

    should be replaced with inheritance from Microsoft.Toolkit.Win32.UI.XamlHost.XamlApplication

  • Add the XAML Island --> would be clearer if there is added that you need to put InitialTypeName to the MainPage (so not to the App)

@azchohfi
Copy link
Contributor

It's the same thing. This class is partial, so the generated counterpart from the XAML compilation will already inherit from XamlApplication. It's up to you to have it declared again, but it's not required.
InitialTypename should be added to the WindowsXamlHost, with the fully qualified name of the control you want to instantiate inside that Island. The App class is required so the generation of the pri files is complete correctly. This way, in theory, you can use the Windows Community Toolkit, or even WinUI, and it will "just work". Remember that WinUI requires you to add the:
XamlControlsResources class into the App's resources. That needs to be done on the XamlApplication inherited class (effectively the App class you just changed), and yes, it can be done inside the App.xaml file.
FYI, these are NOT the final documentation steps.

@michael-hawker michael-hawker added this to the 6.0.0 milestone Aug 29, 2019
@michael-hawker michael-hawker changed the title xaml parsing Missing Documentation for Starting XAML Islands Aug 29, 2019
@michael-hawker
Copy link
Member

This is waiting on Doc PR here

@ken30096
Copy link
Author

ken30096 commented Sep 6, 2019

My apologies for the delayed response but I had to work on other issues. I did try and follow the solution but I am getting to many weird errors to have the confidence that I am following the instructions correctly. Due to the fact that this is not the final documentation and you are under pressure to keep a September 2019 due date, I will just wait for the documentation be corrected. Thanks

@michael-hawker
Copy link
Member

@ken30096 we've merged that PR so the documentation should be updated online. If you're still having troubles, please let us know and open a new issue with your specific error messages! @marb2000 think we can close this one for now?

@marb2000
Copy link
Contributor

Sure thing!

@ghost ghost locked as resolved and limited conversation to collaborators Dec 1, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants