Verplaatsen naar C++/WinRT van C#

Hint

Als u dit onderwerp eerder hebt gelezen en u teruggaat naar het onderwerp met een bepaalde taak in gedachten, kunt u naar de inhoud Zoeken gaan op basis van de taak die u uitvoert in dit onderwerp.

Dit onderwerp biedt een uitgebreid overzicht van de technische details die komen kijken bij het overzetten van de broncode van een C#-project naar de equivalente code in C++/WinRT.

Voor een casestudy van het overzetten van een van de Universal Windows Platform (UWP) app-voorbeelden raadpleegt u het bijbehorende onderwerp Het Klembord-voorbeeld overzetten naar C++/WinRT van C#. U kunt oefenen met het overzetten en ervaring opdoen door die stapsgewijze handleiding te volgen en onderweg zelf het voorbeeld over te zetten.

Voorbereiden en wat u kunt verwachten

De casestudy Het Clipboard-voorbeeld overzetten van C# naar C++/WinRT illustreert welke ontwerpkeuzes voor software u moet maken bij het overzetten van een project naar C++/WinRT. Het is dus een goed idee om u voor te bereiden op porting door een goed inzicht te krijgen in de werking van de bestaande code. Op die manier krijgt u een goed overzicht van de functionaliteit van de app en de structuur van de code, en zorgen de beslissingen die u vervolgens neemt er altijd voor dat u vooruitgaat en de juiste richting op gaat.

In termen van wat voor soort overdrachtswijzigingen u kunt verwachten, kunt u deze groeperen in vier categorieën.

  • De taalprojectie overzetten. De Windows Runtime (WinRT) wordt in verschillende programmeertalen geprojecteerd. Elk van deze taalprojecties is ontworpen om idiomatisch te voelen voor de betreffende programmeertaal. Voor C# worden sommige Windows Runtime typen geprojecteerd als .NET typen. U vertaalt bijvoorbeeld System.Collections.Generic.IReadOnlyList<T> terug naar Windows. Foundation.Collections.IVectorView<T>. Ook in C# worden sommige Windows Runtime bewerkingen geprojecteerd als handige C#-taalfuncties. Een voorbeeld is dat u in C# de syntaxis van de += operator gebruikt om een gemachtigde voor gebeurtenisafhandeling te registreren. U vertaalt dus taalfuncties zoals die terug naar de fundamentele bewerking die wordt uitgevoerd (gebeurtenisregistratie in dit voorbeeld).
  • Syntaxis van poorttaal. Veel van deze wijzigingen zijn eenvoudige mechanische transformaties, waarbij het ene symbool voor het andere wordt vervangen. Bijvoorbeeld: een punt (.) veranderen in een dubbele punt (::).
  • Taalprocedure voor de poort. Sommige hiervan kunnen eenvoudige, herhaalde wijzigingen zijn (zoals myObject.MyProperty naar myObject.MyProperty()). Anderen hebben diepere wijzigingen nodig (bijvoorbeeld het overzetten van een procedure waarbij System.Text.StringBuilder wordt gebruikt voor een procedure waarbij std::wostringstream wordt gebruikt).
  • Overdrachtsgerelateerde taken die specifiek zijn voor C++/WinRT. Bepaalde details van de Windows Runtime worden impliciet verzorgd door C#, achter de schermen. Deze details worden expliciet uitgevoerd in C++/WinRT. Een voorbeeld is dat u een .idl bestand gebruikt om uw runtimeklassen te definiëren.

Na de op taken gebaseerde index die volgt, zijn de rest van de secties in dit onderwerp gestructureerd volgens de bovenstaande taxonomie.

Inhoud zoeken op basis van de taak die u uitvoert

