Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

XAML Islands samples, and home of CppXAML - C++ helpers for XAML

License

MIT, MIT licenses found

Licenses found

MIT
LICENSE
MIT
LICENSE.txt
NotificationsYou must be signed in to change notification settings

asklar/xaml-islands

Repository files navigation

There are a few ways to use UWP XAML in a Win32 app via XAML islands. These options are sometimes independent so there is a matrix of possible combinations:

Setup:

  1. You have a win32 desktop app
  2. AddCppWinRT NuGet package.
  3. AddVCRT forwarders NuGet package. -- or use theHybrid CRT
  4. Add theUnpackaged NuGet package -- or create your own application type; the following assumes you used this package.
#include<Windows.UI.Xaml.Hosting.DesktopWindowXamlSource.h>#include<winrt/base.h>#include<winrt/Windows.UI.Xaml.Controls.h>#include<winrt/Windows.UI.Xaml.h>#include<winrt/Windows.UI.Xaml.Hosting.h>#include<winrt/Windows.UI.Xaml.Interop.h>#include"XamlApplication.h"// Needed if you have a Runtime Component to host markup#include<winrt/AppMarkup.h>usingnamespacewinrt;usingnamespaceWindows::UI::Xaml::Controls;usingnamespaceWindows::UI::Xaml;usingnamespaceWindows::UI::Xaml::Hosting;CppXaml::XamlApplication xapp{nullptr };// This DesktopWindowXamlSource is the object that enables a non-UWP desktop application// to host WinRT XAML controls in any UI element that is associated with a window handle (HWND).DesktopWindowXamlSource desktopXamlSource{nullptr };int APIENTRYwWinMain(_In_ HINSTANCE hInstance,                     _In_opt_ HINSTANCE hPrevInstance,                     _In_ LPWSTR    lpCmdLine,                     _In_int       nCmdShow){LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);LoadStringW(hInstance, IDC_WINDOWSPROJECT1, szWindowClass, MAX_LOADSTRING);MyRegisterClass(hInstance);winrt::init_apartment(apartment_type::single_threaded);// only needed if referencing WinUIauto winuiIXMP =winrt::Microsoft::UI::Xaml::XamlTypeInfo::XamlControlsXamlMetaDataProvider();// only needed if you have a Runtime Component project for compiling markupauto markupIXMP =winrt::AppMarkup::XamlMetaDataProvider();// remove the IXMPs that you don't need    xapp =winrt::make_application(winuiIXMP, markupIXMP);    WindowsXamlManager winxamlmanager =WindowsXamlManager::InitializeForCurrentThread();// needed if using WinUI    xapp.Resources().MergedDictionaries().Append(winrt::Microsoft::UI::Xaml::Controls::XamlControlsResources());    desktopXamlSource =DesktopWindowXamlSource();// Perform application initialization:if (!InitInstance (hInstance, nCmdShow))    {returnFALSE;    }    HACCEL hAccelTable =LoadAccelerators(hInstance,MAKEINTRESOURCE(IDC_WINDOWSPROJECT1));    MSG msg;// Main message loop:while (GetMessage(&msg,nullptr,0,0))    {if (auto xamlSourceNative2 = desktopXamlSource.as<IDesktopWindowXamlSourceNative2>()) {          BOOL xamlSourceProcessedMessage =FALSE;winrt::check_hresult(xamlSourceNative2->PreTranslateMessage(&msg, &xamlSourceProcessedMessage));if (xamlSourceProcessedMessage) {continue;          }        }if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))        {TranslateMessage(&msg);DispatchMessage(&msg);        }    }return (int) msg.wParam;}ATOMMyRegisterClass(HINSTANCE hInstance){    WNDCLASSEXW wcex;    wcex.cbSize =sizeof(WNDCLASSEX);    wcex.style          = CS_HREDRAW | CS_VREDRAW;    wcex.lpfnWndProc    = WndProc;    wcex.cbClsExtra     =0;    wcex.cbWndExtra     =0;    wcex.hInstance      = hInstance;    wcex.hIcon          =LoadIcon(hInstance,MAKEINTRESOURCE(IDI_WINDOWSPROJECT1));    wcex.hCursor        =LoadCursor(nullptr, IDC_ARROW);    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);    wcex.lpszMenuName   =MAKEINTRESOURCEW(IDC_WINDOWSPROJECT1);    wcex.lpszClassName  = szWindowClass;    wcex.hIconSm        =LoadIcon(wcex.hInstance,MAKEINTRESOURCE(IDI_SMALL));returnRegisterClassExW(&wcex);}BOOLInitInstance(HINSTANCE hInstance,int nCmdShow){   hInst = hInstance;// Store instance handle in our global variable   HWND hWnd =CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,      CW_USEDEFAULT,0, CW_USEDEFAULT,0,nullptr,nullptr, hInstance,nullptr);if (!hWnd)   {returnFALSE;   }ShowWindow(hWnd, nCmdShow);UpdateWindow(hWnd);returnTRUE;}LRESULT CALLBACKWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){// Get handle to the core window.auto interop = desktopXamlSource.as<IDesktopWindowXamlSourceNative>();switch (message)    {case WM_CREATE: {// Parent the DesktopWindowXamlSource object to the current window.check_hresult(interop->AttachToWindow(hWnd));auto createStruct =reinterpret_cast<LPCREATESTRUCT>(lParam);// Get the new child window's hwnd      HWND hWndXamlIsland =nullptr;check_hresult(interop->get_WindowHandle(&hWndXamlIsland));SetWindowPos(hWndXamlIsland,nullptr,0,0, createStruct->cx, createStruct->cy, SWP_SHOWWINDOW);#ifdef CREATE_UI_IN_CODE// Option 1: create UI in code:      Controls::TextBlock tb;      tb.Text(L"Hello world!");      desktopXamlSource.Content(tb);#elif defined(CREATE_UI_FROM_STRING)auto tb =Markup::XamlReader::Load(LR"(        <TextBlock Text="Hello world!"/>)").as<TextBlock>();      desktopXamlSource.Content(tb);#else// Option 3: use a Windows Runtime component to define the UI in markup, and load it here      Frame f;      desktopXamlSource.Content(f);      f.Navigate(winrt::xaml_typename<AppMarkup::BlankPage>());#endifbreak;    }case WM_COMMAND:        {int wmId =LOWORD(wParam);// Parse the menu selections:switch (wmId)            {case IDM_ABOUT:DialogBox(hInst,MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);break;case IDM_EXIT:DestroyWindow(hWnd);break;default:returnDefWindowProc(hWnd, message, wParam, lParam);            }        }break;case WM_PAINT:        {        }break;case WM_SIZE:    {      HWND hWndXamlIsland =nullptr;check_hresult(interop->get_WindowHandle(&hWndXamlIsland));SetWindowPos(hWndXamlIsland,nullptr,0,0,LOWORD(lParam),HIWORD(lParam), SWP_SHOWWINDOW);break;    }case WM_DESTROY:        xapp.Close();PostQuitMessage(0);break;default:returnDefWindowProc(hWnd, message, wParam, lParam);    }return0;}

