A vágólap mintájának portolása C++/WinRT-be C#-ból – esettanulmány

Ez a témakör az egyik Univerzális Windows-platform (UWP) alkalmazásmintaC# és C++/WinRT közötti portolásának esettanulmányát mutatja be. Portolási gyakorlatra és tapasztalatra tehet szert, ha követi az útmutatót, és közben saját maga is elvégzi a minta portolását.

Note

A portolt forráskód egy UWP C#-alkalmazás. A cikkben szereplő célként szolgáló C++/WinRT-kódot WinUI 3-hoz (Windows App SDK) írták. Ahol az UWP-forrás a WinUI 3-ban eltérő API-kat használ (például Windows.UI.Core.CoreDispatcher vs. Microsoft.UI.Dispatching.DispatcherQueue), ez a cikk kifejezetten megjeleníti a megfelelő WinUI 3-as megfelelőt a C++/WinRT kimenetben. A C++/WinRT oszlop kódmintáit közvetlenül a WinUI 3 alkalmazásban használhatja.

A C++/WinRT-ről C#-ról történő portolással kapcsolatos technikai részletek átfogó katalógusát az Áthelyezés c++/WinRT-ről C#-ról című társtémakörben találja.

Rövid előszó a C# és c++ forráskódfájlokról

Egy C#-projektben a forráskódfájlok elsősorban .cs fájlok. A C++-ra való áttéréskor látni fogja, hogy több forráskódfájlt is meg kell szoknia. Ennek oka a fordítók közötti különbség, a C++ forráskód újrafelhasználásának módja, valamint a típus és a függvények (metódusok) deklarálásának és definiálásának fogalma.

A függvénydeklaráció csak a függvény aláírását írja le (visszatérési típusát, nevét, paramétertípusait és neveit). A függvénydefiníció tartalmazza a függvény törzsét (implementációját).

A típusokat illetően ez egy kicsit más. Egy típust úgy definiálhat , hogy megadja a nevét, és (legalább) csak az összes tagfüggvényét (és más tagját) deklarálja . Így van, akkor is definiálhat típust, ha nem határozza meg a tagfüggvényeket.

  • A gyakori C++-forráskódfájlok a .h (pont h) és .cpp fájlok. A .h fájl egy fejlécfájl , amely egy vagy több típust határoz meg. Bár tagfüggvényeket definiálhat egy fejlécben, általában erre szolgál a .cpp fájl. Tehát egy hipotetikus C++ típusú MyClass típus esetében a MyClassot definiálná, és a tagfüggvényeket a következőben MyClass.hMyClass.cppdefiniálná. Ha a többi fejlesztő újra szeretné használni az osztályait, csak a fájlokat és az .h objektumkódot osztaná meg. Titokban tartaná a .cpp fájljait, mivel a megvalósítás az Ön szellemi tulajdonát képezi.
  • Előre összeállított fejléc (pch.h). Általában vannak olyan fejlécfájlok, amelyeket belefoglal az alkalmazásba, és ezeket a fájlokat gyakran nem módosítja. Így ahelyett, hogy minden fordításkor feldolgozta a fejléckészlet tartalmát, összesítheti ezeket a fejléceket egy fájlba, amelyet egyszer fordíthat le, majd minden létrehozáskor használhatja az előfordítási lépés kimenetét. Ezt egy előre összeállított fejlécfájlon keresztül teheti meg (általában elnevezve pch.h).
  • .idl fájlok. Ezek a fájlok interfészdefiníciós nyelvet (IDL) tartalmaznak. Az IDL-t Windows-futtatókörnyezet típusú fejlécfájlokként is tekintheti. Az IDL-ről bővebben a MainPage típus IDL szakaszában fogunk beszélni.

A vágólap minta letöltése és tesztelése

Látogasson el a Vágólap mintapélda weboldalára, és kattintson a ZIP letöltése gombra. Bontsa ki a letöltött fájlt, és tekintse meg a mappastruktúrát.

  • A minta forráskód C#-verziója a nevű csmappában található.
  • A minta forráskód C++/WinRT-verziója a névvel ellátott cppwinrtmappában található.
  • A C#- és a C++/WinRT-verzió által egyaránt használt egyéb fájlok a shared és a SharedContent mappákban találhatók meg.

A jelen témakör útmutatója bemutatja, hogyan hozhatja létre újra a vágólapminta C++/WinRT-verzióját a C#-forráskódból való portolással. Így láthatja, hogyan portozhatja saját C#-projektjeit a C++/WinRT fájlba.

Ha meg szeretné tekinteni a minta használatát, nyissa meg a C#-megoldást (\Clipboard_sample\cs\Clipboard.sln), módosítsa a konfigurációt a megfelelő módon (esetleg x64-re), buildelje és futtassa. A minta saját felhasználói felülete (UI) lépésről lépésre végigvezeti a különböző funkciókon.

Tipp

A letöltött minta gyökérmappájának neve Clipboard lehet a(z) Clipboard_sample helyett. De továbbra is Clipboard_sample néven fogunk hivatkozni arra a mappára, hogy megkülönböztessük attól a C++/WinRT-verziótól, amelyet egy későbbi lépésben hozunk majd létre.

Üres alkalmazás létrehozása vágólap néven

Note

A C++/WinRT Visual Studio bővítmény (VSIX) és a NuGet-csomag telepítésével és használatával kapcsolatos információkért (amelyek együttesen nyújtanak projektsablont és buildtámogatást) lásd Visual Studio C++/WinRT támogatását.

Kezdje el a portálási folyamatot egy új C++/WinRT-projekt létrehozásával Microsoft Visual Studio. Hozzon létre egy új projektet a C++ projektsablonhoz készült Blank App, Packaged (WinUI 3 in Desktop) használatával. Állítsa a nevét erre: Clipboard, és (hogy a mappaszerkezet megegyezzen az útmutatóban szereplővel) győződjön meg arról, hogy az A megoldás és a projekt ugyanabba a könyvtárba kerüljön jelölőnégyzet nincs bejelölve.

Kiindulási alapként győződjön meg arról, hogy ez az új, üres projekt lefordul és fut.

Package.appxmanifest és eszközfájlok

Ha a minta C# és C++/WinRT verzióit nem kell egymás mellett telepíteni ugyanazon a gépen, akkor a két projekt alkalmazáscsomagjának jegyzékfájljai (Package.appxmanifest) azonosak lehetnek. Ebben az esetben egyszerűen átmásolhatja Package.appxmanifest a C#-projektből a C++/WinRT projektbe, és elkészült.

Ahhoz, hogy a minta két verziója együtt létezzon, különböző azonosítókra van szükségük. Ebben az esetben a C++/WinRT projektben nyissa meg a Package.appxmanifest fájlt egy XML-szerkesztőben, és jegyezze fel ezt a három értéket.

  • A /Package/Identity elemen belül jegyezze fel a Név attribútum értékét. Ez a csomag neve. Egy újonnan létrehozott projekt esetében a projekt egy egyedi GUID kezdeti értékét adja meg.
  • A /Package/Applications/Application elemen belül jegyezze fel az Id attribútum értékét. Ez az alkalmazás azonosítója.
  • A /Package/mp:PhoneIdentity elemen belül jegyezze fel a PhoneProductId attribútum értékét. Egy újonnan létrehozott projekt esetében ez ugyanazzal a GUID-azonosítóval lesz beállítva, mint a csomag neve.

Ezután másolja Package.appxmanifest a C#-projektből a C++/WinRT projektbe. Végül visszaállíthatja a három feljegyzett értéket. Vagy szerkesztheti a másolt értékeket, hogy egyediek és/vagy megfelelőek legyenek az alkalmazás és a szervezet számára (ahogy általában egy új projekt esetében tenné). Például ebben az esetben a csomagnév értékének visszaállítása helyett egyszerűen módosíthatjuk a másolt értéket Microsoft.SDKSamples.Clipboard.CS-ről Microsoft.SDKSamples.Clipboard.CppWinRT-re. Az alkalmazásazonosítót pedig az App értékre állíthatjuk. Mindaddig, amíg a csomag neve vagy az alkalmazás azonosítója eltérő, a két alkalmazás különböző alkalmazásfelhasználói modellazonosítókkal (AUMID-kkel) rendelkezik. És ez szükséges ahhoz, hogy két alkalmazás egymás mellett legyen telepítve ugyanazon a gépen.

Ebben az útmutatóban érdemes még néhány további módosítást végrehajtani itt: Package.appxmanifest. A(z) Clipboard C# Sample karakterlánc háromszor fordul elő. Módosítsa ezt erre: Vágólap C++/WinRT-minta.

A C++/WinRT projektben a Package.appxmanifest fájl és a projekt nincs szinkronizálva az általuk hivatkozott eszközfájlok tekintetében. Ennek orvoslásához először távolítsa el az objektumokat a C++/WinRT projektből a mappában lévő Assets összes fájl kiválasztásával (a Visual Studio Megoldáskezelő), majd távolítsa el őket (válassza a Törlés lehetőséget a párbeszédpanelen).

A C# projekt megosztott mappából származó objektumfájlokra hivatkozik. Ugyanezt megteheti a C++/WinRT projektben is, vagy átmásolhatja a fájlokat az útmutatóban leírtak szerint.

Lépjen a \Clipboard_sample\SharedContent\media mappába. Jelölje ki azt a hét fájlt, amelyet a C# projekt tartalmaz (microsoft-sdk.png, , smalltile-sdk.png, splash-sdk.pngsquaretile-sdk.png, , storelogo-sdk.pngés tile-sdk.png), másolja őket, és windows-sdk.pngillessze be őket az \Clipboard\Clipboard\Assets új projekt mappájába.

Kattintson a jobb gombbal a Assets mappára (a C++/WinRT-projekt Megoldáskezelőjében), majd válassza a >Hozzáadás>Meglévő elem... lehetőséget, és keresse meg a következőt: \Clipboard\Clipboard\Assets. A fájlválasztóban jelölje ki a hét fájlt, és kattintson a Hozzáadás gombra.

Package.appxmanifest most már szinkronizálva van a projekt eszközfájljaival.

MainPage, beleértve a mintát konfiguráló funkciókat

A vágólap mintája – az összes Univerzális Windows-platform (UWP)-alkalmazásmintához hasonlóan – olyan forgatókönyvek gyűjteményéből áll, amelyeken a felhasználó egyenként haladhat végig. Az adott minta forgatókönyveinek gyűjteménye a minta forráskódjában van konfigurálva. A gyűjtemény minden forgatókönyve egy olyan adatelem, amely egy címet, valamint a forgatókönyvet megvalósító projekt osztályának típusát tárolja.

A minta C#-verziójában, ha megnézi a SampleConfiguration.cs forráskódfájlt, két osztályt fog látni. A konfigurációs logika nagy része a MainPage osztályban található, amely egy részleges osztály (teljes osztályt alkot a korrektúra MainPage.xaml és az imperatív kód MainPage.xaml.csegyüttes használatakor). A forráskódfájl másik osztálya a Forgatókönyv, amelynek cím- és ClassType-tulajdonságai vannak.

A következő néhány alszakaszban a MainPage és a Scenario portját tekintjük át.

A MainPage-típus IDL-azonosítója

Kezdjük ezt a szakaszt úgy, hogy röviden beszélünk az interfészdefiníciós nyelvről (IDL), és arról, hogyan segít nekünk, ha a C++/WinRT-vel programozunk. Az IDL egy olyan forráskód, amely egy Windows-futtatókörnyezet típus hívható felületét írja le. Egy típus hívható (vagy nyilvános) felülete ki van vetítve a világra, hogy a típus felhasználható legyen. A típus előre jelzett része ellentétben áll a típus tényleges belső implementációjával, amely természetesen nem hívható, és nem nyilvános. Csak a kivetített részt definiáljuk az IDL-ben.

Miután létrehozott egy (fájlon .idl belüli) IDL-forráskódot, az IDL-t géppel olvasható metaadatfájlokká (más néven Windows metaadatokká) fordíthatja. Ezek a metaadatfájlok rendelkeznek a bővítménysel .winmd, és íme néhány használatuk.

  • Egy .winmd összetevő Windows-futtatókörnyezet típusait ismertetheti. Amikor egy alkalmazásprojektből hivatkozik egy Windows-futtatókörnyezet összetevőre (WRC), az alkalmazásprojekt beolvassa a WRC-hez tartozó Windows metaadatokat (a metaadatok egy külön fájlban lehetnek, vagy ugyanabba a fájlba lehetnek csomagolva, mint maga a WRC), hogy a WRC típusait az alkalmazásból tudja felhasználni.
  • Az A .winmd az alkalmazás egy részének Windows-futtatókörnyezet típusait írja le, hogy ugyanazon alkalmazás egy másik része is felhasználhassa őket. Például egy Windows-futtatókörnyezet típus, amelyet egy XAML-oldal használ ugyanabban az alkalmazásban.
  • Annak megkönnyítésére, hogy használni tudja a Windows-futtatókörnyezet-típusokat (beépített vagy külső féltől származó típusokat), a C++/WinRT buildrendszere a(z) .winmd fájlokat használja olyan burkoló típusok létrehozására, amelyek a Windows-futtatókörnyezet-típusok leképezett részeit reprezentálják.
  • Annak megkönnyítésére, hogy saját Windows-futtatókörnyezet-típusokat implementálhasson, a C++/WinRT buildrendszer az IDL-ből egy .winmd fájlt hoz létre, majd ezt használja fel arra, hogy wrapperosztályokat generáljon a projekciójához, valamint olyan csonkokat, amelyekre az implementációját alapozhatja (ezekről a csonkokról a témakör későbbi részében még bővebben is szó lesz).