Task Content
Een Windows Runtime-onderdeel maken (WRC) Bepaalde functionaliteit kan alleen worden bereikt (of bepaalde API's die worden aangeroepen) met C++. U kunt deze functionaliteit in een C++/WinRT WRC inschakelen en vervolgens de WRC gebruiken vanuit (bijvoorbeeld) een C#-app. Zie Windows Runtime onderdelen met C++/WinRT en als u een runtimeklasse in een Windows Runtime-onderdeel maakt.
Een asynchrone methode overzetten Het is een goed idee dat de eerste regel van een asynchrone methode in een C++/WinRT-runtimeklasse auto lifetime = get_strong(); is (zie Veilig toegang krijgen tot de this-pointer in een coroutine van een klassenlid).

Porteren vanuit Task, zie Asynchrone actie.
Porteren vanuit Task<T>, zie asynchrone bewerking.
Migreren vanuit async void, zie fire-and-forget-methode.
Een klasse overzetten Bepaal eerst of de klasse een runtimeklasse moet zijn of dat deze een gewone klasse kan zijn. Zie het begin van author-API's met C++/WinRT om u te helpen dat te bepalen. Bekijk vervolgens de volgende drie rijen hieronder.
Een runtimeklasse overzetten Een klasse die functionaliteit deelt buiten de C++-app of een klasse die wordt gebruikt in XAML-gegevensbinding. Kijk of u een runtimeklasse in een Windows Runtime-onderdeel maakt of als u een runtimeklasse ontwerpt waarnaar moet worden verwezen in uw XAML-gebruikersinterface.

Deze koppelingen beschrijven dit in meer detail, maar een runtimeklasse moet worden gedeclareerd in IDL. Als uw project al een IDL-bestand bevat (bijvoorbeeldProject.idl), raden we u aan een nieuwe runtimeklasse in dat bestand te declareren. Declareer in IDL alle methoden en gegevensleden die buiten uw app worden gebruikt of die worden gebruikt in XAML. Nadat u het IDL-bestand hebt bijgewerkt, bouwt u het opnieuw en bekijkt u de gegenereerde stub-bestanden (.hen .cpp) in de map van Generated Files uw project (in Solution Explorer, waarbij het projectknooppunt is geselecteerd, controleert u of Alle bestanden weergeven is ingeschakeld). Vergelijk de stub-bestanden met de bestanden die al in uw project aanwezig zijn, voeg zo nodig bestanden toe of voeg functiehandtekeningen toe of werk deze bij. Stub-bestandssyntaxis is altijd correct, dus we raden u aan deze te gebruiken om buildfouten te minimaliseren. Zodra de stubs in uw project overeenkomen met die in de stub-bestanden, kunt u deze implementeren door de C#-code over te zetten.
Een gewone klasse overzetten Zie Als u geen runtime-klasse schrijft.
Auteur IDL Inleiding tot Microsoft Interface Definition Language 3.0
Als u een runtime-klasse maakt waarnaar wordt verwezen in uw XAML-gebruikersinterface
Objecten uit XAML-opmaak gebruiken
Uw runtimeklassen definiëren in IDL
Een verzameling overzetten Verzamelingen met C++/WinRT
Een gegevensbron beschikbaar maken voor XAML-markeringen
Associatieve container
Vectorlidtoegang
Een gebeurtenis overzetten Eventhandler-delegate als lid van een klasse
Event-handlerdelegate intrekken
Een methode overzetten Vanuit C#: private async void SampleButton_Tapped(object sender, Microsoft.UI.Xaml.Input.TappedRoutedEventArgs e) { ... }
Naar het C++/WinRT-bestand .h : fire_and_forget SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&);
Naar het C++/WinRT-bestand .cpp : fire_and_forget OcrFileImage::SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&) {...}
Poorttekenreeksen Verwerking van tekenreeksen in C++/WinRT
ToString
Strings samenstellen
Boksen en het uitpakken van een tekenreeks opheffen
Typeconversie (type casting) C#: o.ToString()
C++/WinRT: to_hstring(static_cast<int>(o))
Zie ook ToString.

C#: (Value)o
C++/WinRT: unbox_value<Value>(o)
Werpt als het uitpakken mislukt. Zie ook Boksen en uitpakken.

C#: o as Value? ?? fallback
C++/WinRT: unbox_value_or<Value>(o, fallback)
Geeft de fallbackwaarde terug als het unboxen mislukt. Zie ook Inpakken en uitpakken.

C#: (Class)o
C++/WinRT: o.as<Class>()
Werpt als de conversie mislukt.

C#: o as Class
C++/WinRT: o.try_as<Class>()
Retourneert null als de conversie mislukt.

Wijzigingen die betrekking hebben op de taalprojectie

Category C# C++/WinRT Zie ook
Object zonder type objectof System.Object Windows::Foundation::IInspectable De methode EnableClipboardContentChangedNotifications overzetten
Projectienaamruimten using System; using namespace Windows::Foundation;
using System.Collections.Generic; using namespace Windows::Foundation::Collections;
Grootte van een verzameling collection.Count collection.Size() De methode BuildClipboardFormatsOutputString overzetten
Typisch verzamelingstype IList<T> en Toevoegen om een element toe te voegen. IVector<T> en Toevoegen om een element toe te voegen. Als u ergens een std::vector gebruikt, push_back om een element toe te voegen.
Verzamelingstype alleen-lezen IReadOnlyList<T> IVectorView<T> De methode BuildClipboardFormatsOutputString overzetten
Eventhandler-delegate als lid van een klasse myObject.EventName += Handler; token = myObject.EventName({ get_weak(), &Class::Handler }); De methode EnableClipboardContentChangedNotifications overzetten
Eventhandler-delegate intrekken myObject.EventName -= Handler; myObject.EventName(token); De methode EnableClipboardContentChangedNotifications overzetten
Associatieve verzameling IDictionary<K, V> IMap<K, V>
Vectorlidtoegang x = v[i];
v[i] = x;
x = v.GetAt(i);
v.SetAt(i, x);