Packaging

As a packaged app

The easiest way to get started is to have your win32 project reference a Windows Runtime Component project (where you've added your XAML pages).Then have a Windows Application Packaging project referencing your win32 app.

As an unpackaged app

Scenarios

  1. App just needs to use system XAML and create UI programmatically. This is the easiest case.Your app can create all its UI in code. Just set up the basic scaffolding, create the XAML objects in code and then set theDesktopWindowXamlSource's content to the top level object.

  2. App uses WinUI 2.x (or other component libraries)

Things you'll need to worry about:

  • Your app must reference the VCRT Forwarders NuGet package
  • If you are using WinUI 2 in an unpackaged app:
    • If your app needs to run on Windows 10, make sure you are using theprerelease package, or seeUsing framework packages.
    • If your app only needs to run on Windows 11, you can use thecppxaml::InitializeWinUI() API.
  • You need an application manifest to mark your app as working on 19h1 since that was the first XAML islands release:
<?xml version="1.0" encoding="utf-8"?><assemblymanifestVersion="1.0"xmlns="urn:schemas-microsoft-com:asm.v1"xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">  <compatibilityxmlns="urn:schemas-microsoft-com:compatibility.v1">     <application><!-- This Id value indicates the application supports Windows 10 functionality-->      <supportedOSId="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />      <maxversiontestedId="10.0.18362.0" /><!-- Enables Segoe UI Variable font on Windows 11-->      <maxversiontestedId="10.0.22000.0" />    </application>  </compatibility>  <assemblyIdentityname="WindowsProject1"type="winb32"version="1.0.0.0" />  <dependency>    <dependentAssembly>      <assemblyIdentitytype="win32"name="Microsoft.Windows.Common-Controls"version="6.0.0.0"processorArchitecture="*"publicKeyToken="6595b64144ccf1df"language="*"        />    </dependentAssembly>  </dependency>    <asmv3:application>    <asmv3:windowsSettingsxmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">      <dpiAwareness>PerMonitorV2</dpiAwareness>    </asmv3:windowsSettings>  </asmv3:application></assembly>
  • Your app needs to know how to activate the different WinRT types (including WinUI controls) that it references. You either need custom build logic (seen below) or you can use theUnpackaged NuGet package to do this for you.
  <TargetName="_UnpackagedWin32MapWinmdsToManifestFiles"DependsOnTargets="ResolveAssemblyReferences">    <ItemGroup><!-- For each non-system .winmd file in References, generate a .manifest in IntDir for it.-->      <_UnpackagedWin32WinmdManifestInclude="@(ReferencePath->'$(IntDir)\%(FileName).manifest')"Condition="'%(ReferencePath.IsSystemReference)' != 'true' and '%(ReferencePath.WinMDFile)' == 'true' and '%(ReferencePath.ReferenceSourceTarget)' == 'ResolveAssemblyReference' and '%(ReferencePath.Implementation)' != ''">        <WinMDPath>%(ReferencePath.FullPath)</WinMDPath>        <Implementation>%(ReferencePath.Implementation)</Implementation>      </_UnpackagedWin32WinmdManifest><!-- For each referenced project that _produces_ a winmd, generate a temporary item that maps to           the winmd, and use that temporary item to generate a .manifest in IntDir for it.           We don't set Implementation here because it's inherited from the _ResolvedNativeProjectReferencePaths.-->      <_UnpackagedWin32WinmdProjectReferenceCondition="'%(_ResolvedNativeProjectReferencePaths.ProjectType)' != 'StaticLibrary'"Include="@(_ResolvedNativeProjectReferencePaths-&gt;WithMetadataValue('FileType','winmd')-&gt;'%(RootDir)%(Directory)%(TargetPath)')" />      <_UnpackagedWin32WinmdManifestInclude="@(_UnpackagedWin32WinmdProjectReference->'$(IntDir)\%(FileName).manifest')">        <WinMDPath>%(Identity)</WinMDPath>      </_UnpackagedWin32WinmdManifest>    </ItemGroup>  </Target>  <TargetName="_UnpackagedWin32GenerateAdditionalWinmdManifests"Inputs="@(_UnpackagedWin32WinmdManifest.WinMDPath)"Outputs="@(_UnpackagedWin32WinmdManifest)"DependsOnTargets="_UnpackagedWin32MapWinmdsToManifestFiles">    <MessageText="Generating manifest for %(_UnpackagedWin32WinmdManifest.WinMDPath)"Importance="High" /><!-- This target is batched and a new Exec is spawned for each entry in _UnpackagedWin32WinmdManifest.-->    <ExecCommand="mt.exe -winmd:%(_UnpackagedWin32WinmdManifest.WinMDPath) -dll:%(_UnpackagedWin32WinmdManifest.Implementation) -out:%(_UnpackagedWin32WinmdManifest.Identity)" />    <ItemGroup><!-- Emit the generated manifest into the Link inputs. Pass a metadata name that isn't used to wipe all          metadata because otherwise VS tries copying the manifest to the output as %(FileName).winmd.-->      <ManifestInclude="@(_UnpackagedWin32WinmdManifest)"KeepMetadata="DoesntExist" />    </ItemGroup>  </Target>
  • Your app will need to include WinUI'sresources.pri.If your app doesn't have its own set of resources (i.e. you don't have any .xaml markup files, nor any other resources), then your app can just rename Microsoft.UI.Xaml.pri to resources.pri and put this file next to your exe.If you are using the Unpackaged NuGet package (see above), you can set the MSBuild property<HasOwnPriFiles>false</HasOwnPriFiles> to automate this copy.

If your app does include its own resources (e.g. you have a Runtime Component project that includes your markup), then either:

  • the Windows Application Packaging project will take care of merging your app's PRI and WinUI's PRI, or
  • you need to merge the PRIs

Merging PRIs

To merge the PRIs:

  1. Create a filepri.resfiles in your project, and list the set of PRI files your project depends on:
C:\Users\asklar\source\repos\xaml-islands\AppMarkup\debug\AppMarkup\AppMarkup.priC:\Users\asklar\source\repos\xaml-islands\win32\packages\Microsoft.UI.Xaml.2.8.0-prerelease.210927001\runtimes\win10-x86\native\Microsoft.UI.Xaml.pri
  1. Create a filepriconfig.xml in your project that referencespri.resfiles:
<?xml version="1.0" encoding="utf-8"?><resourcestargetOsVersion="10.0.0"majorVersion="1">  <indexroot="\"startIndexAt="pri.resfiles">    <default>      <qualifiername="Language"value="en-US" />      <qualifiername="Contrast"value="standard" />      <qualifiername="Scale"value="200" />      <qualifiername="HomeRegion"value="001" />      <qualifiername="TargetSize"value="256" />      <qualifiername="LayoutDirection"value="LTR" />      <qualifiername="DXFeatureLevel"value="DX9" />      <qualifiername="Configuration"value="" />      <qualifiername="AlternateForm"value="" />      <qualifiername="Platform"value="UAP" />    </default>    <indexer-configtype="PRI" />    <indexer-configtype="RESFILES"qualifierDelimiter="." />  </index></resources>
  1. You can then create the mergedresources.pri by running:
makepri new /pr . /cf .\priconfig.xml /of .\debug\resources.pri /o

Using Framework packages

Apps that use WinUI stable releases don't actually ship the WinUI 2 bits in their package, instead they declare a dependency and Windows will download the right framework package - which is shared with other apps installed on the system.

However unpackaged apps need a way to discover these framework packages. This is possible via using the Dynamic Dependencies API to add the WinUI package to your package graph (new in Windows 11, and also available separate from the Windows SDK as part of the Windows App SDK). Your app installer may also need to do more work to register the dependency.

If your app only needs to run on Windows 11, you can use this API just before creating any WinUI objects:

cppxaml::InitializeWinUI();// this takes an optional value corresponding to the minor version in 2.x, defaults to 8.

Alternatively, you can referenceprerelease NuGet packages of WinUI, which means you'll carry the WinUI 2 bits in your app, but also means you don't have to worry about framework packages and it works on Windows 10 too.

Accessing Win32 APIs from the Runtime Component

Since the runtime component will be desktop-only, it is okay for it to call non-UWP APIs.

Add these props to the WRC project (thanks to @sylveon for this tip):

  <PropertyGroup>    <_NoWinAPIFamilyApp>true</_NoWinAPIFamilyApp>    <_VC_Target_Library_Platform>Desktop</_VC_Target_Library_Platform>    <DesktopCompatible>true</DesktopCompatible>  </PropertyGroup>

You will also need to make sure you#include the right headers, and equally importantly, you'll need to add the right .lib. You can do this in the VS UI, or in your WRC vcxproj:

  <ItemDefinitionGroup>    <Link>      <AdditionalDependencies>Advapi32.lib;shell32.lib;%(AdditionalDependencies)</AdditionalDependencies>    </Link>  </ItemDefinitionGroup>

About

XAML Islands samples, and home of CppXAML - C++ helpers for XAML

Topics

Resources

License

MIT, MIT licenses found

Licenses found

MIT
LICENSE
MIT
LICENSE.txt

Stars

Watchers

Forks

Packages

No packages published

[8]ページ先頭

©2009-2025 Movatter.jp