Passare a C++/WinRT da C#
Suggerimento
Se si è già letto questo argomento e si torna a consultarlo per eseguire un'attività specifica, è possibile passare direttamente alla sezione Trovare il contenuto in base all'attività da eseguire.
Questo argomento cataloga in modo completo i dettagli tecnici relativi alla conversione del codice sorgente di un progetto C# nell'equivalente in C++/WinRT.
Per un case study relativo alla conversione di uno degli esempi di app UWP (Universal Windows Platform), vedi l'argomento complementare Conversione dell'esempio Clipboard da C++/WinRT from C#. Puoi esercitarti e acquisire esperienza seguendo la procedura dettagliata ed eseguendo la conversione dell'esempio mentre procedi.
Preparazione e modifiche attese
Il case study Conversione dell'esempio Clipboard da C++/WinRT from C# illustra alcuni esempi dei tipi di decisioni che dovrai prendere in merito alla progettazione del software durante la conversione di un progetto in C++/WinRT. È quindi consigliabile prepararsi a eseguire la conversione approfondendo le conoscenze sul funzionamento del codice esistente. Otterrai così una panoramica completa delle funzionalità dell'app e della struttura del codice e le decisioni che prenderai ti consentiranno sempre progredire nella giusta direzione.
Per quanto riguarda le modifiche attese dalla conversione, è possibile raggrupparle in quattro categorie.
- Conversione della proiezione del linguaggio. Windows Runtime (WinRT) viene proiettato in diversi linguaggi di programmazione. Ognuna di queste proiezioni viene progettata in modo da essere coerente con il linguaggio di programmazione in questione. Per C#, alcuni tipi di Windows Runtime vengono proiettati come tipi .NET. Ad esempio, System.Collections.Generic.IReadOnlyList<T> verrà riconvertito in Windows.Foundation.Collections.IVectorView<T>. Inoltre, in C# alcune operazioni di Windows Runtime vengono proiettate come funzionalità utili del linguaggio C#. Ad esempio, in C# si usa la sintassi dell'operatore
+=
per registrare un delegato di gestione degli eventi. Le funzionalità del linguaggio come queste verranno quindi riconvertite nell'operazione fondamentale che viene eseguita (registrazione degli eventi, in questo esempio). - Conversione della sintassi del linguaggio. Molte di queste modifiche sono semplici trasformazioni meccaniche che sostituiscono un simbolo con un altro, ad esempio un punto (
.
) con due doppi punti (::
). - Conversione della procedura del linguaggio. In alcuni casi può trattarsi di modifiche semplici e ripetitive, ad esempio la modifica di
myObject.MyProperty
inmyObject.MyProperty()
. In altri casi sono necessarie modifiche più complesse, ad esempio la conversione di una procedura che prevede l'uso di System.Text.StringBuilder in una che prevede l'uso di std::wostringstream. - Attività collegate alla conversione specifiche di C++/WinRT. C# si prende cura di determinati dettagli di Windows Runtime in modo implicito, dietro le quinte. Questi dettagli vengono eseguiti in C++/WinRT in modo esplicito. Un esempio è l'uso di un file di
.idl
per definire le classi di runtime.
Dopo l'indice seguente basato sulle attività, le restanti sezioni di questo argomento sono strutturate in base alla tassonomia precedente.
Trovare il contenuto in base all'attività da eseguire
Attività | Contenuto |
---|---|
Creare un componente Windows Runtime | È possibile ottenere determinate funzionalità (o determinate API chiamate) solo con C++. È possibile scomporre tali funzionalità in un componente Windows Runtime C++/WinRT e quindi utilizzare il componente, ad esempio, in un'app C#. Vedere Componenti Windows Runtime con C++/WinRT e Se si sta creando una classe di runtime in un componente Windows Runtime. |
Convertire un metodo asincrono | È consigliabile che la prima riga di un metodo asincrono in una classe di runtime C++/WinRT sia auto lifetime = get_strong(); (vedere Accesso sicuro al puntatore this in una coroutine membro di classe).Per la conversione da Task , vedere Azione asincrona.Per la conversione da Task<T> , vedere Operazione asincrona.Per la conversione da async void , vedere Metodo Fire-and-forget. |
Convertire una classe | Stabilire prima se la classe deve essere una classe di runtime o se può essere una classe ordinaria. Per facilitare la scelta, vedere la parte iniziale dell'argomento Creare API con C++/WinRT. Vedere quindi le tre righe seguenti. |
Convertire una classe di runtime | Si tratta di una classe che condivide le funzionalità all'esterno dell'app C++ o di una classe usata nel data binding XAML. Vedere Se si sta creando una classe di runtime in un componente Windows Runtime o Se si sta creando una classe di runtime a cui fare riferimento nell'interfaccia utente XAML. Questi collegamenti contengono descrizioni più dettagliate, ma una classe di runtime deve essere dichiarata in IDL. Se il progetto contiene già un file IDL (ad esempio, Project.idl ), è consigliabile dichiarare in questo file ogni nuova classe di runtime. In IDL dichiarare tutti i metodi e i membri dati che verranno usati all'esterno dell'app o che verranno usati in XAML. Dopo aver aggiornato il file IDL, ricompilare ed esaminare i file stub generati (.h e .cpp ) nella cartella Generated Files del progetto (in Esplora soluzioni, con il nodo di progetto selezionato, verificare che l'opzione Mostra tutti i file sia stata attivata). Confrontare i file stub con i file già presenti nel progetto, aggiungendo file o aggiungendo/aggiornando le firme di funzione eventualmente necessarie. La sintassi dei file stub è sempre corretta ed è quindi consigliabile usarla per ridurre il numero di errori di compilazione. Quando gli stub presenti nel progetto corrispondono a quelli presenti nei file stub, è possibile procedere e implementarli convertendo il codice C#. |
Convertire una classe ordinaria | Vedere Se non si sta creando una classe di runtime. |
Creare IDL | Introduzione a Microsoft Interface Definition Language 3.0 Se stai creando una classe di runtime a cui fare riferimento nell'interfaccia utente XAML Utilizzo di oggetti dal markup XAML Definire le classi di runtime in IDL |
Convertire una raccolta | Gruppi di prodotti con C++/WinRT Rendere disponibile un'origine dati per il markup XAML Contenitore associativo Accesso ai membri del vettore |
Convertire un evento | Delegato del gestore eventi come membro di classe Revocare il delegato del gestore eventi |
Convertire un metodo | Da C#: private async void SampleButton_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e) { ... } Nel file .h di C++/WinRT: fire_and_forget SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&); Nel file .cpp di C++/WinRT: fire_and_forget OcrFileImage::SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&) {...} |
Convertire stringhe | Gestione delle stringhe in C++/WinRT ToString Compilazione di stringhe Boxing e unboxing di una stringa |
Conversione dei tipi (cast dei tipi) | C#: o.ToString() C++/WinRT: to_hstring(static_cast<int>(o)) Vedere anche ToString. C#: (Value)o C++/WinRT: unbox_value<Value>(o) Genera un'eccezione se la conversione unboxing non riesce. Vedere anche Boxing e unboxing. C#: o as Value? ?? fallback C++/WinRT: unbox_value_or<Value>(o, fallback) Restituisce il fallback se la conversione unboxing non riesce. Vedere anche Boxing e unboxing. C#: (Class)o C++/WinRT: o.as<Class>() Genera un'eccezione se la conversione non riesce. C#: o as Class C++/WinRT: o.try_as<Class>() Restituisce un valore null se la conversione non riesce. |
Modifiche che coinvolgono la proiezione del linguaggio
Categoria | C# | C++/WinRT | Vedi anche |
---|---|---|---|
Oggetto non tipizzato | object o System.Object |
Windows::Foundation::IInspectable | Conversione del metodo EnableClipboardContentChangedNotifications |
Spazi dei nomi di proiezione | using System; |
using namespace Windows::Foundation; |
|
using System.Collections.Generic; |
using namespace Windows::Foundation::Collections; |
||
Dimensioni di una raccolta | collection.Count |
collection.Size() |
Conversione del metodo BuildClipboardFormatsOutputString |
Tipo di raccolta tipico | IList<T> e Add per aggiungere un elemento. | IVector<T> e Append per aggiungere un elemento. Se usi std::vector in qualsiasi punto, usa allora push_back per aggiungere un elemento. | |
Tipi di raccolta di sola lettura | IReadOnlyList<T> | IVectorView<T> | Conversione del metodo BuildClipboardFormatsOutputString |
Delegato del gestore eventi come membro di classe | myObject.EventName += Handler; |
token = myObject.EventName({ get_weak(), &Class::Handler }); |
Conversione del metodo EnableClipboardContentChangedNotifications |
Revocare il delegato del gestore eventi | myObject.EventName -= Handler; |
myObject.EventName(token); |
Conversione del metodo EnableClipboardContentChangedNotifications |
Contenitore associativo | IDictionary<K, V> | IMap<K, V> | |
Accesso ai membri del vettore | x = v[i]; v[i] = x; |
x = v.GetAt(i); v.SetAt(i, x); |
Registrare/revocare un gestore eventi
In C++/WinRT puoi scegliere tra più opzioni sintattiche per registrare/revocare un delegato del gestore eventi, come illustrato in Gestire eventi mediante i delegati in C++/WinRT. Vedi anche Conversione del metodo EnableClipboardContentChangedNotifications.
In alcuni casi, ad esempio quando il destinatario di un evento (un oggetto che gestisce un evento) sta per essere eliminato definitivamente, è opportuno revocare un gestore dell'evento in modo che l'origine dell'evento (l'oggetto che genera l'evento) non chiami un oggetto eliminato definitivamente. Vedi Revocare un delegato registrato. In casi simili, crea una variabile del membro event_token per i gestori eventi. Per un esempio, vedi Conversione del metodo EnableClipboardContentChangedNotifications.
Puoi anche registrare un gestore eventi nel markup XAML.
<Button x:Name="OpenButton" Click="OpenButton_Click" />
In C# il metodo OpenButton_Click può essere privato e XAML sarà comunque in grado di connetterlo all'evento ButtonBase.Click generato da OpenButton.
In C++/WinRT il metodo OpenButton_Click deve essere pubblico nel tuo tipo di implementazione se vuoi registrarlo nel markup XAML. Se registri un gestore eventi solo in codice imperativo, il gestore eventi non deve necessariamente essere pubblico.
namespace winrt::MyProject::implementation
{
struct MyPage : MyPageT<MyPage>
{
void OpenButton_Click(
winrt::Windows:Foundation::IInspectable const& sender,
winrt::Windows::UI::Xaml::RoutedEventArgs const& args);
}
};
In alternativa, puoi associare la pagina XAML di registrazione al tuo tipo di implementazione e rendere privato OpenButton_Click.
namespace winrt::MyProject::implementation
{
struct MyPage : MyPageT<MyPage>
{
private:
friend MyPageT;
void OpenButton_Click(
winrt::Windows:Foundation::IInspectable const& sender,
winrt::Windows::UI::Xaml::RoutedEventArgs const& args);
}
};
Uno scenario finale è quello in cui il progetto C# da trasferire si associa al gestore eventi dal markup (per informazioni più approfondite su tale scenario, vedere Funzioni in x:Bind).
<Button x:Name="OpenButton" Click="{x:Bind OpenButton_Click}" />
È sufficiente modificare il markup al più semplice Click="OpenButton_Click"
. O, se si preferisce, è possibile mantenerlo così com'è. Per supportare questa operazione è sufficiente dichiarare il gestore eventi in IDL.
void OpenButton_Click(Object sender, Windows.UI.Xaml.RoutedEventArgs e);
Nota
Dichiarare la funzione come void
se la si stesse implementando in modalità Attivare e poi dimenticare.
Modifiche che coinvolgono la sintassi del linguaggio
Categoria | C# | C++/WinRT | Vedi anche |
---|---|---|---|
Modificatori di accesso | public \<member\> |
public: \<member\> |
Conversione del metodo Button_Click |
Accedere a un membro dati | this.variable |
this->variable |
|
Azione asincrona | async Task ... |
IAsyncAction ... |
Interfaccia IAsyncAction, Concorrenza e operazioni asincrone con C++/WinRT |
Operazione asincrona | async Task<T> ... |
IAsyncOperation<T> ... |
Interfaccia IAsyncOperation, Concorrenza e operazioni asincrone con C++/WinRT |
Metodo Fire-and-forget (implica asincronia) | async void ... |
winrt::fire_and_forget ... |
Conversione del metodo CopyButton_Click, Fire and forget |
Accedere a una costante enumerata | E.Value |
E::Value |
Conversione del metodo DisplayChangedFormats |
Attendere in modo cooperativo | await ... |
co_await ... |
Conversione del metodo CopyButton_Click |
Raccolta di tipi proiettati come campo privato | private List<MyRuntimeClass> myRuntimeClasses = new List<MyRuntimeClass>(); |
std::vector <MyNamespace::MyRuntimeClass> m_myRuntimeClasses; |
|
Costruzione GUID | 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} }; |
|
Separatore dello spazio dei nomi | A.B.T |
A::B::T |
|
Null | null |
nullptr |
Conversione del metodo UpdateStatus |
Ottenere un oggetto type | typeof(MyType) |
winrt::xaml_typename<MyType>() |
Conversione della proprietà Scenarios |
Dichiarazione di parametro per un metodo | MyType |
MyType const& |
Passaggio di parametri |
Dichiarazione di parametro per un metodo asincrono | MyType |
MyType |
Passaggio di parametri |
Chiamare un metodo statico | T.Method() |
T::Method() |
|
Stringhe | string o System.String |
winrt::hstring | Gestione delle stringhe in C++/WinRT |
Valore letterale stringa | "a string literal" |
L"a string literal" |
Conversione del costruttore Current e di FEATURE_NAME |
Tipo dedotto | var |
auto |
Conversione del metodo BuildClipboardFormatsOutputString |
Direttiva using | using A.B.C; |
using namespace A::B::C; |
Conversione del costruttore Current e di FEATURE_NAME |
Valori letterali stringa verbatim/non elaborati | @"verbatim string literal" |
LR"(raw string literal)" |
Conversione del metodo DisplayToast |
Nota
Se un file di intestazione non contiene una direttiva using namespace
per un determinato spazio dei nomi, dovrai qualificare completamente tutti i nomi di tipo per tale spazio dei nomi o perlomeno qualificarli in misura sufficiente per consentire al compilatore di trovarli. Per un esempio, vedi Conversione del metodo DisplayToast.
Conversione di classi e membri
Dovrai decidere se convertire ogni tipo C# in un tipo di Windows Runtime o in una normale classe/struct/enumerazione C++. Per altre informazioni ed esempi dettagliati che illustrano come prendere tali decisioni, vedi Conversione dell'esempio Clipboard da C++/WinRT from C#.
Una proprietà C# diventa in genere una funzione di accesso, una funzione mutatore e un membro dati di supporto. Per altre informazioni e un esempio, vedi Conversione della proprietà IsClipboardContentChangedEnabled.
Per i campi non statici, impostali come membri dati del tuo tipo di implementazione.
Un campo statico C# diventa una funzione di accesso statica e/o una funzione mutatore C++/WinRT. Per altre informazioni e un esempio, vedi Conversione del costruttore Current e di FEATURE_NAME.
Anche per le funzioni membro dovrai decidere se ognuna appartiene o meno a IDL oppure se si tratta di una funzione membro pubblica o privata del tuo tipo di implementazione. Per altre informazioni ed esempi su come prendere una decisione, vedi IDL per il tipo MainPage.
Conversione del markup XAML e dei file di asset
Nel caso illustrato in Conversione dell'esempio Clipboard da C++/WinRT from C# abbiamo potuto usare lo stesso markup XAML (incluse risorse) e gli stessi file di asset in tutto il progetto C# e C++/WinRT. In alcuni casi, le modifiche apportate al markup saranno necessarie a raggiungere lo scopo. Vedi Copiare il codice XAML e gli stili necessari per completare la conversione di MainPage.
Modifiche che coinvolgono le procedure all'interno del linguaggio
Categoria | C# | C++/WinRT | Vedi anche |
---|---|---|---|
Gestione del ciclo di vita in un metodo asincrono | N/D | auto lifetime{ get_strong() }; oppureauto lifetime = get_strong(); |
Conversione del metodo CopyButton_Click |
Dismissione | using (var t = v) |
auto t{ v }; t.Close(); // or let wrapper destructor do the work |
Conversione del metodo CopyImage |
Costruire un oggetto | new MyType(args) |
MyType{ args } oppureMyType(args) |
Conversione della proprietà Scenarios |
Creare un riferimento non inizializzato | MyType myObject; |
MyType myObject{ nullptr }; oppureMyType myObject = nullptr; |
Conversione del costruttore Current e di FEATURE_NAME |
Costruire un oggetto in una variabile con args | var myObject = new MyType(args); |
auto myObject{ MyType{ args } }; oppure auto myObject{ MyType(args) }; oppure auto myObject = MyType{ args }; oppure auto myObject = MyType(args); oppure MyType myObject{ args }; oppure MyType myObject(args); |
Conversione del metodo Footer_Click |
Costruire un oggetto in una variabile senza args | var myObject = new T(); |
MyType myObject; |
Conversione del metodo BuildClipboardFormatsOutputString |
Abbreviazione dell'inizializzazione dell'oggetto | var p = new FileOpenPicker{ ViewMode = PickerViewMode.List }; |
FileOpenPicker p; p.ViewMode(PickerViewMode::List); |
|
Operazione vettore in blocco | var p = new FileOpenPicker{ FileTypeFilter = { ".png", ".jpg", ".gif" } }; |
FileOpenPicker p; p.FileTypeFilter().ReplaceAll({ L".png", L".jpg", L".gif" }); |
Conversione del metodo CopyButton_Click |
Eseguire l'iterazione nella raccolta | foreach (var v in c) |
for (auto&& v : c) |
Conversione del metodo BuildClipboardFormatsOutputString |
Recuperare un'eccezione | catch (Exception ex) |
catch (winrt::hresult_error const& ex) |
Conversione del metodo PasteButton_Click |
Dettagli dell'eccezione | ex.Message |
ex.message() |
Conversione del metodo PasteButton_Click |
Ottenere il valore di una proprietà | myObject.MyProperty |
myObject.MyProperty() |
Conversione del metodo NotifyUser |
Impostare il valore di una proprietà | myObject.MyProperty = value; |
myObject.MyProperty(value); |
|
Incrementare il valore di una proprietà | myObject.MyProperty += v; |
myObject.MyProperty(thing.Property() + v); Per le stringhe, passare a un generatore |
|
ToString() | myObject.ToString() |
winrt::to_hstring(myObject) |
ToString() |
Da stringa di linguaggio a stringa di Windows Runtime | N/D | winrt::hstring{ s } |
|
Compilazione di stringhe | StringBuilder builder; builder.Append(...); |
std::wostringstream builder; builder << ...; |
Compilazione di stringhe |
Interpolazione di stringa | $"{i++}) {s.Title}" |
winrt::to_hstring e/o winrt::hstring::operator+ | Conversione del metodo OnNavigatedTo |
Stringa vuota per il confronto | System.String.Empty | winrt::hstring::empty | Conversione del metodo UpdateStatus |
Creare una stringa vuota | var myEmptyString = String.Empty; |
winrt::hstring myEmptyString{ L"" }; |
|
Operazioni del dizionario | 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) |
|
Conversione dei tipi (throw in caso di errore) | (MyType)v |
v.as<MyType>() |
Conversione del metodo Footer_Click |
Conversione dei tipi (null in caso di errore) | v as MyType |
v.try_as<MyType>() |
Conversione del metodo PasteButton_Click |
Gli elementi XAML con x:Name sono proprietà | MyNamedElement |
MyNamedElement() |
Conversione del costruttore Current e di FEATURE_NAME |
Passare a thread di UI | CoreDispatcher.RunAsync | CoreDispatcher.RunAsync o winrt::resume_foreground | Conversione del metodo NotifyUser e Conversione del metodo HistoryAndRoaming |
Costruzione di elementi dell'interfaccia utente in codice imperativo in una pagina XAML | Vedi Costruzione di elementi dell'interfaccia utente | Vedi Costruzione di elementi dell'interfaccia utente |
Le sezioni seguenti illustrano in modo più dettagliato alcuni elementi della tabella.
Costruzione di elementi dell'interfaccia utente
Questi esempi di codice illustrano la costruzione di un elemento dell'interfaccia utente nel codice imperativo di una pagina XAML.
var myTextBlock = new TextBlock()
{
Text = "Text",
Style = (Windows.UI.Xaml.Style)this.Resources["MyTextBlockStyle"]
};
TextBlock myTextBlock;
myTextBlock.Text(L"Text");
myTextBlock.Style(
winrt::unbox_value<Windows::UI::Xaml::Style>(
Resources().Lookup(
winrt::box_value(L"MyTextBlockStyle")
)
)
);
ToString()
I tipi C# forniscono il metodo Object::ToString.
int i = 2;
var s = i.ToString(); // s is a System.String with value "2".
C++/ WinRT non fornisce direttamente questa funzionalità, ma puoi ricorrere ad alternative.
int i{ 2 };
auto s{ std::to_wstring(i) }; // s is a std::wstring with value L"2".
C++/WinRT supporta inoltre winrt::to_hstring per un numero limitato di tipi. Dovrai aggiungere gli overload per tutti i tipi aggiuntivi che vuoi convertire in stringhe.
Lingua | Conversione in stringa di int | Conversione in stringa di enum |
---|---|---|
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); |
In caso di conversione in stringa di un enum, dovrai fornire l'implementazione di winrt::to_hstring.
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));
}
}
}
Queste conversioni in stringa vengono spesso utilizzate in modo implicito dal data binding.
<TextBlock>
You have <Run Text="{Binding FlowerCount}"/> flowers.
</TextBlock>
<TextBlock>
Most recent status is <Run Text="{x:Bind LatestOperation.Status}"/>.
</TextBlock>
I binding eseguiranno la funzione winrt::to_hstring sulla proprietà associata. Nel caso del secondo esempio (StatusEnum), devi fornire un overload personalizzato di winrt::to_hstring, altrimenti verrà restituito un errore del compilatore.
Vedi anche Conversione del metodo Footer_Click.
Compilazione di stringhe
Per la compilazione di stringhe, C# dispone di un tipo StringBuilder incorporato.
Categoria | C# | C++/WinRT |
---|---|---|
Compilazione di stringhe | StringBuilder builder; builder.Append(...); |
std::wostringstream builder; builder << ...; |
Aggiungere una stringa di Windows Runtime, mantenendo i valori null | builder.Append(s); |
builder << std::wstring_view{ s }; |
Aggiungere una nuova riga | builder.Append(Environment.NewLine); |
builder << std::endl; |
Accedere al risultato | s = builder.ToString(); |
ws = builder.str(); |
Vedi anche Conversione del metodo BuildClipboardFormatsOutputString e Conversione del metodo DisplayChangedFormats.
Esecuzione del codice nel thread principale dell'interfaccia utente
Questo esempio è basato sul contenuto di Esempio di scanner di codice a barre.
Quando si vuole intervenire sul thread principale dell'interfaccia utente in un progetto C#, si usa in genere il metodo CoreDispatcher.RunAsync, come illustrato di seguito.
private async void Watcher_Added(DeviceWatcher sender, DeviceInformation args)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
// Do work on the main UI thread here.
});
}
È molto più semplice esprimere questo scenario in C++/WinRT. Si noti che i parametri vengono accettati in base al valore presupponendo che si voglia accedere a tali parametri dopo il primo punto di sospensione (co_await
in questo caso). Per altre informazioni, vedere Passaggio dei parametri.
winrt::fire_and_forget Watcher_Added(DeviceWatcher sender, winrt::DeviceInformation args)
{
co_await Dispatcher();
// Do work on the main UI thread here.
}
Se è necessario intervenire con una priorità diversa da quella predefinita, vedere la funzione winrt::resume_foreground, che ha un overload che accetta una priorità. Per alcuni esempi di codice che illustrano come attendere una chiamata a winrt::resume_foreground, vedere Programmazione basata sull'affinità del thread.
Attività collegate alla conversione specifiche di C++/WinRT
Definire le classi di runtime in IDL
Vedi IDL per il tipo MainPage e Consolidare i file .idl
.
Includere i file di intestazione dello spazio dei nomi di Windows C++/WinRT necessari
In C++/WinRT, ogni volta che vuoi usare un tipo di uno spazio dei nomi di Windows, devi includere il file di intestazione dello spazio dei nomi di Windows C++/WinRT corrispondente. Per un esempio, vedi Conversione del metodo NotifyUser.
Boxing e unboxing
C# esegue automaticamente la conversione boxing di valori scalari in oggetti. C++/WinRT richiede che la funzione winrt::box_value venga chiamata esplicitamente. Per entrambi i linguaggi è necessario eseguire l'unboxing esplicito. Vedi Boxing e unboxing con C++/WinRT.
Nelle tabelle seguenti useremo queste definizioni.
C# | C++/WinRT |
---|---|
int i; |
int i; |
string s; |
winrt::hstring s; |
object o; |
IInspectable o; |
Operation | C# | C++/WinRT |
---|---|---|
Boxing | o = 1; o = "string"; |
o = box_value(1); o = box_value(L"string"); |
Unboxing | i = (int)o; s = (string)o; |
i = unbox_value<int>(o); s = unbox_value<winrt::hstring>(o); |
C++/CX e C# generano eccezioni se provi a eseguire l'unboxing di un puntatore Null in un tipo di valore. C++/WinRT considera questo caso come un errore di programmazione e si arresta in modo anomalo. In C++/WinRT usa la funzione winrt::unbox_value_or se vuoi gestire il caso in cui l'oggetto non è del tipo previsto.
Scenario | C# | C++/WinRT |
---|---|---|
Unboxing di un numero intero noto | i = (int)o; |
i = unbox_value<int>(o); |
Se o è Null | System.NullReferenceException |
Arresto anomalo |
Se o non è un valore int sottoposto a boxing | System.InvalidCastException |
Arresto anomalo |
Unboxing di int, fallback se Null e arresto anomalo negli altri casi | i = o != null ? (int)o : fallback; |
i = o ? unbox_value<int>(o) : fallback; |
Unboxing di int, se possibile, e fallback per gli altri casi | i = as int? ?? fallback; |
i = unbox_value_or<int>(o, fallback); |
Per un esempio, vedi Conversione del metodo OnNavigatedTo e Conversione del metodo Footer_Click.
Boxing e unboxing di una stringa
Una stringa è per certi versi un tipo di valore e per altri versi un tipo di riferimento. C# e C++/WinRT trattano le stringhe in modo diverso.
Il tipo ABI HSTRING è un puntatore a una stringa con conteggio dei riferimenti. Non deriva tuttavia da IInspectable e pertanto non è tecnicamente un oggetto. Inoltre, un'istanza Null di HSTRING rappresenta la stringa vuota. La conversione boxing di elementi non derivati da IInspectable viene eseguita tramite il wrapping all'interno di un'interfaccia IReference<T> e Windows Runtime offre un'implementazione standard sotto forma di oggetto PropertyValue. I tipi personalizzati vengono segnalati come PropertyType::OtherType.
C# rappresenta una stringa di Windows Runtime come tipo di riferimento, mentre C++/WinRT proietta una stringa come tipo di valore. Ciò significa che una stringa Null sottoposta a boxing può avere rappresentazioni diverse a seconda del modo in cui viene raggiunta.
Comportamento | C# | C++/WinRT |
---|---|---|
Dichiarazioni | object o; string s; |
IInspectable o; hstring s; |
Categoria del tipo di stringa | Tipo di riferimento | Tipo di valore |
HSTRING Null proiettata come | "" |
hstring{} |
Null e "" sono identici? |
No | Sì |
Validità di Null | s = null; s.Length genera NullReferenceException |
s = hstring{}; s.size() == 0 (valido) |
Se si assegna una stringa null all'oggetto | o = (string)null; o == null |
o = box_value(hstring{}); o != nullptr |
Se si assegna "" all'oggetto |
o = ""; o != null |
o = box_value(hstring{L""}); o != nullptr |
Boxing e unboxing di base.
Operation | C# | C++/WinRT |
---|---|---|
Boxing di una stringa | o = s; Una stringa vuota diventa un oggetto non null. |
o = box_value(s); Una stringa vuota diventa un oggetto non null. |
Unboxing di una stringa nota | s = (string)o; Un oggetto null diventa una stringa null. InvalidCastException se non è una stringa. |
s = unbox_value<hstring>(o); Arresto anomalo dell'oggetto null. Arresto anomalo se non è una stringa. |
Unboxing di una possibile stringa | s = o as string; L'oggetto null o non stringa diventa una stringa null. OPPURE s = o as string ?? fallback; Il valore null o non stringa diventa fallback. La stringa vuota viene mantenuta. |
s = unbox_value_or<hstring>(o, fallback); Il valore null o non stringa diventa fallback. La stringa vuota viene mantenuta. |
Rendere disponibile una classe per l'estensione di markup {Binding}
Se intendi usare l'estensione di markup {Binding} per il data binding al tipo di dati, vedi Binding di oggetti dichiarati usando {Binding}.
Utilizzo di oggetti dal markup XAML
In un progetto C# puoi utilizzare membri privati ed elementi denominati dal markup XAML. In C++/WinRT, tuttavia, tutte le entità utilizzate con l'estensione di markup {x:Bind} XAML devono essere esposte pubblicamente in IDL.
Inoltre, il binding a un valore booleano viene visualizzato come true
o false
in C#, ma appare come Windows.Foundation.IReference`1<Boolean> in C++/WinRT.
Per altre informazioni ed esempi di codice, vedi Utilizzo di oggetti dal markup XAML.
Rendere disponibile un'origine dati per il markup XAML
In C++/WinRT versione 2.0.190530.8 o successive, winrt::single_threaded_observable_vector crea un vettore osservabile che supporta sia IObservableVector<T> sia IObservableVector<IInspectable>. Per un esempio, vedi Conversione della proprietà Scenarios.
Puoi creare il file Midl (.idl) nel modo seguente. Vedi anche Factoring delle classi di runtime nei file Midl (.idl).
namespace Bookstore
{
runtimeclass BookSku { ... }
runtimeclass BookstoreViewModel
{
Windows.Foundation.Collections.IObservableVector<BookSku> BookSkus{ get; };
}
runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
{
MainPage();
BookstoreViewModel MainViewModel{ get; };
}
}
Esegui quindi l'implementazione nel modo seguente.
// 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;
};
...
Per altre informazioni, vedi Controlli di elementi XAML, binding a una raccolta C++/WinRT e Raccolte con C++/WinRT.
Rendere disponibile un'origine dati per il markup XAML (prima di C++/WinRT 2.0.190530.8)
Per il data binding XAML è necessario che un'origine degli elementi implementi IIterable<IInspectable> e una delle combinazioni di interfacce seguenti.
- IObservableVector<IInspectable>
- IBindableVector e INotifyCollectionChanged
- IBindableVector e IBindableObservableVector
- IBindableVector da sola (non risponde alle modifiche)
- IVector<IInspectable>
- IBindableIterable (esegue l'iterazione e salva gli elementi in una raccolta privata)
Un'interfaccia generica come IVector<T> non può essere rilevata in fase di runtime. Ogni IVector<T> ha un identificatore di interfaccia (IID) diverso, che è una funzione di T. Gli sviluppatori possono espandere il set di T in modo arbitrario e quindi il codice di binding XAML non può mai conoscere il set completo per l'esecuzione di query. Questa restrizione non è un problema per C# perché ogni oggetto CLR che implementa IEnumerable<T> implementa automaticamente anche IEnumerable. A livello di ABI, ciò significa che ogni oggetto che implementa IObservableVector<T> implementa automaticamente anche IObservableVector<IInspectable>.
C++/WinRT non offre questa garanzia. Se una classe di runtime C++/WinRT implementa IObservableVector<T>, non è possibile presupporre che venga fornita anche un'implementazione di IObservableVector<IInspectable>.
Ecco quindi come dovrebbe essere l'esempio precedente.
...
runtimeclass BookstoreViewModel
{
// This is really an observable vector of BookSku.
Windows.Foundation.Collections.IObservableVector<Object> BookSkus{ get; };
}
Ed ecco l'implementazione.
// 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;
};
...
Se hai la necessità di accedere ad oggetti in m_bookSkus, devi eseguire una chiamata a QI su tali oggetti in 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;
}
Classi derivate
Per essere derivata da una classe di runtime, la classe di base deve essere componibile. In C# non è necessario eseguire operazioni particolari per rendere componibili le classi, diversamente da C++/WinRT. Per indicare che una classe può essere usata come classe di base devi usare la parola chiave unsealed.
unsealed runtimeclass BasePage : Windows.UI.Xaml.Controls.Page
{
...
}
runtimeclass DerivedPage : BasePage
{
...
}
Nel file di intestazione del tuo tipo di implementazione devi includere il file di intestazione della classe di base prima dell'intestazione generata automaticamente per la classe derivata. In caso contrario, verranno restituiti errori come "Tipo non valido come espressione".
// 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>
{
...
}
}