Een gebeurtenis-handler registreren/intrekken

In C++/WinRT hebt u verschillende syntactische opties voor het registreren/intrekken van een gemachtigde voor een gebeurtenis-handler, zoals beschreven in Handle events by using delegates in C++/WinRT. Zie ook de methode EnableClipboardContentChangedNotifications overzetten.

Soms wilt u bijvoorbeeld, wanneer een gebeurtenisontvanger (een object dat een gebeurtenis afhandelt) op het punt staat te worden vernietigd, een gebeurtenisafhandelaar verwijderen zodat de gebeurtenisbron (het object dat de gebeurtenis activeert) geen vernietigd object meer aanroept. Zie Een geregistreerde gemachtigde intrekken. In dergelijke gevallen maakt u voor uw gebeurtenisverwerkers een lidvariabele event_token aan. Zie voor een voorbeeld De methode EnableClipboardContentChangedNotifications overzetten.

U kunt ook een gebeurtenis-handler registreren in XAML-markeringen.

<Button x:Name="OpenButton" Click="OpenButton_Click" />

In C# kan uw OpenButton_Click methode privé zijn en kan XAML deze nog steeds verbinden met de ButtonBase.Click-gebeurtenis die door OpenButton wordt gegenereerd.

In C++/WinRT moet uw OpenButton_Click methode openbaar zijn in uw implementatietypeals u deze wilt registreren in XAML-markeringen. Als u een gebeurtenis-handler alleen registreert in imperatieve code, hoeft de gebeurtenis-handler niet openbaar te zijn.

namespace winrt::MyProject::implementation
{
    struct MyPage : MyPageT<MyPage>
    {
        void OpenButton_Click(
            winrt::Windows::Foundation::IInspectable const& sender,
            winrt::Microsoft::UI::Xaml::RoutedEventArgs const& args);
    }
};

U kunt ook de registrerende XAML-pagina een vriend van uw implementatietype maken en OpenButton_Click privé.

namespace winrt::MyProject::implementation
{
    struct MyPage : MyPageT<MyPage>
    {
    private:
        friend MyPageT;
        void OpenButton_Click(
            winrt::Windows::Foundation::IInspectable const& sender,
            winrt::Microsoft::UI::Xaml::RoutedEventArgs const& args);
    }
};

Een laatste scenario is dat het C#-project dat u overzet bindt aan de eventhandler in de markup (meer achtergrond over dat scenario vindt u in Functions in x:Bind).

<Button x:Name="OpenButton" Click="{x:Bind OpenButton_Click}" />

U kunt die markering gewoon wijzigen in de eenvoudigere Click="OpenButton_Click". Of, als u wilt, kunt u die markeringen behouden zoals het is. Het enige wat u hoeft te doen om dit te ondersteunen, is de gebeurtenisafhandelaar in IDL declareren.

void OpenButton_Click(Object sender, Microsoft.UI.Xaml.RoutedEventArgs e);

Note

Declareer de functie als void zelfs als je die implementeert als Fire and forget.

Wijzigingen die betrekking hebben op de taalsyntaxis

Category C# C++/WinRT Zie ook
Toegangsaanpassingen public \<member\> public:
    \<member\>