A C++/WinRT-lel használt IDL adott verziója Microsoft Interface Definition Language 3.0. A témakör további részében részletesen megvizsgáljuk a C# MainPage típusát. Eldöntjük, hogy mely részeinek kell lennie a C++/WinRT MainPage típusú (azaz hívható vagy nyilvános) felület kivetítésében, és amelyek csak a megvalósítás részei lehetnek. Ez a megkülönböztetés azért fontos, mert amikor az IDL-t készítjük (amelyet a következő szakaszban fogunk elvégezni), csak a hívható részeket fogjuk meghatározni.

A MainPage típust közösen megvalósító C#-forráskódfájlok a következők: MainPage.xaml (amelyeket hamarosan portolunk, másolással), MainPage.xaml.csés SampleConfiguration.cs.

A C++/WinRT-verzióban a MainPage típust hasonló módon forráskódfájlokra fogjuk bontani. Átvesszük a MainPage.xaml.cs logikáját, és azt nagyrészt átültetjük a MainPage.h-be és a MainPage.cpp-be. A SampleConfiguration.cs-ban lévő logikát pedig SampleConfiguration.h-re és SampleConfiguration.cpp-re fordítjuk le.

A C# Univerzális Windows-platform (UWP)-alkalmazások osztályai természetesen Windows-futtatókörnyezet típusúak. Ha azonban egy C++/WinRT-alkalmazásban ír egy típust, megadhatja, hogy ez a típus Windows-futtatókörnyezet típus-e, vagy egy normál C++ osztály/struct/enumerálás.

A projekt minden XAML-oldalának Windows-futtatókörnyezet típusúnak kell lennie, ezért a MainPage-nek Windows-futtatókörnyezet típusúnak kell lennie. A C++/WinRT projektben a MainPage már Windows-futtatókörnyezet típus, ezért nem kell ezen a szemponton változtatni. Konkrétan egy futásidejű osztály.

  • Ha többet szeretne megtudni arról, hogy érdemes-e futtatókörnyezeti osztályt létrehoznia egy adott típushoz, olvassa el a C++/WinRT-lel rendelkező API-k készítése című témakört.
  • A C++/WinRT-ben egy futtatókörnyezeti osztály belső implementálása és annak előre jelzett (nyilvános) részei két különböző osztály formájában léteznek. Ezeket implementálási típusnak és tervezett típusnak nevezzük. Ezekről az előző felsoroláspontban említett témakörben, valamint a C++/WinRT api-k használata című témakörben tudhat meg többet.
  • A futtatókörnyezeti osztályok és az IDL- (.idl) fájlok közötti kapcsolatról további információért olvassa el, és kövesse a következő témakört: XAML-vezérlők; kötés egy C++/WinRT-tulajdonsághoz. Ez a témakör végigvezet egy új futtatókörnyezeti osztály létrehozásának folyamatán, amelynek első lépése egy új Midl-fájl (.idl) elem hozzáadása a projekthez.

A MainPage esetében már rendelkezünk a szükséges MainPage.idl fájllal a C++/WinRT projektben. Ennek az az oka, hogy a projektsablon létrehozta számunkra. Az útmutató későbbi részében azonban további .idl fájlokat adunk hozzá a projekthez.

Hamarosan látni fogunk egy felsorolást arról, hogy pontosan milyen IDL-t kell hozzáadnunk a meglévő MainPage.idl fájlhoz. Előtte át kell gondolnunk, hogy minek kell, és minek nem kell bekerülnie az IDL-be.

Annak megállapításához, hogy a MainPage mely tagjait MainPage.idl kell deklarálni (hogy azok a MainPage futtatókörnyezeti osztály részévé váljanak), és amelyek egyszerűen a MainPage implementálási típusának tagjai lehetnek, készítsük el a C# MainPage osztály tagjainak listáját. Ezeket a tagokat úgy találjuk meg, hogy a MainPage.xaml.cs és a SampleConfiguration.cs elemben keressük őket.

Összesen 12 protected mezőt és private metódust találunk. És megtaláljuk a következő public tagokat.

  • Az alapértelmezett konstruktor MainPage().
  • Az Aktuális és FEATURE_NAME statikus mezők.
  • Az IsClipboardContentChangedEnabled és a Scenarios tulajdonságok.
  • A BuildClipboardFormatsOutputString, DisplayToast, EnableClipboardContentChangedNotifications és NotifyUser metódusok.

Azok a public tagok deklarálhatók a(z) MainPage.idl elemben. Ezért vizsgáljuk meg mindegyiket, és nézzük meg, hogy a MainPage futtatókörnyezeti osztályának kell-e lenniük, vagy csak a megvalósítás részeként kell lenniük.

  • Az alapértelmezett konstruktor MainPage(). Egy XAML Page esetében szokásos egy alapértelmezett konstruktort deklarálni az IDL-jében. Így az XAML felhasználói felületi keretrendszer aktiválhatja a típust.
  • A Current statikus mezőt az egyes forgatókönyvek XAML-oldalain belül az alkalmazás MainPage példányának eléréséhez használják. Mivel a Current nem használható az XAML-keretrendszerrel való együttműködésre (és fordítási egységekben sem), fenntarthatjuk, hogy kizárólag a megvalósítási típus tagja legyen. Az ilyen esetekben a saját projektjeivel ezt is megteheti. Mivel azonban a mező a kivetített típus egy példánya, logikusnak tűnik deklarálni az IDL-ben. Tehát ezt fogjuk tenni itt (és így is teszi a kódot kissé tisztább).
  • Hasonló eset a statikus FEATURE_NAME mezőhöz, amely a MainPage típuson belül érhető el. Ismét, ha úgy dönt, hogy deklarálja azt az IDL-ben, a kód kissé tisztább lesz.
  • Az IsClipboardContentChangedEnabled tulajdonság csak az OtherScenarios osztályban használható. Így a port alatt egy kicsit leegyszerűsítjük a dolgokat, és az OtherScenarios futtatókörnyezeti osztály privát mezőjévé tesszük. Hogy az ne kerüljön be az IDL-be.
  • A Scenarios tulajdonság Scenario típusú objektumok gyűjteménye (egy korábban említett típus). A forgatókönyvről a következő alszakaszban fogunk beszélni, ezért addig is hagyjuk a Scenarios tulajdonságot.
  • A BuildClipboardFormatsOutputString, a DisplayToast és az EnableClipboardContentChangedNotifications metódusok olyan segédprogramfüggvények, amelyek jobban kapcsolódnak a minta általános állapotához, mint a főoldalhoz. A port során tehát ezt a három metódust egy SampleState nevű új segédprogramtípusra helyezzük át (amelynek nem kell Windows-futtatókörnyezet típusnak lennie). Ezért ez a három módszer nem fog az IDL-ben menni.
  • A NotifyUser metódus meghívása az egyes forgatókönyv XAML-lapjairól történik a MainPage statikus Aktuális mezőből visszaadott példányán. Mivel (amint már említettük) a Current a leképezett típus egy példánya, deklarálnunk kell a NotifyUser elemet az IDL-ben. A NotifyUser egy NotifyType típusú paramétert vesz fel. Erről a következő alszakaszban fogunk beszélni.

Minden olyan tagot, amelyet adatkötéshez szeretne használni, az IDL-ben is deklarálni kell (akár a {x:Bind}, akár a {Binding} elemet használja). További információ: Adatkötés.

Előrehaladást értünk el: kidolgozunk egy listát, amely tartalmazza, hogy mely tagokat kell hozzáadni a MainPage.idl fájlhoz, és melyeket nem. De továbbra is meg kell beszélnünk a Scenarios tulajdonságot és az NotifyType típust . Csináljuk ezt most.

A Forgatókönyv- és NotifyType-típusok IDL-azonosítója

A Forgatókönyv osztály a következőben van definiálva SampleConfiguration.cs: . Döntésünk van az osztály C++/WinRT-be történő portjáról. Alapértelmezés szerint valószínűleg normál C++ structlesz. Ha azonban a forgatókönyvet bináris fájlokon keresztül használják, vagy az XAML-keretrendszerrel együttműködnek, akkor Windows-futtatókörnyezet típusú idL-ben kell deklarálni.

A C#-forráskód tanulmányozásával azt találjuk, hogy a forgatókönyv ebben a kontextusban van használatban.

<ListBox x:Name="ScenarioControl" ... >
var itemCollection = new List<Scenario>();
int i = 1;
foreach (Scenario s in scenarios)
{
    itemCollection.Add(new Scenario { Title = $"{i++}) {s.Title}", ClassType = s.ClassType });
}
ScenarioControl.ItemsSource = itemCollection;

Forgatókönyvobjektumok gyűjteménye van hozzárendelve egy ListBoxItemsSource tulajdonságához (amely egy elemvezérlő). Mivel a forgatókönyvnek együttműködnie kell az XAML-vel, Windows-futtatókörnyezet típusúnak kell lennie. Ezért az IDL-ben kell definiálni. A forgatókönyv típusának idL-ben való definiálása miatt a C++/WinRT buildrendszer létrehoz egy forráskóddefiníciót a forgatókönyvről egy háttérbeli fejlécfájlban (amelynek neve és helye nem fontos ehhez az útmutatóhoz).

És emlékezni fog rá, hogy a MainPage.ScenariosScenario objektumok gyűjteménye, amelyről az imént azt mondtuk, hogy IDL-ben kell lennie. Ezért magát a MainPage.Scenarios-t is deklarálni kell az IDL-ben.

Az NotifyType a enum C# MainPage.xaml.cs-ban deklarált. Mivel a NotifyType-et a MainPage futtatókörnyezeti osztályhoz tartozó metódusnak adjuk át, az NotifyType-nak is Windows-futtatókörnyezet típusúnak kell lennie, és a paraméterben MainPage.idlkell definiálni.

Most vegyük fel a fájlba azokat az MainPage.idl új típusokat és a Mainpage új tagját, amelyeket úgy döntöttünk, hogy IDL-ben deklarálunk. Ugyanakkor eltávolítjuk az IDL-ből a Mainpage helyőrző tagjait, amelyeket a Visual Studio projektsablon adott nekünk.

A C++/WinRT projektben tehát nyissa meg MainPage.idlés szerkessze, hogy az az alábbi listaelemhez hasonlóan nézzen ki. Vegye figyelembe, hogy az egyik módosítás az, hogy a névtér nevét vágólaprólSDKTemplate-ra módosítja. Ha szeretné, egyszerűen cserélje le a teljes tartalmat MainPage.idl a következő kódra. Egy másik megjegyzendő módosítás, hogy a Scenario::ClassType nevét Scenario::ClassName névre változtatjuk.

// MainPage.idl
namespace SDKTemplate
{
    struct Scenario
    {
        String Title;
        Microsoft.UI.Xaml.Interop.TypeName ClassName;
    };

    enum NotifyType
    {
        StatusMessage,
        ErrorMessage
    };

    [default_interface]
    runtimeclass MainPage : Microsoft.UI.Xaml.Controls.Page
    {
        MainPage();

        static MainPage Current{ get; };
        static String FEATURE_NAME{ get; };

        static Windows.Foundation.Collections.IVector<Scenario> scenarios{ get; };

        void NotifyUser(String strMessage, NotifyType type);
    };
}

Note

A C++/WinRT-projektben lévő fájlok tartalmáról .idl további információt Microsoft Interface Definition Language 3.0 című témakörben talál.

Előfordulhat, hogy a saját portolási munkája során nem szeretné vagy nem kell módosítania a névtér nevét, ahogyan mi fent tettük. Ezt csak azért tesszük itt, mert az általunk portolt C#-projekt alapértelmezett névtere az SDKTemplate, a projekt és az assembly neve pedig Clipboard.

Azonban, ahogy ebben az útmutatóban továbblépünk a portolással, a forráskódban a Clipboard névtérnév minden előfordulását SDKTemplate-re módosítjuk. A C++/WinRT projekttulajdonságokban található egy hely is, ahol megjelenik a vágólap névterének neve, ezért most megragadjuk a lehetőséget a módosításra.

A Visual Studióban a C++/WinRT-projektnél állítsa a Common Properties>C++/WinRT>Gyökérnévtér projekt-tulajdonság értékét SDKTemplate értékre.

Mentse az IDL-t, és hozza létre újra a csonkfájlokat

A témakör XAML-vezérlői; A C++/WinRT tulajdonsághoz való kötés bemutatja a csonkfájlok fogalmát, és működés közben mutatja be azokat. Korábban említettük a csonkokat is, amikor megemlítettük, hogy a C++/WinRT buildrendszer a fájlok tartalmát .idl Windows Metaadatokká alakítja, majd ebből a metaadatokból egy nevesített cppwinrt.exe eszköz csonkokat hoz létre, amelyekre alapozhatja a megvalósítást.