De Button_Click-methode overzetten
Toegang tot een gegevenslid this.variable this->variable  
Asynchrone actie async Task ... IAsyncAction ... IAsyncAction-interface, gelijktijdigheid en asynchrone bewerkingen met C++/WinRT
Asynchrone bewerking async Task<T> ... IAsyncOperation<T> ... IAsyncOperation-interface, gelijktijdigheid en asynchrone bewerkingen met C++/WinRT
Fire-and-forget-methode (impliceert asynchrone uitvoering) async void ... winrt::fire_and_forget ... De CopyButton_Click methode overzetten, Fire en vergeet
Toegang krijgen tot een geïnventariseerd constante E.Value E::Value De methode DisplayChangedFormats overzetten
Coöperatief wachten await ... co_await ... De CopyButton_Click-methode overzetten
Verzameling van projectietypen als een privéveld private List<MyRuntimeClass> myRuntimeClasses = new List<MyRuntimeClass>(); std::vector
<MyNamespace::MyRuntimeClass>
m_myRuntimeClasses;
GUID-constructie private static readonly Guid myGuid = new Guid("C380465D-2271-428C-9B83-ECEA3B4A85C1"); winrt::guid myGuid{ 0xC380465D, 0x2271, 0x428C, { 0x9B, 0x83, 0xEC, 0xEA, 0x3B, 0x4A, 0x85, 0xC1} };
Scheidingsteken van naamruimte A.B.T A::B::T
Null null nullptr De methode UpdateStatus overzetten
Een typeobject verkrijgen typeof(MyType) winrt::xaml_typename<MyType>() De eigenschap Scenario's overzetten
Parameterdeclaratie voor een methode MyType MyType const& Parameteroverdracht
Parameterdeclaratie voor een asynchrone methode MyType MyType Parameteroverdracht
Een statische methode aanroepen T.Method() T::Method()
Tekenreeksen stringof System.String winrt::hstring Verwerking van tekenreeksen in C++/WinRT
Letterlijke tekenreeks "a string literal" L"a string literal" Het overzetten van de constructor, Current en FEATURE_NAME
Afgeleid (of gededuceerd) type var auto De methode BuildClipboardFormatsOutputString overzetten
Using-directive using A.B.C; using namespace A::B::C; De constructor, huidige en FEATURE_NAME overzetten
letterlijke/onbewerkte stringliteral @"verbatim string literal" LR"(raw string literal)" De DisplayToast-methode overzetten

Note

Als een headerbestand geen instructie voor een bepaalde naamruimte bevat using namespace , moet u alle typenamen voor die naamruimte volledig in aanmerking nemen of ten minste voldoende kwalificeren voor de compiler om ze te vinden. Zie voor een voorbeeld de methode DisplayToast overzetten.

Klassen en leden overzetten

U moet voor elk C#-type bepalen of u het wilt overzetten naar een Windows Runtime type of naar een reguliere C++-klasse/struct/opsomming. Zie Het Klembord-voorbeeld overzetten naar C++/WinRT van C# voor meer informatie en gedetailleerde voorbeelden die illustreren hoe u deze beslissingen kunt nemen.

Een C#-eigenschap wordt doorgaans omgezet in een accessorfunctie, een mutatorfunctie en een onderliggend gegevenslid. Zie De eigenschap IsClipboardContentChangedEnabled overzetten voor meer informatie en een voorbeeld.

Voor niet-statische velden moet u gegevensleden van uw implementatietype maken.

Een statisch C#-veld wordt een statische C++/WinRT-toegangsfunctie en/of mutatorfunctie. Zie De constructor, Current en FEATURE_NAME overzetten voor meer informatie, en een voorbeeld.

Voor lidfuncties moet u voor elke functie beslissen of deze deel uitmaakt van de IDL, of het een openbare of persoonlijke lidfunctie van uw implementatietype is. Zie IDL voor het type MainPage voor meer informatie en voorbeelden van hoe u kunt beslissen.

Het overzetten van XAML-opmaak en assetbestanden

In het geval van het overzetten van het Klembord-voorbeeld naar C++/WinRT vanuit C#, konden we dezelfde XAML-markeringen (inclusief resources) en assetbestanden in de C# en het C++/WinRT-project gebruiken. In sommige gevallen zijn bewerkingen voor markeringen nodig om dat te bereiken. Zie Kopiëren van de XAML en stijlen die nodig zijn om de overdracht van MainPage te voltooien.

Wijzigingen die betrekking hebben op procedures in de taal

Category C# C++/WinRT Zie ook
Levensduurbeheer in een asynchrone methode N/A auto lifetime{ get_strong() }; of
auto lifetime = get_strong();
De CopyButton_Click-methode overzetten
Afstoting using (var t = v) auto t{ v };
t.Close(); // or let wrapper destructor do the work
De methode CopyImage overzetten
Object maken new MyType(args) MyType{ args } of
MyType(args)
De eigenschap Scenario's overzetten
Niet-geïnitialiseerde verwijzing maken MyType myObject; MyType myObject{ nullptr }; of
MyType myObject = nullptr;
Overzetten van de constructor, Current en FEATURE_NAME
Object samenstellen in een variabele met args var myObject = new MyType(args); auto myObject{ MyType{ args } }; of
auto myObject{ MyType(args) }; of
auto myObject = MyType{ args }; of
auto myObject = MyType(args); of
MyType myObject{ args }; of
MyType myObject(args);
De Footer_Click-methode overzetten
Object maken in variabele zonder argumenten var myObject = new T(); MyType myObject; De methode BuildClipboardFormatsOutputString overzetten
Verkorte initialisatie van objecten var p = new FileOpenPicker{
    ViewMode = PickerViewMode.List
};
FileOpenPicker p;
p.ViewMode(PickerViewMode::List);
Bulkvectorbewerking var p = new FileOpenPicker{
    FileTypeFilter = { ".png", ".jpg", ".gif" }
};
FileOpenPicker p;
p.FileTypeFilter().ReplaceAll({ L".png", L".jpg", L".gif" });
De CopyButton_Click-methode overzetten
Itereren over een verzameling foreach (var v in c) for (auto&& v : c) De methode BuildClipboardFormatsOutputString overzetten
Een uitzondering vangen catch (Exception ex) catch (winrt::hresult_error const& ex) De methode PasteButton_Click overzetten
Details van uitzondering ex.Message ex.message() De methode PasteButton_Click overzetten
Een eigenschapswaarde ophalen myObject.MyProperty myObject.MyProperty() De methode NotifyUser overzetten
Een eigenschapswaarde instellen myObject.MyProperty = value; myObject.MyProperty(value);
Een eigenschapswaarde verhogen myObject.MyProperty += v; myObject.MyProperty(thing.Property() + v);
Gebruik voor tekenreeksen een builder
ToString() myObject.ToString() winrt::to_hstring(myObject) ToString()
Taaltekenreeks naar Windows Runtime-tekenreeks N/A winrt::hstring{ s }
Tekenreeks bouwen StringBuilder builder;
builder.Append(...);
std::wostringstream builder;
builder << ...;
Strings samenstellen
Tekenreeksinterpolatie $"{i++}) {s.Title}" winrt::to_hstring en/of winrt::hstring::operator+ De methode OnNavigatedTo overzetten
Lege tekenreeks voor vergelijking System.String.Empty winrt::hstring::empty De methode UpdateStatus overzetten
Lege tekenreeks maken var myEmptyString = String.Empty; winrt::hstring myEmptyString{ L"" };
Woordenlijstbewerkingen map[k] = v; // replaces any existing
v = map[k]; // throws if not present
map.ContainsKey(k)
map.Insert(k, v); // replaces any existing
v = map.Lookup(k); // throws if not present
map.HasKey(k)
Typeconversie (exceptie genereren bij mislukking) (MyType)v v.as<MyType>() De Footer_Click-methode overzetten
Typeconversie (null bij fout) v as MyType v.try_as<MyType>() De methode PasteButton_Click overzetten
XAML-elementen met x:Name zijn eigenschappen MyNamedElement MyNamedElement() Overzetten van de constructor, Current en FEATURE_NAME
Overschakelen naar de ui-thread CoreDispatcher.RunAsync DispatcherQueue.TryEnqueue, of winrt::resume_foreground De methode NotifyUser overzetten en de methode HistoryAndRoaming overzetten
Ui-elementconstructie in imperatieve code op een XAML-pagina Zie ui-elementconstructie Zie ui-elementconstructie

In de volgende secties wordt dieper ingegaan op een aantal items in de tabel.

Opbouw van UI-elementen

Deze codevoorbeelden tonen de constructie van een UI-element in de imperatieve code van een XAML-pagina.

var myTextBlock = new TextBlock()
{
    Text = "Text",
    Style = (Microsoft.UI.Xaml.Style)this.Resources["MyTextBlockStyle"]
};
TextBlock myTextBlock;
myTextBlock.Text(L"Text");
myTextBlock.Style(
    winrt::unbox_value<Microsoft::UI::Xaml::Style>(
        Resources().Lookup(
            winrt::box_value(L"MyTextBlockStyle")
        )
    )
);

ToString()

C#-typen bieden de methode Object.ToString .

int i = 2;
var s = i.ToString(); // s is a System.String with value "2".

C++/WinRT biedt deze faciliteit niet rechtstreeks, maar u kunt ook alternatieven gebruiken.

int i{ 2 };
auto s{ std::to_wstring(i) }; // s is a std::wstring with value L"2".

C++/WinRT ondersteunt ook winrt::to_hstring voor een beperkt aantal typen. U moet overbelastingen toevoegen voor eventuele extra typen die u wilt stringifyen.

Language Stringify int Enum naar tekenreeks converteren
C# string result = "hello, " + intValue.ToString();
string result = $"hello, {intValue}";
string result = "status: " + status.ToString();
string result = $"status: {status}";
C++/WinRT hstring result = L"hello, " + to_hstring(intValue); // must define overload (see below)
hstring result = L"status: " + to_hstring(status);

Als u een enum naar een tekenreeks converteert, moet u de implementatie van winrt::to_hstring aanleveren.

namespace winrt
{
    hstring to_hstring(StatusEnum status)
    {
        switch (status)
        {
        case StatusEnum::Success: return L"Success";
        case StatusEnum::AccessDenied: return L"AccessDenied";
        case StatusEnum::DisabledByPolicy: return L"DisabledByPolicy";
        default: return to_hstring(static_cast<int>(status));
        }
    }
}