Valahányszor valamit hozzáadnak, eltávolítanak vagy módosítanak az IDL-ben, majd buildelnek, a fordítási rendszer frissíti a stubimplementációkat azokban a stubfájlokban. Ezért minden alkalommal, amikor módosítja az IDL-t és a buildet, javasoljuk, hogy tekintse meg ezeket a csonkfájlokat, másolja ki a módosított aláírásokat, és illessze be őket a projektbe. További részleteket és példákat adunk arra, hogyan kell ezt egy pillanat alatt megtenni. Ennek azonban az az előnye, hogy hibamentes módot ad arra, hogy mindig tudja, milyen legyen a megvalósítási típus alakja, és hogy milyen legyen a metódusok aláírása.

Az útmutató ezen szakaszában egyelőre befejeztük a MainPage.idl fájl szerkesztését, ezért érdemes most menteni. A projekt jelenleg nem fog elkészülni, de a build most történő végrehajtása hasznos dolog, mert újragenerálja a MainPage csonkfájljait. Ezért most hozza létre a projektet, és hagyja figyelmen kívül a buildelési hibákat.

Ebben a C++/WinRT-projektben a rendszer létrehozza a csonkfájlokat a \Clipboard\Clipboard\Generated Files\sources mappában. Ott találja majd meg őket, miután a részleges build befejeződött (ismét csak, ahogy várható volt, a build nem fog teljes egészében sikerülni. De az a lépés, amely minket érdekel — a stubok generálása — sikeresen le fog futni). A minket érdeklő fájlok a MainPage.h és MainPage.cpp.

Ebben a két csonkfájlban a MainPage tagjainak új stub implementációi láthatók, amelyeket hozzáadtunk az IDL-hez (például aktuális és FEATURE_NAME). Ezeket a csonkimplementációkat át kell másolnia a projektben már meglévő MainPage.h és MainPage.cpp fájlokba. Ugyanakkor, ahogy az IDL-vel is, eltávolítjuk a meglévő fájlokból a Mainpage helyőrző tagjait, amelyeket a Visual Studio projektsablon adott nekünk (a MyProperty nevű dummy tulajdonság és a ClickHandler nevű eseménykezelő).

Valójában a MainPage jelenlegi verziójának egyetlen olyan tagja, amelyet meg szeretnénk tartani, a konstruktor.

Miután kimásolta az új tagokat a csonkfájlokból, törölte a nem kívánt tagokat, és frissítette a névteret, és a MainPage.hMainPage.cpp projektben lévő fájloknak az alábbi kódlistához hasonlóan kell kinézniük. Figyelje meg, hogy két MainPage-típus létezik. Egy a megvalósítási névtérben, egy másik a factory_implementation névtérben. Az egyetlen módosítás, amit a factory_implementation esetében végeztünk, az volt, hogy hozzáadtuk az SDKTemplate-et a névteréhez.

// MainPage.h
#pragma once
#include "MainPage.g.h"

namespace winrt::SDKTemplate::implementation
{
    struct MainPage : MainPageT<MainPage>
    {
        MainPage();

        static SDKTemplate::MainPage Current();
        static hstring FEATURE_NAME();
        static Windows::Foundation::Collections::IVector<SDKTemplate::Scenario> scenarios();
        void NotifyUser(hstring const& strMessage, SDKTemplate::NotifyType const& type);
    };
}
namespace winrt::SDKTemplate::factory_implementation
{
    struct MainPage : MainPageT<MainPage, implementation::MainPage>
    {
    };
}
// MainPage.cpp
#include "pch.h"
#include "MainPage.h"
#include "MainPage.g.cpp"

namespace winrt::SDKTemplate::implementation
{
    MainPage::MainPage()
    {
        InitializeComponent();
    }
    SDKTemplate::MainPage MainPage::Current()
    {
        throw hresult_not_implemented();
    }
    hstring MainPage::FEATURE_NAME()
    {
        throw hresult_not_implemented();
    }
    Windows::Foundation::Collections::IVector<SDKTemplate::Scenario> MainPage::scenarios()
    {
        throw hresult_not_implemented();
    }
    void MainPage::NotifyUser(hstring const& strMessage, SDKTemplate::NotifyType const& type)
    {
        throw hresult_not_implemented();
    }
}

Sztringek esetén a C# a System.Stringet használja. Egy példát a MainPage.NotifyUser metódusban talál. Az IDL-ben egy karakterláncot a String típussal deklarálunk, és amikor az cppwinrt.exe eszköz C++/WinRT-kódot generál nekünk, a winrt::hstring típust használja. Valahányszor egy C#-kódban található sztringgel találkozunk, azt winrt::hstring típusra ültetjük át. További információ: Sztringkezelés a C++/WinRT-ben.

A metódusaláírásokban szereplő const& paraméterek magyarázatát lásd: Paraméterátadás.

Az összes fennmaradó névtér-deklaráció/hivatkozás frissítése és buildelés

A C++/WinRT projekt létrehozása előtt keresse meg a vágólap névterének deklarációit (és hivatkozásait), és módosítsa őket SDKTemplate-ra.

  • MainPage.xaml és App.xaml. A névtér megjelenik az és az x:Classxmlns:local attribútumok értékeiben.
  • App.idl.
  • App.h.
  • App.cpp. Két using namespace direktíva van (keresse meg a(z) using namespace Clipboard részkarakterláncot), valamint a MainPage típusnak két minősített előfordulása is van (keressen rá erre: Clipboard::MainPage). Ezeknek változnia kell.

Mivel eltávolítottuk az eseménykezelőt a MainPage-ből, lépjen be MainPage.xaml és törölje a Gomb elemet a korrektúrából.

Mentse az összes fájlt. Tisztítsa meg a megoldást (Build>Clean Solution), majd hozza létre. Miután követte az összes eddigi módosítást, pontosan az írott módon, a build várhatóan sikeres lesz.

Az IDL-ben deklarált MainPage-tagok implementálása

A konstruktor, Current és FEATURE_NAME