Deze tekenreeksrepresentaties worden vaak impliciet verwerkt door gegevensbinding.

<TextBlock>
You have <Run Text="{Binding FlowerCount}"/> flowers.
</TextBlock>
<TextBlock>
Most recent status is <Run Text="{x:Bind LatestOperation.Status}"/>.
</TextBlock>

Met deze bindingen wordt winrt::to_hstring van de afhankelijke eigenschap uitgevoerd. In het tweede voorbeeld ( statusenum) moet u uw eigen overbelasting van winrt::to_hstring opgeven, anders krijgt u een compilerfout.

Zie ook De methode Footer_Click porteren.

Tekenreeks bouwen

Voor het samenstellen van tekenreeksen heeft C# een ingebouwd StringBuilder-type.

Category C# C++/WinRT
Tekenreeks bouwen StringBuilder builder;
builder.Append(...);
std::wostringstream builder;
builder << ...;
Een Windows Runtime-tekenreeks toevoegen, waarbij null-tekens behouden blijven builder.Append(s); builder << std::wstring_view{ s };
Een nieuwe regel toevoegen builder.Append(Environment.NewLine); builder << std::endl;
Het resultaat openen s = builder.ToString(); ws = builder.str();

Zie ook de methode BuildClipboardFormatsOutputString overzetten en de methode DisplayChangedFormats overzetten.

Code uitvoeren op de hoofd-UI-thread

Dit voorbeeld wordt genomen uit het voorbeeld van de streepjescodescanner.

Als u wilt werken aan de hoofd-UI-thread in een C#-project, gebruikt u doorgaans de methode DispatcherQueue.TryEnqueue (of de oudere CoreDispatcher.RunAsync in UWP). Het patroon ziet er als volgt uit in C#.

private async void Watcher_Added(DeviceWatcher sender, DeviceInformation args)
{
    DispatcherQueue.TryEnqueue(() =>
    {
        // Do work on the main UI thread here.
    });
}

Het is veel eenvoudiger om dat in C++/WinRT uit te drukken. Merk op dat we parameters op basis van waarde doorgeven, ervan uitgaande dat we er na het eerste onderbrekingspunt toegang toe willen hebben (de co_await, in dit geval). Zie Parameter-passing voor meer informatie.

winrt::fire_and_forget Watcher_Added(DeviceWatcher sender, winrt::DeviceInformation args)
{
    co_await DispatcherQueue();
    // Do work on the main UI thread here.
}

Als u het werk met een andere prioriteit dan de standaardprioriteit moet uitvoeren, raadpleegt u de functie winrt::resume_foreground, die een overload heeft die een prioriteit accepteert. Zie Programmeren met threadaffiniteit in gedachten voor codevoorbeelden die laten zien hoe u wacht op een aanroep naar winrt::resume_foreground.

Runtimeklassen definiëren in IDL

Zie IDL voor het type MainPage en voeg uw .idl bestanden samen.

Neem de C++/WinRT-Windows naamruimteheaderbestanden op die u nodig hebt

Wanneer u in C++/WinRT een type uit een Windows naamruimten wilt gebruiken, moet u het bijbehorende C++/WinRT-Windows naamruimteheaderbestand opnemen. Zie De methode NotifyUser overzetten voor een voorbeeld.

Verpakken en uitpakken

C# vakken automatisch scalaire waarden in objecten. C++/WinRT vereist dat u de winrt::box_value-functie expliciet aanroept. Voor beide talen moet u het selectievakje expliciet uitvinken. Zie Boksen en uitpakken met C++/WinRT.

In de volgende tabellen gebruiken we deze definities.

C# C++/WinRT
int i; int i;
string s; winrt::hstring s;
object o; IInspectable o;
Operation C# C++/WinRT
Boksen o = 1;
o = "string";
o = box_value(1);
o = box_value(L"string");
Uitpakken i = (int)o;
s = (string)o;
i = unbox_value<int>(o);
s = unbox_value<winrt::hstring>(o);

C++/CX en C# werpen een uitzondering op als u probeert een null-pointer te unboxen naar een waardetype. C++/WinRT beschouwt deze als een programmeerfout en loopt vast. Gebruik in C++/WinRT de functie winrt::unbox_value_or als u de case wilt afhandelen waarin het object niet van het type is dat u dacht dat het was.

Scenario C# C++/WinRT
Een bekend geheel getal uitpakken i = (int)o; i = unbox_value<int>(o);
Als o null is System.NullReferenceException Ongeluk
Als o geen geboxte int is System.InvalidCastException Ongeluk
Pak int uit, gebruik de fallback als deze null is; crash in alle andere gevallen i = o != null ? (int)o : fallback; i = o ? unbox_value<int>(o) : fallback;
Unbox int indien mogelijk; gebruik anders een fallback voor alle andere gevallen i = as int? ?? fallback; i = unbox_value_or<int>(o, fallback);

Zie voor een voorbeeld de methode OnNavigatedTo overzetten en de methode Footer_Click overzetten.

Boxing en unboxing van een tekenreeks

Een tekenreeks is in sommige opzichten een waardetype en in andere opzichten een referentietype. C# en C++/WinRT behandelen tekenreeksen anders.

Het ABI-type HSTRING is een aanwijzer naar een tekenreeks die wordt geteld. Maar het is niet afgeleid van IInspectable, dus het is technisch geen object. Bovendien vertegenwoordigt een null-HSTRING de lege tekenreeks. Boksen van dingen die niet zijn afgeleid van IInspectable wordt uitgevoerd door ze in een IReference<T> te verpakken en de Windows Runtime biedt een standaard implementatie in de vorm van het PropertyValue-object (aangepaste typen worden gerapporteerd als PropertyType::OtherType).

C# vertegenwoordigt een Windows Runtime tekenreeks als referentietype; terwijl C++/WinRT een tekenreeks projecteert als een waardetype. Dit betekent dat een in een vak geplaatste null-tekenreeks verschillende weergaven kan hebben, afhankelijk van hoe u daar bent gekomen.

Behavior C# C++/WinRT
Verklaringen object o;
string s;
IInspectable o;
hstring s;
Categorie van het tekenreekstype Verwijzingstype Waardetype
null HSTRING projecteert als "" hstring{}
Zijn null en "" identiek? No Yes
Geldigheid van null s = null;
s.Length genereert NullReferenceException
s = hstring{};
s.size() == 0 (geldig)
Als u null-tekenreeks toewijst aan object o = (string)null;
o == null
o = box_value(hstring{});
o != nullptr
Als u "" aan een object toewijst o = "";
o != null
o = box_value(hstring{L""});
o != nullptr

Basisprincipes van boxing en unboxing.

Operation C# C++/WinRT
Plaats een kader om een tekenreeks o = s;
Een lege tekenreeks wordt een object dat niet null is.
o = box_value(s);
Een lege tekenreeks wordt een object dat niet null is.
Een bekende tekenreeks uitpakken s = (string)o;
Null-object wordt null-tekenreeks.
InvalidCastException als het geen string is.
s = unbox_value<hstring>(o);
Null-object loopt vast.
Crash als het geen tekenreeks is.
Een mogelijke tekenreeks uitpakken s = o as string;
Een null-object of geen tekenreeks wordt een null-tekenreeks.

OR

s = o as string ?? fallback;
Null of niet-tekenreeks wordt terugval.
De lege tekenreeks is behouden.
s = unbox_value_or<hstring>(o, fallback);
Null of niet-tekenreeks wordt terugval.
De lege tekenreeks is behouden.

Een klasse beschikbaar maken voor de markeringsextensie {Binding}

Als u de markeringsextensie {Binding} wilt gebruiken om gegevens te binden aan uw gegevenstype, raadpleegt u Bindingsobject dat is gedeclareerd met {Binding}.

Objecten uit XAML-opmaak gebruiken

In een C#-project kunt u privéleden en benoemde elementen uit XAML-markeringen gebruiken. Maar in C++/WinRT moeten alle entiteiten die worden gebruikt met behulp van de XAML -markeringsextensie {x:Bind} openbaar worden weergegeven in IDL.

Ook wordt bij binding aan een Booleaanse waarde in C# true of false weergegeven, maar in C++/WinRT wordt Windows.Foundation.IReference`1<Boolean> weergegeven.

Zie Objecten in markup gebruiken voor meer informatie en codevoorbeelden.

Een gegevensbron beschikbaar maken voor XAML-markeringen

In C++/WinRT versie 2.0.190530.8 of hoger maakt winrt::single_threaded_observable_vector een waarneembare vector die zowel IObservableVectorT< als IObservableVector ><IInspectable> ondersteunt. Zie De eigenschap Scenario's overzetten voor een voorbeeld.

U kunt uw Midl-bestand (.idl) als volgt opstellen (zie ook Runtime-klassen onderbrengen in Midl-bestanden (.idl)).

namespace Bookstore
{
    runtimeclass BookSku { ... }

    runtimeclass BookstoreViewModel
    {
        Windows.Foundation.Collections.IObservableVector<BookSku> BookSkus{ get; };
    }

    runtimeclass MainPage : Microsoft.UI.Xaml.Controls.Page
    {
        MainPage();
        BookstoreViewModel MainViewModel{ get; };
    }
}