Íme a megfelelő kód (a C#-projektből), amelyet ki kell portolni.

<!-- MainPage.xaml -->
...
<TextBlock x:Name="SampleTitle" ... />
...
// MainPage.xaml.cs
...
public sealed partial class MainPage : Page
{
    public static MainPage Current;

    public MainPage()
    {
        InitializeComponent();
        Current = this;
        SampleTitle.Text = FEATURE_NAME;
    }
...
}
...

// SampleConfiguration.cs
...
public partial class MainPage : Page
{
    public const string FEATURE_NAME = "Clipboard C# sample";
...
}
...

Hamarosan teljes egészében újra fogjuk használni MainPage.xaml (másolással). Egyelőre (alább) ideiglenesen hozzáadunk egy TextBlock-elemet a megfelelő névvel a MainPage.xaml C++/WinRT projekthez.

A FEATURE_NAME a MainPage statikus mezője (egy C#-constmező viselkedését tekintve lényegében statikus), amely a SampleConfiguration.cs fájlban van definiálva. C++/WinRT esetén a (statikus) mező helyett ennek C++/WinRT-es megfelelőjeként egy (statikus) csak olvasható tulajdonságot használunk. A tulajdonság getter kifejezésének C++/WinRT módja olyan függvény, amely visszaadja a tulajdonság értékét, és nem vesz fel paramétereket (tartozékot). A C# FEATURE_NAME statikus mező tehát a C++/WinRT FEATURE_NAME statikus kiegészítő függvény lesz (ebben az esetben a sztring literálját adja vissza).

Egyébként ugyanezt tennénk, ha egy C# csak olvasható tulajdonságot portolnánk. Egy írható C#-tulajdonság esetén a tulajdonság setterének C++/WinRT szerinti kifejezése egy void függvény, amely paraméterként a tulajdonság értékét veszi át (módosító). Mindkét esetben, ha a C#-mező vagy -tulajdonság statikus, akkor a C++/WinRT-beli elérője és/vagy beállítója is az.

Current a(z) MainPage statikus (nem konstans) mezője. Itt is csak olvasható tulajdonsággá tesszük (a C++/WinRT-beli megfelelőjét), és ezt is statikussá tesszük. Ahol FEATURE_NAME állandó, az Aktuális nem. A C++/WinRT-ben tehát szükségünk lesz egy háttérmezőre, és a kiegészítőnk ezt adja vissza. A C++/WinRT-projektben tehát a MainPage.h helyen deklarálunk egy current nevű privát statikus mezőt, a MainPage.cpp helyen definiáljuk/inicializáljuk a current értékét (mivel statikus tárolási időtartamú), és egy Current nevű nyilvános statikus lekérdezőfüggvényen keresztül érjük el.

Maga a konstruktor több feladatot is végrehajt, amelyek könnyen portozhatóak.

A C++/WinRT projektben adjon hozzá egy új Visual C++>Code>C++ Fájl (.cpp) elemet a névvel SampleConfiguration.cpp.

Szerkessze a(z) MainPage.xaml, MainPage.h, MainPage.cpp és SampleConfiguration.cpp elemet az alábbi listának megfelelően.

<!-- MainPage.xaml -->
...
<StackPanel ...>
    <TextBlock x:Name="SampleTitle" />
</StackPanel>
...
// MainPage.h
...
namespace winrt::SDKTemplate::implementation
{
    struct MainPage : MainPageT<MainPage>
    {
...
        static SDKTemplate::MainPage Current() { return current; }
...
    private:
        static SDKTemplate::MainPage current;
...
    };
...
}

// MainPage.cpp
...
namespace winrt::SDKTemplate::implementation
{
    SDKTemplate::MainPage MainPage::current{ nullptr };

    MainPage::MainPage()
    {
        InitializeComponent();
        MainPage::current = *this;
        SampleTitle().Text(FEATURE_NAME());
    }
...
}

// SampleConfiguration.cpp
#include "pch.h"
#include "MainPage.h"

using namespace winrt;
using namespace SDKTemplate;

hstring implementation::MainPage::FEATURE_NAME()
{
    return L"Clipboard C++/WinRT Sample";
}

Emellett mindenképpen törölje a MainPage.cpp és MainPage::FEATURE_NAME() függvénytörzseket, mert ezeket a metódusokat máshol definiáljuk.

Mint látható, a MainPage::currentSDKTemplate::MainPage típusúként van deklarálva, amely az előrejelzett típus. Nem a(z) SDKTemplate::implementation::MainPage típusú, amely a megvalósítás típusa. A leképezett típus olyan típus, amelyet úgy terveztek, hogy akár a projekten belül XAML-interoperabilitás céljából, akár különböző binárisok között használjanak fel. A megvalósítási típus az, amellyel megvalósíthatja a leképezett típuson elérhetővé tett funkciókat. Mivel a MainPage::current (in ) deklarációja a implementálási névtérben MainPage.h(winrt::SDKTemplate::implementáció) jelenik meg, a nem minősített MainPage a megvalósítás típusára hivatkozott. Ezért az SDKTemplate:: minősítőt használjuk, hogy egyértelmű legyen: a MainPage::current a winrt::SDKTemplate::MainPage leképezett típus egy példánya.

A konstruktorban van néhány, a MainPage::current = *this;-hoz kapcsolódó szempont, amely magyarázatra szorul.

  • Ha az implementációs típus egy tagján belül használja a this mutatót, akkor a this mutató természetesen az implementációs típusra mutató mutató.
  • A this mutató megfelelő leképezett típussá alakításához dereferálja azt. Feltéve, hogy a megvalósítási típust az IDL-ből hozza létre (ahogy itt is láttuk), a megvalósítási típus rendelkezik egy konverziós operátorral, amely a tervezett típusra konvertál. Ezért működik itt a feladat.

További információ ezekről a részletekről: Implementációtípusok és felületek példányosítása és visszaadása.

A konstruktorban is ott van a(z) SampleTitle().Text(FEATURE_NAME());. A SampleTitle() rész egy SampleTitle nevű egyszerű kiegészítő függvény hívása, amely az XAML-hez hozzáadott TextBlockot adja vissza. Amikor x:Name XAML-elemet használ, az XAML fordító létrehoz egy, az elemhez elnevezett kiegészítőt. A .Text(...) rész meghívja a Text mutációs függvényt azon TextBlock objektumon, amelyet a SampleTitle tartozék visszaadott . És FEATURE_NAME() meghívja a statikus MainPage::FEATURE_NAME kiegészítő függvényt a sztringkonstans visszaadásához. Vagyis ez a kódsor beállítja a Text tulajdonságát a SampleTitle nevű TextBlock elemnek.

Ne feledje, hogy mivel a karakterláncok a Windows-futtatókörnyezet rendszerben széles karakteresek, egy karakterlánc-literál átültetésekor a L széleskarakter-kódolási előtaggal látjuk el. Ezért (például) az „a string literal” karakterlánc-literált L"a string literal"-ra módosítjuk. Lásd még: Széles karakterlánc-literálok.

Forgatókönyvek

Itt találja a megfelelő C# kódot, amelyet ki kell portolni.

// MainPage.xaml.cs
...
public sealed partial class MainPage : Page
{
...
    public List<Scenario> Scenarios
    {
        get { return this.scenarios; }
    }
...
}
...

// SampleConfiguration.cs
...
public partial class MainPage : Page
{
...
    List<Scenario> scenarios = new List<Scenario>
    {
        new Scenario() { Title = "Copy and paste text", ClassType = typeof(CopyText) },
        new Scenario() { Title = "Copy and paste an image", ClassType = typeof(CopyImage) },
        new Scenario() { Title = "Copy and paste files", ClassType = typeof(CopyFiles) },
        new Scenario() { Title = "Other Clipboard operations", ClassType = typeof(OtherScenarios) }
    };
...
}
...

A korábbi vizsgálatból tudjuk, hogy ez a forgatókönyvobjektum-gyűjtemény egy ListBoxban jelenik meg. A C++/WinRT-ben korlátozva van a gyűjtemény típusa , amelyet hozzárendelhetünk egy elemvezérlő ItemsSource tulajdonságához. A gyűjteménynek vektornak vagy megfigyelhető vektornak kell lennie, és elemeinek a következők egyikének kell lenniük:

Az IInspectable-eset esetében, ha az elemek nem maguk a futtatókörnyezeti osztályok, akkor ezeknek az elemeknek olyan típusúnak kell lenniük, amely az IInspectable-ba és az IInspectable-ba is be- és kicsomagozható. Ez pedig azt jelenti, hogy Windows-futtatókörnyezet-típusoknak kell lenniük (lásd: Boxing and unboxing values to IInspectable).

Ebben az esettanulmányban a Scenario osztályt nem tettük futásidejű osztállyá. Ez még mindig ésszerű lehetőség. És a saját átültetési munkád során is lesznek olyan esetek, amikor egy futásidejű osztály egyértelműen a megfelelő választás lesz. Ha például megfigyelhetővé kell tennie az elemtípust (lásd : XAML-vezérlők; kötés egy C++/WinRT tulajdonsághoz), vagy ha az elemnek bármilyen más okból kell metódusokkal rendelkeznie, és ez nem csupán adattagok halmaza.

Mivel ebben az útmutatóban nem a Scenario típushoz tartozó futásidejű osztályt választjuk, számolnunk kell a boxolással. Ha a Forgatókönyvet normál C++ struct-ként hoztuk volna létre, akkor nem tudnánk bekeretezni. De a structScenario-t deklaráltuk az IDL-ben, ezért boxolhatjuk.

Két lehetőségünk marad: vagy előre boxoljuk a Scenario objektumokat, vagy megvárjuk, amíg értéket adunk az ItemsSource-nak, és csak éppen időben boxoljuk őket. Íme néhány szempont ezzel a két lehetőséggel kapcsolatban.

  • Ökölvívás előre. Ennél a lehetőségnél az adattagunk a felhasználói felülethez közvetlenül hozzárendelhető IInspectable elemek gyűjteménye. Az inicializáláskor a Forgatókönyv objektumokat ebbe az adattagba kell beszűkítenünk. Csak egy példányra van szükségünk a gyűjteményből, de minden alkalommal ki kell csomagolnunk egy elemet, amikor a mezők olvasásához szükségünk van rá.
  • Dobozolás pont időben. Ennél a beállításnál az adattag a Scenario elemek gyűjteménye. Amikor eljön az ideje, hogy a felhasználói felülethez rendeljük, az adattagból származó Scenario objektumokat egy új IInspectable-gyűjteménybe bedobozoljuk. Az adatmezőben lévő elemek mezőit kicsomagolás nélkül is beolvashatjuk, de a gyűjtemény két példányára van szükségünk.

Mint látható, egy ilyen kis gyűjtemény esetében az előnyök és hátrányok nagyjából kiegyenlítik egymást. Ezért ebben az esettanulmányban az éppen időben lehetőséget választjuk.

A scenarios tag a MainPage egyik mezője, amely a SampleConfiguration.cs részben van definiálva és inicializálva. A Scenarios a MainPage csak olvasható tulajdonsága, amely a MainPage.xaml.cs elemben van definiálva (és úgy van megvalósítva, hogy egyszerűen a scenarios mezőt adja vissza). A C++/WinRT projektben hasonlót csinálunk; de a két tagot statikussá tesszük (mivel az alkalmazáson belül csak egy példányra van szükségünk, és így osztálypéldány nélkül is hozzáférhetünk hozzájuk). És rendre a scenariosInner és a scenarios nevet adjuk nekik. A scenariosInnert a következőben MainPage.hdeklaráljuk: . Mivel a tárolási időtartam statikus, egy fájlban .cpp fogjuk definiálni/inicializálni (SampleConfiguration.cppebben az esetben).

Szerkessze a(z) MainPage.h és SampleConfiguration.cpp elemet úgy, hogy megfeleljenek az alábbi bejegyzéseknek.

// MainPage.h
...
struct MainPage : MainPageT<MainPage>
{
...
    static Windows::Foundation::Collections::IVector<Scenario> scenarios() { return scenariosInner; }
...
private:
    static winrt::Windows::Foundation::Collections::IVector<Scenario> scenariosInner;
...
};

// SampleConfiguration.cpp
...
using namespace Windows::Foundation::Collections;
...
IVector<Scenario> implementation::MainPage::scenariosInner = winrt::single_threaded_observable_vector<Scenario>(
{
    Scenario{ L"Copy and paste text", xaml_typename<SDKTemplate::CopyText>() },
    Scenario{ L"Copy and paste an image", xaml_typename<SDKTemplate::CopyImage>() },
    Scenario{ L"Copy and paste files", xaml_typename<SDKTemplate::CopyFiles>() },
    Scenario{ L"History and roaming", xaml_typename<SDKTemplate::HistoryAndRoaming>() },
    Scenario{ L"Other Clipboard operations", xaml_typename<SDKTemplate::OtherScenarios>() },
});

Emellett törölje a MainPage.cpp függvény törzsét is, mert ezt a metódust most definiáljuk a fejlécfájlban.

Amint látható, a SampleConfiguration.cpp részben a scenariosInner statikus adattagot a winrt::single_threaded_observable_vector nevű C++/WinRT-segédfüggvény meghívásával inicializáljuk. Ez a függvény létrehoz egy új Windows-futtatókörnyezet gyűjteményobjektumot, és IObservableVector felületként adja vissza. Mivel ebben a mintában a gyűjtemény nem figyelhető meg (nem kell, mert az inicializálás után nem ad hozzá és nem távolít el elemeket), ehelyett a winrt::single_threaded_vector meghívását választhattuk volna. Ez a függvény A gyűjteményt IVector-felületként adja vissza.

A gyűjteményekről és a hozzájuk való kötésről további információt az XAML-elemek vezérlői, a C++/WinRT-gyűjteményekhez való kötés, valamint a C++/WinRT-lel rendelkező gyűjtemények című témakörben talál.

Az imént hozzáadott inicializálási kód olyan típusokra hivatkozik, amelyek még nem szerepelnek a projektben (például winrt::SDKTemplate::CopyText. Ennek orvoslásához adjunk hozzá öt új üres XAML-lapot a projekthez.

Öt új üres XAML-lap hozzáadása

Adjon hozzá egy új Visual C++>Blank Page (C++/WinRT) elemet a projekthez (győződjön meg arról, hogy ez az Üres lap (C++/WinRT) elemsablon, nem pedig az Üres oldal elemsablon. Nevezze el a következőképpen: CopyText. Az új XAML-lap az SDKTemplate névtérben van definiálva, amit szeretnénk.

Ismételje meg a fenti folyamatot még négyszer, és nevezte el az XAML-oldalakat CopyImage, CopyFilesHistoryAndRoamingés OtherScenarios.

Most már újra felépíthet, ha szeretné.

NotifyUser

A C# projektben a MainPage.NotifyUser metódus implementációját találja a következőben MainPage.xaml.cs: . A MainPage.NotifyUser függ a MainPage.UpdateStatustól, és ez a metódus függőségekkel rendelkezik a még nem portolt XAML-elemekhez. Egyelőre csak létrehozunk egy ideiglenes UpdateStatus metódust a C++/WinRT-projektben, és ezt később portoljuk.

Itt találja a megfelelő C# kódot, amelyet ki kell portolni.

// MainPage.xaml.cs
...
public void NotifyUser(string strMessage, NotifyType type)
if (Dispatcher.HasThreadAccess)
{
    UpdateStatus(strMessage, type);
}
else
{
    var task = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => UpdateStatus(strMessage, type));
}
private void UpdateStatus(string strMessage, NotifyType type) { ... }{
...

Az NotifyUser felhasználói felületi frissítéseket küld a fő szálra. A WinUI 3-ban ehhez a régebbi CoreDispatcher helyett a Microsoft.UI.Dispatching.DispatcherQueue használatos. A C++/WinRT-ben, amikor egy Windows vagy Microsoft névtérből szeretne típust használni, a megfelelő C++/WinRT névtérfejlécfájlt kell tartalmaznia (erről további információt a C++/WinRT használatának első lépéseiben talál). Ebben az esetben, ahogy az alábbi kódlistában is látható, a fejléc az winrt/Microsoft.UI.Dispatching.h elem, és bele fogjuk foglalni a(z) pch.h elembe.

Az UpdateStatus privát. Ezért ezt a privát metódust a MainPage implementálási típusában fogjuk használni. Az UpdateStatus-t nem a futtatókörnyezeti osztályban kell meghívni, ezért nem deklaráljuk idL-ben.

Miután átültettük a MainPage.NotifyUser függvényt, és helyettesítő implementációval láttuk el a MainPage.UpdateStatus függvényt, ezt kapjuk a C++/WinRT-projektben. A kódlista után megvizsgálunk néhány részletet.

// pch.h
...
#include <winrt/Microsoft.UI.Dispatching.h>
...

// MainPage.h
...
struct MainPage : MainPageT<MainPage>
{
...
    void NotifyUser(hstring const& strMessage, SDKTemplate::NotifyType const& type);
...
private:
    void UpdateStatus(hstring const& strMessage, SDKTemplate::NotifyType const& type);
...
};

// MainPage.cpp
...
void MainPage::NotifyUser(hstring const& strMessage, SDKTemplate::NotifyType const& type)
{
    if (DispatcherQueue().HasThreadAccess())
    {
        UpdateStatus(strMessage, type);
    }
    else
    {
        DispatcherQueue().TryEnqueue([strMessage, type, this]()
            {
                UpdateStatus(strMessage, type);
            });
    }
}
void MainPage::UpdateStatus(hstring const& strMessage, SDKTemplate::NotifyType const& type)
{
    throw hresult_not_implemented();
}
...

C#-ban pontozott jelöléssel beleléphet a beágyazott tulajdonságokba. A C# MainPage típus tehát elérheti a saját Dispatcher tulajdonságát a szintaxissal Dispatcher. A C# pedig további pontokat is be tud illeszteni ebbe az értékbe olyan szintaxissal, mint a Dispatcher.HasThreadAccess. A C++/WinRT-ben a tulajdonságok kiegészítő függvényekként vannak implementálva, így a szintaxis csak abban különbözik, hogy minden függvényhíváshoz zárójeleket ad hozzá.

C# C++/WinRT
Dispatcher.HasThreadAccess DispatcherQueue().HasThreadAccess()

Amikor a NotifyUser C#-verziója meghívja a Dispatcher.RunAsyncet, a WinUI 3 megfelelője a DispatcherQueue.TryEnqueue-t használja. A C++/WinRT verzió lambda függvényként implementálja a visszahívási delegáltat. A C++/WinRT-ben rögzítjük a használni kívánt két paramétert, valamint az this egérmutatót (mivel tagfüggvényt fogunk hívni). A meghatalmazottak lambdasként való implementálásáról és a kódpéldákról a C++/WinRT delegáltak használatával történő események kezelése című témakörben talál további információt.

A MainPage fennmaradó tagjainak implementálása

Készítsük el a MainPage tagjainak teljes listáját (a MainPage.xaml.cs és SampleConfiguration.cs között megvalósítva), hogy lássuk, melyeket ültettük már át eddig, és melyek vannak még hátra.

Tag Access Status
MainPage konstruktor public Portolva
Aktuális tulajdonság public Portolt
FEATURE_NAME tulajdonság public Portolva
IsClipboardContentChangedEnabled tulajdonság public Nincs elindítva
Forgatókönyvek tulajdonsága public Portolva
BuildClipboardFormatsOutputString metódus public Nincs elindítva
DisplayToast metódus public Nincs elindítva
EnableClipboardContentChangedNotifications metódus public Nincs elindítva
NotifyUser metódus public Portolva
OnNavigatedTo metódus protected Nincs elindítva
isApplicationWindowActive mező private Nincs elindítva
needToPrintClipboardFormat mező private Nincs elindítva
Forgatókönyvek mező private Portolva
Button_Click metódus private Nincs elindítva
DisplayChangedFormats metódus private Nincs elindítva
Footer_Click metódus private Nincs elindítva
HandleClipboardChanged metódus private Nincs elindítva
OnClipboardChanged metódus private Nincs elindítva
OnWindowActivated metódus private Nincs elindítva
ScenarioControl_SelectionChanged metódus private Nincs elindítva
UpdateStatus metódus private Kicsúszva

A következő néhány alszakaszban a még be nem jelentett tagokról lesz szó.

Note

Időnként a forráskódban az XAML-jelölőnyelvben lévő felhasználóifelület-elemekre mutató hivatkozásokkal találkozunk (a(z) MainPage.xaml elemben). Amikor ezekre a hivatkozásokra visszatérünk, ideiglenesen megkerüljük őket, ha egyszerű helyőrző elemeket adunk hozzá az XAML-hez. Így a projekt minden alszakasz után tovább fog épülni. Alternatív megoldásként a hivatkozásokat úgy oldhatja meg, hogy a C# projekt teljes tartalmát MainPage.xaml átmásolja a C++/WinRT projektbe. De ha ezt tesszük, sok időbe telik majd, mire meg tudunk állni egy pillanatra, és újra lefuttatni a buildet (így előfordulhat, hogy közben elrejtjük az út során ejtett elírásokat vagy egyéb hibákat).

Ha végzett a MainPage osztály imperatív kódjának portolásával, átmásoljuk az XAML-fájl tartalmát, és biztosak vagyunk benne, hogy a projekt továbbra is létrejön.

IsClipboardContentChangedEnabled

Ez egy beolvasási C# tulajdonság, amely alapértelmezés szerint a következő: false. A MainPage tagja, és a következőben van definiálva SampleConfiguration.cs: .

C++/WinRT esetén szükség lesz egy kiegészítő függvényre, egy mutációs függvényre és egy háttéradat-tagra mezőként. Mivel az IsClipboardContentChangedEnabled a mintában szereplő egyik forgatókönyv állapotát jelöli, és nem magát a MainPage állapotát, az új tagokat egy SampleState nevű új segédprogramtípuson fogjuk létrehozni. Ezt implementáljuk a forráskódfájlban SampleConfiguration.cpp , és a tagokat static is meg fogjuk tenni (mivel az alkalmazáson belül csak egy példányra van szükségünk, és hogy osztálypéldány nélkül is hozzáférhessünk hozzájuk).

Ha a C++/WinRT projekthez szeretne csatlakozni SampleConfiguration.cpp , adjon hozzá egy új Visual C++>Code>Header File (.h) elemet a névvel SampleConfiguration.h. Szerkessze a(z) SampleConfiguration.h és SampleConfiguration.cpp elemet úgy, hogy megfeleljenek az alábbi felsorolásnak.

// SampleConfiguration.h
#pragma once 
#include "pch.h"

namespace winrt::SDKTemplate
{
    struct SampleState
    {
        static bool IsClipboardContentChangedEnabled();
        static void IsClipboardContentChangedEnabled(bool checked);
    private:
        static bool isClipboardContentChangedEnabled;
    };
}

// SampleConfiguration.cpp
...
#include "SampleConfiguration.h"
...
bool SampleState::isClipboardContentChangedEnabled = false;
...
bool SampleState::IsClipboardContentChangedEnabled()
{
    return isClipboardContentChangedEnabled;
}
void SampleState::IsClipboardContentChangedEnabled(bool checked)
{
    if (isClipboardContentChangedEnabled != checked)
    {
        isClipboardContentChangedEnabled = checked;
    }
}

Ismét meg kell határozni egy tárolót tartalmazó static mezőt (például SampleState::isClipboardContentChangedEnabled) egyszer az alkalmazásban, és egy .cpp fájl jó hely erre (SampleConfiguration.cpp ebben az esetben).

BuildClipboardFormatsOutputString

Ez a metódus a MainPage nyilvános tagfüggvénye, és itt van definiálva: SampleConfiguration.cs

// SampleConfiguration.cs
...
public string BuildClipboardFormatsOutputString()
{
    DataPackageView clipboardContent = Windows.ApplicationModel.DataTransfer.Clipboard.GetContent();
    StringBuilder output = new StringBuilder();

    if (clipboardContent != null && clipboardContent.AvailableFormats.Count > 0)
    {
        output.Append("Available formats in the clipboard:");
        foreach (var format in clipboardContent.AvailableFormats)
        {
            output.Append(Environment.NewLine + " * " + format);
        }
    }
    else
    {
        output.Append("The clipboard is empty");
    }
    return output.ToString();
}
...

A C++/WinRT-ben a BuildClipboardFormatsOutputString metódust a SampleState nyilvános statikus metódusaként fogjuk létrehozni. Statikussá tehetjük a static elemet, mert nem fér hozzá egyetlen példánytaghoz sem.

A Clipboard és a DataPackageView típusok C++/WinRT-ben való használatához fel kell vennünk a C++/WinRT Windows névtérhez tartozó fejlécfájlt winrt/Windows.ApplicationModel.DataTransfer.h.

A C#-ban a DataPackageView.AvailableFormats tulajdonság egy IReadOnlyList, így hozzáférhetünk annak Count tulajdonságához. A C++/WinRT-ben a DataPackageView::AvailableFormats tartozékfüggvény egy IVectorView-t ad vissza, amely egy méretkiegészítő funkcióval rendelkezik, amelyet meghívhatunk.

A C# System.Text.StringBuilder típus használatának portolásához az std::wostringstream szabványos C++ típust használjuk. Ez a típus a széles sztringek kimeneti adatfolyama (és a használatához a sstream fejlécfájlt is tartalmaznia kell). A StringBuilder esetében használt Append metódus helyett a beillesztő operátort (<<) használja egy kimeneti adatfolyammal, például wostringstream használatakor. További információ: iostream-programozás és C++/WinRT-sztringek formázása.

A C#-kód létrehoz egy StringBuildert a new kulcsszóval. A C#-ban az objektumok alapértelmezés szerint referenciatípusok, deklarálva a halomra a következővel new: . A modern szabványos C++-ban az objektumok alapértelmezés szerint értéktípusok, és a veremen vannak deklarálva (a new használata nélkül). Tehát a StringBuilder output = new StringBuilder(); elemet egyszerűen átültetjük C++/WinRT-re, így: std::wostringstream output;.

A C# var kulcsszó arra kéri a fordítót, hogy következtetjen egy típusra. Ön a(z) var elemet auto elemre portolja C++/WinRT-ben. A C++/WinRT esetében azonban vannak olyan esetek, amikor (a másolatok elkerülése érdekében) egy kikövetkeztetett (vagy dedukált) típusra szeretne hivatkozni , és egy lekövetett típusra mutató lvalue-hivatkozást ad meg a következővel auto&: . Vannak olyan esetek is, amikor egy speciális fajta hivatkozást szeretne, amely helyesen kötődik, függetlenül attól, hogy lvalue-val vagy rvalue-val inicializálják. És ezt kifejezi a auto&&. Ez az a forma, amelyet az alábbi portolt kódban látható for ciklus használ. A lvalue és rvalue fogalmának bevezetéséhez lásd a Értékkategóriák és a rájuk mutató referenciák című témakört.

Szerkessze a pch.h, SampleConfiguration.h és SampleConfiguration.cpp elemeket úgy, hogy megfeleljenek az alábbi listáknak.

// pch.h
...
#include <sstream>
#include "winrt/Windows.ApplicationModel.DataTransfer.h"
...

// SampleConfiguration.h
...
struct SampleState
{
    static hstring BuildClipboardFormatsOutputString();
    ...
}
...

// SampleConfiguration.cpp
...
using namespace Windows::ApplicationModel::DataTransfer;
...
hstring SampleState::BuildClipboardFormatsOutputString()
{
    DataPackageView clipboardContent{ Clipboard::GetContent() };
    std::wostringstream output;

    if (clipboardContent && clipboardContent.AvailableFormats().Size() > 0)
    {
        output << L"Available formats in the clipboard:";
        for (auto&& format : clipboardContent.AvailableFormats())
        {
            output << std::endl << L" * " << std::wstring_view(format);
        }
    }
    else
    {
        output << L"The clipboard is empty";
    }

    return hstring{ output.str() };
}

Note

A DataPackageView clipboardContent{ Clipboard::GetContent() }; kódsor szintaxisa a modern szabványos C++ egyik, egységes inicializálásnak nevezett funkcióját használja, amelyre a = jel helyett a kapcsos zárójelek használata jellemző. Ez a szintaxis egyértelművé teszi, hogy inicializálás történik, nem pedig hozzárendelés. Ha inkább a hozzárendelésnek (de valójában nem) tűnő szintaxist részesíti előnyben, akkor a fenti szintaxist lecserélheti a megfelelőre DataPackageView clipboardContent = Clipboard::GetContent();. Érdemes azonban mindkét inicializálási módszert használni, mert valószínűleg mindkettőt gyakran használják a használt kódban.

DisplayToast

A(z) DisplayToast a C# MainPage osztály nyilvános statikus metódusa, és itt találja a definícióját: SampleConfiguration.cs A C++/WinRT-ben a SampleState nyilvános statikus metódusaként fogjuk használni.

Már találkoztunk a módszer portolásával kapcsolatos legtöbb részletkel és technikával. Egy új dolog, amit érdemes megjegyezni, hogy egy C# verbatim sztringliterált @ egy szabványos C++-os nyers sztringliterálra (LR) ültetsz át.

Ha a C++/WinRT-ben a ToastNotification és az XmlDocument típusra hivatkozik, névtérnév alapján minősítheti őket, vagy szerkesztheti SampleConfiguration.cpp és hozzáadhatja using namespace az irányelveket, például az alábbi példát.

using namespace Windows::UI::Notifications;

Az XmlDocument típusra való hivatkozáskor és bármely más Windows-futtatókörnyezet típusra való hivatkozáskor is ugyanaz a lehetőség áll rendelkezésre.

Ezen elemeken kívül kövesse ugyanazt az útmutatást, mint korábban a következő lépések végrehajtásához.

  • Deklarálja a metódust a(z) SampleConfiguration.h elemben, és definiálja a(z) SampleConfiguration.cpp elemben.
  • Módosítsa a(z) pch.h fájlt úgy, hogy tartalmazza az összes szükséges C++/WinRT Windows-névtérhez tartozó fejlécfájlt.
  • A C++/WinRT-objektumokat ne a heapen, hanem a veremen hozza létre.
  • Cserélje le a tulajdonságok lekérő hozzáférőinek hívásait függvényhívási szintaxisra (()).

A fordító-/linkerhibák egyik gyakori oka, hogy elfelejti tartalmazni a szükséges C++/WinRT-Windows névtérfejlécfájlokat. További információt egy lehetséges hibáról lásd: C3779: Miért adja a fordító a(z) „consume_Something: az 'auto' értéket visszaadó függvény nem használható a definiálása előtt” hibát?.

Ha követni szeretné az útmutatót, és saját maga szeretné portolni a DisplayToast elemet, akkor összehasonlíthatja az eredményeit a letöltött Vágólap-minta forráskódjának ZIP-fájljában található C++/WinRT-verzió kódjával.

Vágólaptartalom-változási értesítések engedélyezése

A C# MainPage osztály EnableClipboardContentChangedNotifications eleme egy nyilvános statikus metódus, és a(z) SampleConfiguration.cs elemben van definiálva.

// SampleConfiguration.cs
...
public bool EnableClipboardContentChangedNotifications(bool enable)
{
    if (IsClipboardContentChangedEnabled == enable)
    {
        return false;
    }

    IsClipboardContentChangedEnabled = enable;
    if (enable)
    {
        Clipboard.ContentChanged += OnClipboardChanged;
        Window.Current.Activated += OnWindowActivated;
    }
    else
    {
        Clipboard.ContentChanged -= OnClipboardChanged;
        Window.Current.Activated -= OnWindowActivated;
    }
    return true;
}
...
private void OnClipboardChanged(object sender, object e) { ... }
private void OnWindowActivated(object sender, WindowActivatedEventArgs e) { ... }
...

A C++/WinRT-ben a SampleState nyilvános statikus metódusaként fogjuk használni.

A C#-ban a += és a -= operátorszintaxist használhatja az eseménykezelő delegáltak felvételéhez és eltávolításához. A C++/WinRT-ben számos szintaktikai lehetőség közül választhat a meghatalmazottak regisztrálására/visszavonására, az Események kezelése c++/WinRT-meghatalmazottak használatával. Az általános minta azonban az, hogy az eseményről elnevezett függvénypár meghívásával regisztrál, illetve megszünteti a regisztrációt. A regisztrációhoz át kell adnia a delegáltját a regisztráló függvénynek, és cserébe egy visszavonási tokent kap vissza (winrt::event_token). A visszavonáshoz ezt a tokent átadod a visszavonó függvénynek. Ebben az esetben az hander statikus, és (mint az alábbi kódlistában látható) a függvényhívás szintaxisa egyszerű.

Hasonló tokeneket használnak valójában a háttérben a C#-ban. De a nyelv ezt a részletet implicité teszi. A C++/WinRT egyértelművé teszi.

Az objektumtípus megjelenik a C# eseménykezelő aláírásaiban. A C# nyelvben az object a .NET System.Object típus aliasa. A C++/WinRT megfelelője a winrt::Windows::Foundation::IInspectable. Az IInspectable tehát megjelenik a C++/WinRT eseménykezelőkben.

Szerkessze a(z) SampleConfiguration.h és SampleConfiguration.cpp elemet úgy, hogy megfeleljenek az alábbi felsorolásnak.

// SampleConfiguration.h
...
    static bool EnableClipboardContentChangedNotifications(bool enable);
    ...
private:
    ...
    static event_token clipboardContentChangedToken;
    static event_token activatedToken;
    static void OnClipboardChanged(Windows::Foundation::IInspectable const& sender, Windows::Foundation::IInspectable const& e);
    static void OnWindowActivated(Windows::Foundation::IInspectable const& sender, Microsoft::UI::Xaml::WindowActivatedEventArgs const& e);
...

// SampleConfiguration.cpp
...
using namespace Windows::Foundation;
using namespace Microsoft::UI;
using namespace Microsoft::UI::Xaml;
...
event_token SampleState::clipboardContentChangedToken;
event_token SampleState::activatedToken;
...
bool SampleState::EnableClipboardContentChangedNotifications(bool enable)
{
    if (isClipboardContentChangedEnabled == enable)
    {
        return false;
    }

    IsClipboardContentChangedEnabled(enable);
    if (enable)
    {
        clipboardContentChangedToken = Clipboard::ContentChanged(OnClipboardChanged);
        activatedToken = Window::Current().Activated(OnWindowActivated);
    }
    else
    {
        Clipboard::ContentChanged(clipboardContentChangedToken);
        Window::Current().Activated(activatedToken);
    }
    return true;
}
void SampleState::OnClipboardChanged(IInspectable const&, IInspectable const&){}
void SampleState::OnWindowActivated(IInspectable const&, WindowActivatedEventArgs const& e){}

Az eseménykezelő meghatalmazottakat (OnClipboardChanged és OnWindowActivated) egyelőre hagyja csonkként. Már szerepelnek a portra vonandó tagok listáján, ezért a későbbi alszakaszokban fogjuk elérni őket.

OnNavigatedTo

Az OnNavigatedTo a C# MainPage osztály védett metódusa, amely a következőben MainPage.xaml.csvan definiálva: . Íme, az általa hivatkozott XAML-ListBox vezérlővel együtt.

<!-- MainPage.xaml -->
...
<ListBox x:Name="ScenarioControl" ... />
...
// MainPage.xaml.cs
protected override void OnNavigatedTo(NavigationEventArgs e)
{
    // Populate the scenario list from the SampleConfiguration.cs file
    var itemCollection = new List<Scenario>();
    int i = 1;
    foreach (Scenario s in scenarios)
    {
        itemCollection.Add(new Scenario { Title = $"{i++}) {s.Title}", ClassType = s.ClassType });
    }
    ScenarioControl.ItemsSource = itemCollection;

    if (Window.Current.Bounds.Width < 640)
    {
        ScenarioControl.SelectedIndex = -1;
    }
    else
    {
        ScenarioControl.SelectedIndex = 0;
    }
}

Ez egy fontos és érdekes módszer, mert itt rendeljük hozzá a forgatókönyvobjektumok gyűjteményét a felhasználói felülethez. A C# kód létrehoz egy System.Collections.Generic.List of Scenario objektumot, és hozzárendeli azt egy ListBoxItemsSource tulajdonságához (amely egy elemvezérlő). C#-ban pedig sztringinterpolációt használunk az egyes Scenario objektumok címének létrehozásához (figyelje meg a $ speciális karakter használatát).

A C++/WinRT-ben az OnNavigatedTo-t a MainPage nyilvános metódusának fogjuk tenni. És hozzáadunk egy csonk ListBox elemet az XAML-hez, hogy a build sikeres legyen. A kódlista után megvizsgálunk néhány részletet.

<!-- MainPage.xaml -->
...
<StackPanel ...>
    ...
    <ListBox x:Name="ScenarioControl" />
</StackPanel>
...
// MainPage.h
...
void OnNavigatedTo(Microsoft::UI::Xaml::Navigation::NavigationEventArgs const& e);
...

// MainPage.cpp
...
using namespace winrt::Microsoft::UI::Xaml;
using namespace winrt::Microsoft::UI::Xaml::Navigation;
...
void MainPage::OnNavigatedTo(NavigationEventArgs const& /* e */)
{
    auto itemCollection = winrt::single_threaded_observable_vector<IInspectable>();
    int i = 1;
    for (auto s : MainPage::scenarios())
    {
        s.Title = winrt::to_hstring(i++) + L") " + s.Title;
        itemCollection.Append(winrt::box_value(s));
    }
    ScenarioControl().ItemsSource(itemCollection);

    if (Window::Current().Bounds().Width < 640)
    {
        ScenarioControl().SelectedIndex(-1);
    }
    else
    {
        ScenarioControl().SelectedIndex(0);
    }
}
...

Ismét meghívjuk a winrt::single_threaded_observable_vector függvényt, de ezúttal egy IInspectable-gyűjtemény létrehozásához. Ez is része volt annak a döntésnek, hogy a Scenario objektumaink boxing műveletét éppen időben hajtsuk végre.

A C#sztring interpolációjának helyett pedig a to_hstring függvény és a winrt::hstringösszefűzési operátorának kombinációját használjuk.

isApplicationWindowActive

A C#-ban az isApplicationWindowActive egy egyszerű privát bool mező, amely a MainPage osztályhoz tartozik, és a következőben SampleConfiguration.csvan definiálva: . Alapértelmezés szerint false. A C++/WinRT-ben a SampleState privát statikus mezőjévé tesszük (a már ismertetett okok miatt) a SampleConfiguration.hSampleConfiguration.cpp fájlokban, ugyanazzal az alapértelmezett beállítással.

Már láttuk, hogyan deklarálhat, definiálhat és inicializálhat statikus mezőt. Frissítőként tekintse meg az isClipboardContentChangedEnabled mezővel végzett műveleteket, és tegye ugyanezt az isApplicationWindowActive-val is.

needToPrintClipboardFormat

Ugyanaz a minta, mint az isApplicationWindowActive (lásd a címsort közvetlenül előtte).

Button_Click

Button_Click a C# MainPage osztály privát (eseménykezelési) metódusa, amely a következőben MainPage.xaml.csvan definiálva: . Íme, együtt azzal az XAML SplitView-val, amelyre hivatkozik, valamint az azt regisztráló ToggleButton-nel.

<!-- MainPage.xaml -->
...
<SplitView x:Name="Splitter" ... />
...
<ToggleButton Click="Button_Click" .../>
...
private void Button_Click(object sender, RoutedEventArgs e)
{
    Splitter.IsPaneOpen = !Splitter.IsPaneOpen;
}

Íme az ennek megfelelő, C++/WinRT-re átültetett változat. Vegye figyelembe, hogy a C++/WinRT verzióban az eseménykezelő public (amint látható, azt a private: deklarációk előtt deklarálja). Ennek az az oka, hogy az XAML-jelölőnyelvben regisztrált eseménykezelőnek, mint ez is, a C++/WinRT-ben public kell lennie ahhoz, hogy az XAML-jelölőnyelv hozzáférhessen. Ha viszont az imperatív kódban regisztrál egy eseménykezelőt (mint a MainPage::EnableClipboardContentChangedNotifications korábban), akkor az eseménykezelőnek nem kell lennie public.

<!-- MainPage.xaml -->
...
<StackPanel ...>
    ...
    <SplitView x:Name="Splitter" />
</StackPanel>
...
// MainPage.h
...
    void Button_Click(Windows::Foundation::IInspectable const& sender, Microsoft::UI::Xaml::RoutedEventArgs const& e);
private:
...

// MainPage.cpp
void MainPage::Button_Click(Windows::Foundation::IInspectable const& /* sender */, Microsoft::UI::Xaml::RoutedEventArgs const& /* e */)
{
    Splitter().IsPaneOpen(!Splitter().IsPaneOpen());
}

DisplayChangedFormats

A C# nyelvben a DisplayChangedFormats a MainPage osztályhoz tartozó privát metódus, és itt van definiálva: SampleConfiguration.cs.

private void DisplayChangedFormats()
{
    string output = "Clipboard content has changed!" + Environment.NewLine;
    output += BuildClipboardFormatsOutputString();
    NotifyUser(output, NotifyType.StatusMessage);
}

C++/WinRT-ben ezt a SampleState privát statikus mezőjévé tesszük (mivel nem fér hozzá egyetlen példánytaghoz sem), a SampleConfiguration.h és SampleConfiguration.cpp fájlban. A metódus C#-kódja nem használja a System.Text.StringBuildert; de elég sztringformázást végez, hogy a C++/WinRT verzió esetében ez egy másik jó hely az std::wostringstream használatához.

A C#-kódban használt statikus System.Environment.NewLine tulajdonság helyett a standard C++ std::endl (új vonal karakter) karaktert fogjuk beszúrni a kimeneti adatfolyamba.

// SampleConfiguration.h
...
private:
    static void DisplayChangedFormats();
...

// SampleConfiguration.cpp
void SampleState::DisplayChangedFormats()
{
    std::wostringstream output;
    output << L"Clipboard content has changed!" << std::endl;
    output << BuildClipboardFormatsOutputString().c_str();
    MainPage::Current().NotifyUser(output.str(), NotifyType::StatusMessage);
}

A fenti C++/WinRT-verzió kialakítása kis hatékonyságot jelent. Először létrehozunk egy std::wostringstreamet. De meghívjuk a BuildClipboardFormatsOutputString metódust is (amelyet korábban portoltunk). Ez a metódus létrehozza a saját std::wostringstreamjét. És az adatfolyamát winrt::hstringgé alakítja, majd azt adja vissza. A hstring::c_str függvényt arra használjuk, hogy a visszaadott hstring értéket ismét C-stílusú sztringgé alakítsuk, majd ezt beillesztjük a streambe. Hatékonyabb lenne csak egyetlen std::wostringstream objektumot létrehozni, és azt (vagy az arra mutató hivatkozást) továbbadni, hogy a metódusok közvetlenül írhassanak bele sztringeket.

Ezt tesszük a vágólap minta forráskódjának C++/WinRT verziójában (a letöltött ZIP-ben). Ebben a forráskódban egy új, SampleState nevű privát statikus metódus található ::AddClipboardFormatsOutputString, amely egy kimeneti streamre mutató hivatkozáson alapul és működik. Ezután a SampleState::DisplayChangedFormats és SampleState::BuildClipboardFormatsOutputString metódusokat úgy refaktorálják, hogy az új metódust hívják meg. Ez funkcionálisan egyenértékű a jelen témakör kódlistáival, de hatékonyabb.

Footer_Click a C# MainPage osztályhoz tartozó aszinkron eseménykezelő, amely a következőben MainPage.xaml.csvan definiálva: . Az alábbi kódlista funkcionálisan egyenértékű a letöltött forráskód metódusával. De itt egy sor helyett négy sorra bontottam, hogy könnyebb legyen átlátni, mit csinál, és ennek megfelelően hogyan érdemes átültetnünk.

async void Footer_Click(object sender, RoutedEventArgs e)
{
    var hyperlinkButton = (HyperlinkButton)sender;
    string tagUrl = hyperlinkButton.Tag.ToString();
    Uri uri = new Uri(tagUrl);
    await Windows.System.Launcher.LaunchUriAsync(uri);
}

Bár a módszer technikailag aszinkron, a művelet után awaitsemmit sem tesz, így nincs rá szüksége await (és nem is a async kulcsszóra). Valószínűleg arra használja őket, hogy elkerülje az IntelliSense üzenetet a Visual Studio.

A megfelelő C++/WinRT metódus is aszinkron lesz (mivel a Launcher.LaunchUriAsync metódust hívja meg). De nem kell, és nem is co_awaitkell visszaadnia egy aszinkron objektumot. Az objektumokról és az aszinkron műveletekről co_await további információt a C++/WinRT egyidejűségi és aszinkron műveletek című témakörben talál.

Most pedig beszéljünk arról, hogy mit csinál ez a módszer. Mivel ez egy HyperlinkButtonClick eseményének eseménykezelője, a sender nevű objektum valójában egy HyperlinkButton. Tehát a típusátalakítás biztonságos (ezt az átalakítást sender as HyperlinkButtonmásként is kifejezhettük volna). Ezután lekérjük a Címke tulajdonság értékét (ha megtekinti a C# projekt XAML-jelölőjét, látni fogja, hogy ez egy webes URL-címet jelölő sztringre van állítva). Bár a FrameworkElement.Tag tulajdonság (HyperlinkButton egy FrameworkElement) típusú objektum, a C#-ban sztringezhetjük az Object.ToString tulajdonsággal. Az eredményül kapott sztringből létrehozunk egy Uri-objektumot . Végül (a Shell segítségével) elindítunk egy böngészőt, és navigálunk az URL-címre.

Az alábbiakban a C++/WinRT fájlba portolt metódust (ismét kibontva az egyértelműség kedvéért), amely után a részletek leírása következik.

// pch.h
...
#include "winrt/Windows.System.h"
...

// MainPage.h
...
    void Footer_Click(Windows::Foundation::IInspectable const& sender, Microsoft::UI::Xaml::RoutedEventArgs const& e);
private:
...

// MainPage.cpp
...
using namespace winrt::Windows::Foundation;
using namespace winrt::Microsoft::UI::Xaml::Controls;
...
void MainPage::Footer_Click(Windows::Foundation::IInspectable const& sender, Microsoft::UI::Xaml::RoutedEventArgs const&)
{
    auto hyperlinkButton{ sender.as<HyperlinkButton>() };
    hstring tagUrl{ winrt::unbox_value<hstring>(hyperlinkButton.Tag()) };
    Uri uri{ tagUrl };
    Windows::System::Launcher::LaunchUriAsync(uri);
}

Mint mindig, létrehozzuk az eseménykezelőt public. A feladó objektum as függvényét használjuk a HyperlinkButtonra való konvertáláshoz. A C++/WinRT esetében a Tag tulajdonság egy IInspectable (a Object megfelelője). De nincs Tostring az IInspectable-en. Ehelyett a IInspectable-t egy skaláris értékké kell kicsomagolnunk (ebben az esetben karakterlánccá). Ismét, a dobozolással és a kicsomagolással kapcsolatos további információért lásd: Boxing and unboxing values to IInspectable.

Az utolsó két sor ismétli a korábban látott portálási mintákat, és nagyjából a C#-verziót tükrözik.

HandleClipboardChanged

Ennek a módszernek a portolásában nincs semmi új. A letöltött vágólap minta forráskódjának ZIP-fájljában összehasonlíthatja a C# és a C++/WinRT verziót.

OnClipboardChanged és OnWindowActivated

Eddig csak üres csonkok vannak ehhez a két eseménykezelőhöz. Az átültetésük viszont egyszerű, és nem vet fel semmi új megbeszélnivalót.

ScenarioControl_SelectionChanged

Ez egy másik privát eseménykezelő, amely a C# MainPage osztályhoz tartozik, és a következőben MainPage.xaml.csvan definiálva: . A C++/WinRT-ben nyilvánossá tesszük, és implementáljuk a következőben MainPage.h : és MainPage.cpp.

Ehhez a metódushoz szükségünk lesz a MainPage::navigating nevű privát logikai típusú mezőre, amelynek kezdeti értéke false. És szüksége lesz egy Frame-ra a MainPage.xamlScenarioFrame nevű környezetben. A részleteken kívül azonban a módszer portolása nem fed fel új technikákat.

Ha a kézi portolás helyett a letöltött Clipboard-minta forráskódjának ZIP-fájljában található C++/WinRT-verzióból másolja át a kódot, akkor ott a MainPage::NavigateTo használatát fogja látni. Egyelőre csak refaktoráld át a NavigateTo tartalmát a ScenarioControl_SelectionChanged függvénybe.

UpdateStatus

A MainPage.UpdateStatus metódushoz eddig csak egy vázlatos megvalósítás van. A megvalósítás portolása ismét jórészt már ismert területet érint. Egy újabb megjegyzendő pont, hogy míg C#-ban összehasonlíthatunk egy sztringet a String.Empty értékkel, a C++/WinRT-ben ehelyett a winrt::hstring::empty függvényt hívjuk meg. Egy másik dolog, hogy a nullptr a C#-beli null szabványos C++-beli megfelelője.

A portolás fennmaradó részét azokkal a technikákkal végezheti el, amelyeket már ismertettünk. Az alábbi lista felsorolja azokat a műveleteket, amelyeket a metódus portolt verziójának fordítása előtt el kell végeznie.

  • A(z) MainPage.xaml elemhez adjon hozzá egy Border elemet StatusBorder néven.
  • A MainPage.xaml elemhez adjon hozzá egy TextBlock elemet StatusBlock néven.
  • Ehhez MainPage.xamladjon hozzá egy StatusPanel nevű StackPanelt.
  • A(z) pch.h hozzáadásához adja hozzá a következőt: #include "winrt/Microsoft.UI.Xaml.Media.h".
  • A(z) pch.h hozzáadásához adja hozzá a következőt: #include "winrt/Microsoft.UI.Xaml.Automation.Peers.h".
  • A MainPage.cpp hozzáadásához using namespace winrt::Microsoft::UI::Xaml::Media;.
  • A hozzáadáshoz MainPage.cppusing namespace winrt::Microsoft::UI::Xaml::Automation::Peers;.

A MainPage portolásának befejezéséhez szükséges XAML és stílusok másolása

XAML esetén az ideális eset az, hogy ugyanazt az XAML-korrektúrát használhatja egy C# és egy C++/WinRT projekt esetében. A vágólap minta pedig az ilyen esetek egyike.

A vágólap mintája Styles.xaml XAML ResourceDictionary stílusokkal rendelkezik, amelyek az alkalmazás felhasználói felületén található gombokra, menükre és egyéb felhasználói felületi elemekre vonatkoznak. A Styles.xaml lap egyesítve lesz a következőbe App.xaml: . És itt van a felhasználói felület szabványos MainPage.xaml kiindulópontja, amelyet már rövid ideig láttunk. A projekt C++/WinRT verziójában mostantól változatlanul használhatjuk újra ezt a három .xaml fájlt.

Az eszközfájlokhoz hasonlóan az alkalmazás több verziójából is hivatkozhat ugyanazokra a megosztott XAML-fájlokra. Ebben az útmutatóban csak az egyszerűség kedvéért átmásoljuk a fájlokat a C++/WinRT projektbe, és így adjuk hozzá őket.

Lépjen a \Clipboard_sample\SharedContent\xaml mappára, jelölje ki és másolja App.xamlMainPage.xaml ki, majd illessze be a két fájlt a \Clipboard\Clipboard C++/WinRT projekt mappájába, és válassza a fájlok cseréjét, amikor a rendszer kéri.

Az Visual Studio C++/WinRT projektjében kattintson a Minden fájl megjelenítése elemre a bekapcsolásához. Most vegyen fel egy új mappát, közvetlenül a projektcsomópont alatt, és nevezze el.Styles A Fájlkezelőben keresse meg a \Clipboard_sample\SharedContent\xaml mappát, jelölje ki és másolja Styles.xamlki, majd illessze be az \Clipboard\Clipboard\Styles imént létrehozott mappába. Visszatérve a C++/WinRT-projekt Megoldáskezelőébe, kattintson a jobb gombbal a Styles mappára, majd válassza a >Hozzáadás>Meglévő elem... lehetőséget, és navigáljon ide: \Clipboard\Clipboard\Styles A fájlválasztóban válassza ki Styles és kattintson a Hozzáadás gombra.

Adjon hozzá egy új mappát a C++/WinRT projekthez, közvetlenül a projektcsomópont alatt, és elnevezve Styles. Keresse meg a(z) \Clipboard_sample\SharedContent\xaml mappát, jelölje ki és másolja ki a(z) Styles.xaml elemet, majd illessze be a C++/WinRT-projektjében található \Clipboard\Clipboard\Styles mappába. Kattintson a jobb gombbal a Styles mappára (a C++/WinRT-projekt Megoldáskezelőjében), majd válassza a >Hozzáadás>Meglévő elem... lehetőséget, és keresse meg a következőt: \Clipboard\Clipboard\Styles. A fájlválasztóban válassza ki Styles és kattintson a Hozzáadás gombra.

Kattintson ismét az Összes fájl megjelenítése gombra a kikapcsoláshoz.

Ezzel befejeztük a MainPage portolását, és ha követte a lépéseket, akkor a C++/WinRT-projekt már létrejön és fut.

Fájlok konszolidálása .idl

A felhasználói felület szabványos MainPage.xaml kiindulási pontja mellett a vágólap mintája öt másik forgatókönyvspecifikus XAML-oldallal és a hozzájuk tartozó kód mögötti fájlokkal is rendelkezik. A projekt C++/WinRT-verziójában mindezen oldalak eredeti XAML-jelölőnyelvi kódját változtatás nélkül újra fel fogjuk használni. A következő néhány főbb szakaszban megnézzük, hogyan lehet átültetni a code-behind kódot. De előtte beszéljünk az IDL-ről.

Előnyös lehet a futásidejű osztályaihoz tartozó IDL-t egyetlen IDL-fájlba foglalni. Az értékkel kapcsolatos további információkért lásd a következőt: Futtatókörnyezeti osztályok Midl-fájlokba való kiszervezése (.idl). Így a következő lépésben összevonjuk a CopyFiles.idl, CopyImage.idl, CopyText.idl, HistoryAndRoaming.idl és OtherScenarios.idl tartalmát azáltal, hogy az IDL-t egyetlen, Project.idl nevű fájlba helyezzük át (majd töröljük az eredeti fájlokat).

Miközben ezt tesszük, távolítsuk el az automatikusan létrehozott dummy tulajdonságot (Int32 MyProperty;és annak implementációját) az öt XAML-oldaltípus mindegyikéből.

Először adjon hozzá egy új Midl-fájl (.idl) elemet a C++/WinRT projekthez. Nevezze el a következőképpen: Project.idl. Cserélje le a(z) Project.idl teljes tartalmát a következő kóddal.

// Project.idl
namespace SDKTemplate
{
    [default_interface]
    runtimeclass CopyFiles : Microsoft.UI.Xaml.Controls.Page
    {
        CopyFiles();
    }

    [default_interface]
    runtimeclass CopyImage : Microsoft.UI.Xaml.Controls.Page
    {
        CopyImage();
    }

    [default_interface]
    runtimeclass CopyText : Microsoft.UI.Xaml.Controls.Page
    {
        CopyText();
    }

    [default_interface]
    runtimeclass HistoryAndRoaming : Microsoft.UI.Xaml.Controls.Page
    {
        HistoryAndRoaming();
    }

    [default_interface]
    runtimeclass OtherScenarios : Microsoft.UI.Xaml.Controls.Page
    {
        OtherScenarios();
    }
}

Mint látható, ez csak az egyes .idl fájlok tartalmának másolata, egy névtéren belül, és az egyes futtatókörnyezeti MyProperty osztályokból eltávolítva.

A Visual Studio Megoldáskezelőjében többes kijelöléssel jelölje ki az összes eredeti IDL-fájlt (CopyFiles.idl, CopyImage.idl, CopyText.idl, HistoryAndRoaming.idl és OtherScenarios.idl), majd a Szerkesztés>Eltávolítás paranccsal távolítsa el őket (a párbeszédpanelen válassza a Törlés lehetőséget).

Végül — a(z) MyProperty eltávolításának befejezéséhez — ugyanazon öt XAML-oldaltípus mindegyikének .h és .cpp fájljában törölje a int32_t MyProperty() lekérdező és a void MyProperty(int32_t) beállító függvény deklarációit és definícióit.

Egyébként mindig jó ötlet, ha az XAML-fájlok neve megegyezik az általuk képviselt osztály nevével. Ha például egy XAML-jelölőfájlban x:Class="MyNamespace.MyPage" szerepel, akkor a fájl neve MyPage.xaml legyen. Bár ez nem technikai követelmény, ha nem kell különböző neveket váltania ugyanarra az összetevőre, a projekt érthetőbbé és karbantarthatóbbá válik, és könnyebben kezelhető lesz.

CopyFiles

A C# projektben a CopyFiles XAML oldaltípus implementálva van a CopyFiles.xamlCopyFiles.xaml.cs forráskódfájlokban. Vessünk egy pillantást a CopyFiles egyes tagjaira.

rootPage

Ez egy privát mező.

// CopyFiles.xaml.cs
...
public sealed partial class CopyFiles : Page
{
    MainPage rootPage = MainPage.Current;
    ...
}
...

A C++/WinRT-ben így definiálhatjuk és inicializálhatjuk.

// CopyFiles.h
...
struct CopyFiles : CopyFilesT<CopyFiles>
{
    ...
private:
    SDKTemplate::MainPage rootPage{ MainPage::Current() };
};
...

Ismét (csakúgy, mint a MainPage::current esetében), a CopyFiles::rootPage úgy van deklarálva, hogy a típusa SDKTemplate::MainPage, amely a kivetített típus, nem pedig a megvalósítás típusa.

CopyFiles (a konstruktor)

A C++/WinRT projektben a CopyFiles típus már tartalmaz egy konstruktort, amely tartalmazza a kívánt kódot (csak meghívja az InitializeComponentet).

CopyButton_Click

A C# CopyButton_Click metódus egy eseménykezelő, és az async aláírásában szereplő kulcsszó alapján megállapíthatjuk, hogy a metódus aszinkron módon működik. A C++/WinRT-ben egy aszinkron metódust implementálunk koroutinként. A C++/WinRT egyidejűségének bemutatása a coroutine leírásával együtt: Concurrency and asynchronous operations with C++/WinRT.

Gyakran előfordul, hogy a koroutin befejeződése után további munkát szeretne ütemezni, és ilyen esetekben a koroutin valamilyen aszinkron objektumtípust ad vissza, amely várható, és opcionálisan jelzi a haladást. Ezek a szempontok azonban általában nem vonatkoznak egy eseménykezelőre. Tehát ha van egy eseménykezelője, amely aszinkron műveleteket végez, azt megvalósíthatja olyan korutin formájában, amelynek a visszatérési értéke winrt::fire_and_forget. További információ: Tűz és felejtés.

Bár a fire-and-forget korutin lényege az, hogy nem számít, mikor fejeződik be, a végrehajtás a háttérben továbbra is folyik (vagy fel van függesztve, és a folytatásra vár). A C#-implementációból látható, hogy a CopyButton_Click a this mutatótól függ (eléri a(z) rootPage példányadattagot). Ezért biztosnak kell lennie abban, hogy a this mutató (egy CopyFiles objektumra mutató mutató) túllépi a CopyButton_Click coroutine-t. Olyan helyzetben, mint ez a mintaalkalmazás, ahol a felhasználó a felhasználói felület lapjai között navigál, nem tudjuk közvetlenül szabályozni az oldalak élettartamát. Ha a CopyFiles oldal megsemmisül (ha elnavigálunk róla), miközben a CopyButton_Click még egy háttérszálon fut, nem lesz biztonságos hozzáférni a(z) rootPage-hoz. Ahhoz, hogy a koroutin helyes legyen, erős hivatkozást kell szereznie a this mutatóra, és meg kell őriznie ezt a hivatkozást a koroutin időtartamára. További információ: Erős és gyenge hivatkozások a C++/WinRT alkalmazásban.

Ha megnézi a minta C++/WinRT-verzióját a CopyFiles::CopyButton_Click függvénynél, látni fogja, hogy ezt egy egyszerű verembeli deklarációval oldották meg.

fire_and_forget CopyFiles::CopyButton_Click(IInspectable const&, RoutedEventArgs const&)
{
    auto lifetime{ get_strong() };
    ...
}

Tekintsük át a portolt kód egyéb, figyelemre méltó aspektusait.

A kódban létrehozunk egy FileOpenPicker-objektumot , és két sor múlva hozzáférünk az objektum FileTypeFilter tulajdonságához. A tulajdonság visszatérési típusa karakterláncokból álló IVector felületet valósít meg. És azon a IVectoron meghívjuk a IVector<T>.ReplaceAll(T[]) metódust. Az érdekes szempont az az érték, amelyet a metódusnak adunk át, ahol tömb várható. Íme a kódsor.

filePicker.FileTypeFilter().ReplaceAll({ L"*" });

Az átadott érték ({ L"*" }) egy szabványos C++ inicializáló lista. Ebben az esetben egyetlen objektumot tartalmaz, de az inicializálók listája tetszőleges számú vesszővel tagolt objektumot tartalmazhat. A C++/WinRT azon részei, amelyek lehetővé teszik az inicializálók listájának egy ilyen módszernek való átadását, a Standard inicializáló listákban találhatók.

A C# await -kulcsszót co_await a C++/WinRT fájlba portjuk. Az alábbi példa a kódból származik.

auto storageItems{ co_await filePicker.PickMultipleFilesAsync() };

Ezután vegye figyelembe a C#-kód ezen sorát.

dataPackage.SetStorageItems(storageItems);

A C# implicit módon képes a <, amelyet a storageItems képvisel, a DataPackage.SetStorageItems által elvárt < típussá konvertálni. A C++/WinRT-ben azonban explicit módon át kell alakítanunk az IVectorView<StorageFile-ról>az IIterable<IStorageItem fájlra>. Íme egy újabb példa a as függvény működésére.

dataPackage.SetStorageItems(storageItems.as<IVectorView<IStorageItem>>());

Ahol a kulcsszót c null # nyelven használjuk (például Clipboard.SetContentWithOptions(dataPackage, null)), a C++/WinRT-ben használjuk nullptr (például Clipboard::SetContentWithOptions(dataPackage, nullptr)).

PasteButton_Click

Ez egy másik eseménykezelő, tűz és felejtés koroutin formájában. Tekintsük át a portolt kód figyelemre méltó jellemzőit.

A minta C#-os verziójában a kivételeket a következővel kapjuk el: catch (Exception ex). A portolt C++/WinRT kódban megjelenik a kifejezés catch (winrt::hresult_error const& ex). A winrt::hresult_error és annak használatáról további információt a C++/WinRT hibakezelése című témakörben talál.

Példa annak tesztelésére, hogy egy C#-objektum létezik-e nullif (storageItems != null)vagy sem. A C++/WinRT-ben támaszkodhatunk egy bool konverziós operátorra, amely belsőleg elvégzi a nullptr elleni ellenőrzést.

Íme a minta portolt C++/WinRT-verziójából származó kódtöredék kissé leegyszerűsített verziója.

std::wostringstream output;
output << std::wstring_view(ApplicationData::Current().LocalFolder().Path());

Egy std::wstring_view létrehozása egy winrt::hstring objektumból így jól szemlélteti a hstring::c_str függvény meghívásának egy alternatíváját (vagyis a winrt::hstring C stílusú karakterlánccá alakításához). Ez az alternatíva a hstringstd::wstring_view típusra konvertáló operátorának köszönhetően működik.

Tekintsük a következő C#-kódrészletet.

var file = storageItem as StorageFile;
if (file != null)
...

A C# as -kulcsszó C++/WinRT-re való portolásához eddig a függvényt többször használtuk. Ez a függvény kivételt eredményez, ha a típusátalakítás sikertelen. Ha azonban azt szeretnénk, hogy a konverzió sikertelenség esetén nullptr értéket adjon vissza (hogy ezt az állapotot kezelni tudjuk a kódban), akkor ehelyett a try_as függvényt használjuk.

auto file{ storageItem.try_as<StorageFile>() };
if (file)
...

Másolja ki a CopyFiles portolásának befejezéséhez szükséges XAML-t

Most már kiválaszthatja a fájl teljes tartalmát az CopyFiles.xamlshared eredeti forráskód eredeti letöltési mappájából, és beillesztheti azt a CopyFiles.xaml C++/WinRT projekt fájljába (a fájl meglévő tartalmát lecserélheti a C++/WinRT projektben).

Végül szerkessze a CopyFiles.h és .cpp elemeket, majd törölje a helykitöltő ClickHandler függvényt, mivel most felülírtuk a hozzájuk tartozó XAML-jelölést.

Ezzel befejeztük a CopyFiles portolását, és ha a lépéseket követte, akkor a C++/WinRT-projekt már létrejön és fut, és a CopyFiles-forgatókönyv működőképes lesz.

CopyImage

A CopyImage XAML oldaltípus portolásához ugyanazt a folyamatot kell követnie, mint a CopyFiles esetében. A CopyImage portolása során találkozni fogsz a C# using utasítás használatával, amely biztosítja, hogy az IDisposable interfészt megvalósító objektumok megfelelően fel legyenek szabadítva.

if (imageReceived != null)
{
    using (var imageStream = await imageReceived.OpenReadAsync())
    {
        ... // Pass imageStream to other APIs, and do other work.
    }
}

A C++/WinRT megfelelő felülete IClosable, egyetlen Close metódussal. Itt látható a C#-kód C++/WinRT megfelelője.

if (imageReceived)
{
    auto imageStream{ co_await imageReceived.OpenReadAsync() };
    ... // Pass imageStream to other APIs, and do other work.
    imageStream.Close();
}

A C++/WinRT-objektumok elsősorban az IClosable-t implementálják a determinisztikus véglegesítést nem igénylő nyelvek érdekében. A C++/WinRT determinisztikus véglegesítéssel rendelkezik, ezért gyakran nem kell meghívnunk az IClosable::Close függvényt, amikor C++/WinRT-t írunk. De vannak olyan idők, amikor jó ezt nevezni, és ez az egyik ilyen alkalom. Itt az imageStream-azonosító egy hivatkozásszámozott burkoló egy mögöttes Windows-futtatókörnyezet objektum körül (ebben az esetben az IRandomAccessStreamWithContentType-t implementáló objektum). Bár megállapíthatjuk, hogy az imageStream véglegesítője (destruktora) a befoglaló hatókör végén (a kapcsos zárójelek között) lefut, abban nem lehetünk biztosak, hogy ez a véglegesítő meg fogja hívni a Close metódust. Ennek az az oka, hogy átadtuk az imageStreamet más API-knak, és továbbra is hozzájárulhatnak a mögöttes Windows-futtatókörnyezet objektum hivatkozási számához. Tehát ebben az esetben jó ötlet explicit módon meghívni a Close-t. További információkért lásd: Meg kell hívnom az IClosable::Close metódust az általam használt futtatókörnyezeti osztályokon?.

Ezután fontolja meg a C# kifejezést (uint)(imageDecoder.OrientedPixelWidth * 0.5), amelyet az OnDeferredImageRequestedHandler eseménykezelőben talál. Ez a kifejezés egy uint-t szoroz egy double-gyel, aminek eredménye egy double. Ezután egy uint típusra alakítja azt. A C++/WinRT-ben használhatunk egy hasonló kinézetű C-stílusú öntöttet ((uint32_t)(imageDecoder.OrientedPixelWidth() * 0.5)), de célszerű egyértelművé tenni, hogy pontosan milyen típusú öntöttet szeretnénk, és ebben az esetben ezt static_cast<uint32_t>(imageDecoder.OrientedPixelWidth() * 0.5)tennénk.

A CopyImage.OnDeferredImageRequestedHandler C#-verziója rendelkezik záradékkal finally , de záradékkal catch nem. Kicsit tovább mentünk a C++/WinRT verzióban, és implementáltunk egy catch záradékot, hogy jelezhessük, hogy a késleltetett renderelés sikeres volt-e.

Az XAML-oldal fennmaradó részének portolása nem ad új megvitatandó újdonságokat. Ne felejtse el törölni a próbabábu ClickHandler függvényét. És a CopyFiles-hoz hasonlóan a portolás utolsó lépése az, hogy kijelöli a CopyImage.xaml teljes tartalmát, majd beilleszti azt a C++/WinRT projekt ugyanazon fájljába.

CopyText

A már tárgyalt technikákkal portolhatja a CopyText.xaml és CopyText.xaml.cs elemeket.

HistoryAndRoaming

A HistoryAndRoaming XAML oldaltípusának portolása során bizonyos érdekes pontok merülnek fel.

Először tekintse meg a C# forráskódját, és kövesse az OnNavigatedTo vezérlőfolyamatát az OnHistoryEnabledChanged eseménykezelőn keresztül, végül pedig a CheckHistoryAndRoaming aszinkron függvényt (amely nem vár, tehát lényegében tűz és felejtés). Mivel a CheckHistoryAndRoaming aszinkron, óvatosnak kell lennünk a C++/WinRT fájlban a this mutató élettartamával kapcsolatban. Az eredmény akkor jelenik meg, ha a forráskódfájlban tekinti meg a megvalósítást HistoryAndRoaming.cpp . Először is, amikor delegáltakat kapcsolunk a Clipboard::HistoryEnabledChanged és a Clipboard::RoamingEnabledChanged eseményekhez, csak gyenge hivatkozást veszünk fel a HistoryAndRoaming oldalobjektumhoz. Ezt úgy hajtjuk végre, hogy a winrt::get_weak által visszaadott értéktől függőséggel rendelkező delegáltat hozunk létre az this egérmutatótól való függőség helyett. Ez azt jelenti, hogy maga a delegált, amely végül aszinkron kódot hív meg, nem tartja életben a HistoryAndRoaming oldalt, ha elnavigálunk róla.

Másodszor, amikor végül elérjük a fire-and-forget CheckHistoryAndRoaming korutint, az első dolog, amit teszünk, hogy erős hivatkozást veszünk fel a this elemre annak biztosítására, hogy a HistoryAndRoaming oldal legalább addig életben maradjon, amíg a korutin végre be nem fejeződik. Az imént ismertetett két szempontról további információt a C++/WinRT erős és gyenge hivatkozásaiban talál.

A CheckHistoryAndRoaming portolása során egy másik érdekes pontot talál. A felhasználói felület frissítésére szolgáló kódot tartalmaz; Ezért biztosnak kell lennie abban, hogy ezt a fő felhasználói felületi szálon végezzük. Az eseménykezelőt elsőként meghívó szál a fő felhasználói felületi szál. Az aszinkron metódusok azonban általában tetszőleges szálon futtathatók és/vagy folytathatók. A C#-ban a megoldás a munka elküldése a felhasználói felületi szálra. A C++/WinRT-ben a winrt::resume_foreground függvényt a this mutató DispatcherQueue tagjával együtt használhatjuk a korutin felfüggesztésére és arra, hogy az azonnal a fő felhasználói felület szálán folytatódjon.

A megfelelő kifejezés: co_await winrt::resume_foreground(DispatcherQueue());. A rövidebb verzió a C++/WinRT által biztosított konverziós operátornak köszönhetően érhető el.

Az XAML-oldal fennmaradó részének portolása nem ad új megvitatandó újdonságokat. Ne felejtse el törölni a helykitöltő ClickHandler függvényt, és átmásolni az XAML-jelölést.

Egyéb forgatókönyvek

A már tárgyalt technikák segítségével átültetheti a OtherScenarios.xaml és OtherScenarios.xaml.cs elemeket.

Conclusion

Remélhetőleg ez az útmutató elegendő információval és technikával vértezte fel ahhoz, hogy most már nekilásson saját C#-alkalmazásai C++/WinRT-re való portolásának. Emlékeztetőül továbbra is visszautalhat a előtti (C#) és utáni (C++/WinRT) verziókra a forráskódban, a Vágólap mintában, és egymás mellett összehasonlítva láthatja a megfeleltetést.