En implementeer zo.

// BookstoreViewModel.h
...
struct BookstoreViewModel : BookstoreViewModelT<BookstoreViewModel>
{
    BookstoreViewModel()
    {
        m_bookSkus = winrt::single_threaded_observable_vector<Bookstore::BookSku>();
        m_bookSkus.Append(winrt::make<Bookstore::implementation::BookSku>(L"To Kill A Mockingbird"));
    }
    
	Windows::Foundation::Collections::IObservableVector<Bookstore::BookSku> BookSkus();
    {
        return m_bookSkus;
    }

private:
    Windows::Foundation::Collections::IObservableVector<Bookstore::BookSku> m_bookSkus;
};
...

Zie besturingselementen voor XAML-items voor meer informatie; bind aan een C++/WinRT-verzameling en verzamelingen met C++/WinRT.

Een gegevensbron beschikbaar maken voor XAML-opmaak (vóór C++/WinRT 2.0.190530.8)

XAML-gegevensbinding vereist dat een itemsbron IIterable<IInspectable> implementeert, evenals een van de volgende combinaties van interfaces.

  • IObservableVector<IInspectable>
  • IBindableVector en INotifyCollectionChanged
  • IBindableVector en IBindableObservableVector
  • IBindableVector zelf (reageert niet op wijzigingen)
  • IVector<IInspectable>
  • IBindableIterable (zal over elementen itereren en deze opslaan in een privécollectie)

Een algemene interface, zoals IVector<T> , kan tijdens runtime niet worden gedetecteerd. Elke IVector<T> heeft een andere interface-id (IID), een functie van T. Elke ontwikkelaar kan de set T willekeurig uitbreiden, zodat de XAML-bindingscode nooit de volledige set kan kennen waarvoor een query moet worden uitgevoerd. Deze beperking is geen probleem voor C# omdat elk CLR-object dat IEnumerable<T implementeert, IEnumerable T> automatisch IEnumerable implementeert. Op ABI-niveau betekent dit dat elk object dat IObservableVector T< implementeert, automatisch IObservableVector><IInspectable> implementeert.

C++/WinRT biedt geen garantie. Als een C++/WinRT-runtimeklasse IObservableVector<T> implementeert, kunnen we er niet van uitgaan dat een implementatie van IObservableVector<IInspectable> ook is opgegeven.

Daarom moet het vorige voorbeeld er als volgt uitzien.

...
runtimeclass BookstoreViewModel
{
    // This is really an observable vector of BookSku.
    Windows.Foundation.Collections.IObservableVector<Object> BookSkus{ get; };
}

En de implementatie.

// BookstoreViewModel.h
...
struct BookstoreViewModel : BookstoreViewModelT<BookstoreViewModel>
{
    BookstoreViewModel()
    {
        m_bookSkus = winrt::single_threaded_observable_vector<Windows::Foundation::IInspectable>();
        m_bookSkus.Append(winrt::make<Bookstore::implementation::BookSku>(L"To Kill A Mockingbird"));
    }
    
    // This is really an observable vector of BookSku.
	Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> BookSkus();
    {
        return m_bookSkus;
    }

private:
    Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> m_bookSkus;
};
...

Als u toegang wilt krijgen tot objecten in m_bookSkus, moet u ze weer terugzetten naar bookstore::BookSku.

Widget MyPage::BookstoreViewModel(winrt::hstring title)
{
    for (auto&& obj : m_bookSkus)
    {
        auto bookSku = obj.as<Bookstore::BookSku>();
        if (bookSku.Title() == title) return bookSku;
    }
    return nullptr;
}

Afgeleide klassen

Om van een runtime-klasse te kunnen overerven, moet de basisklasse samenstelbaar zijn. C# vereist niet dat u speciale stappen uitvoert om uw klassen composable te maken, maar C++/WinRT wel. U gebruikt het niet-verzegelde trefwoord om aan te geven dat uw klasse bruikbaar moet zijn als basisklasse.

unsealed runtimeclass BasePage : Microsoft.UI.Xaml.Controls.Page
{
    ...
}
runtimeclass DerivedPage : BasePage
{
    ...
}

In het headerbestand voor uw implementatietype moet u het headerbestand van de basisklasse opnemen voordat u de automatisch gegenereerde header voor de afgeleide klasse opneemt. Anders krijgt u fouten zoals 'Ongeldig gebruik van dit type als een expressie'.

// DerivedPage.h
#include "BasePage.h"       // This comes first.
#include "DerivedPage.g.h"  // Otherwise this header file will produce an error.

namespace winrt::MyNamespace::implementation
{
    struct DerivedPage : DerivedPageT<DerivedPage>
    {
        ...
    }
}

Belangrijke API's