Conversione dell'esempio Clipboard da C# a C++/WinRT - Case studyPorting the Clipboard sample to C++/WinRT from C#—a case study

Questo argomento presentata un case study sulla conversione di uno degli esempi di app della piattaforma UWP (Universal Windows Platform) da C# a C++/WinRT.This topic presents a case study of porting one of the Universal Windows Platform (UWP) app samples from C# to C++/WinRT. Per esercitarti e acquisire esperienza nella conversione, segui la procedura dettagliata e prova tu stesso a eseguire la conversione dell'esempio.You can gain porting practice and experience by following along with the walkthrough and porting the sample for yourself as you go.

Per un catalogo completo dei dettagli tecnici relativi alla conversione a C++/WinRT da C#, vedi l'argomento complementare Passare a C++/WinRT da C#.For a comprehensive catalog of the technical details involved in porting to C++/WinRT from C#, see the companion topic Move to C++/WinRT from C#.

Breve introduzione ai file di codice sorgente C# e C++A brief preface about C# and C++ source code files

In un progetto C#, i file di codice sorgente sono principalmente file .cs.In a C# project, your source code files are primarily .cs files. Quando inizierai a utilizzare il linguaggio C++, noterai che dovrai acquisire familiarità con altri tipi di file di codice sorgente.When you move to C++, you'll notice that there are more kinds of source code files to get used to. Ciò è dovuto alla differenza tra i compilatori, al modo in cui viene riutilizzato il codice sorgente C++ e alle nozioni di dichiarazione e definizione di un tipo e delle relative funzioni (metodi).The reason is to do with the difference between compilers, the way C++ source code is reused, and the notions of declaring and defining a type and its functions (its methods).

Una dichiarazione di funzione descrive solo la firma della funzione, ovvero il tipo restituito, il nome, nonché i nomi e i tipi di parametro.A function declaration describes just the function's signature (its return type, its name, and its parameter types and names). Una definizione di funzione include il corpo della funzione, ovvero la relativa implementazione.A function definition includes the function's body (its implementation).

Per quanto riguarda i tipi, questi concetti sono leggermente diversi.It's a little different when it comes to types. Un tipo viene definito specificandone il nome e semplicemente dichiarando (come minimo) tutte le relative funzioni membro (e gli altri membri).You define a type by providing its name and by (at a minimum) just declaring all of its member functions (and other members). In pratica, puoi definire un tipo anche se non definisci le relative funzioni membro.That's right, you can define a type even if you don't define its member functions.

  • I file di codice sorgente C++ comuni sono file .h e .cpp.Common C++ source code files are .h and .cpp files. Un file .h è un file di intestazione e definisce uno o più tipi.A .h file is a header file, and it defines one or more types. Anche se puoi definire funzioni membro in un'intestazione, questa operazione viene in genere eseguita tramite un file cpp.While you can define member functions in a header, that's typically what a cpp file is for. Pertanto, per un ipotetico tipo C++ MyClass, definirai MyClass in MyClass.h e le relative funzioni membro in MyClass.cpp.So for a hypothetical C++ type MyClass, you'd define MyClass in MyClass.h, and you'd define its member functions in MyClass.cpp. Per consentire ad altri sviluppatori di riutilizzare le classi, è sufficiente condividere i file .h e il codice dell'oggetto.For other developers to re-use your classes, you'd share out just the .h files and object code. È opportuno mantenere segreti i file .cpp, perché l'implementazione è una tua proprietà intellettuale.You'd keep your .cpp files secret, because the implementation constitutes your intellectual property.
  • Intestazione precompilata (pch.h).Precompiled header (pch.h). Nell'applicazione è in genere incluso un set di file di intestazione che cambia solo raramente.Typically there's a set of header files that you include in your application, and that set changes only rarely. Di conseguenza, invece di elaborare il contenuto di tale set di intestazioni a ogni compilazione, puoi aggregare le intestazioni in una sola, compilarla una volta e quindi usare l'output di questo passaggio ogni volta che esegui la compilazione.So rather than processing the contents of that set of headers each time you compile, you can aggregate those headers into one, compile that once, and then use the output of that precompilation step each time you build. Questa operazione viene eseguita tramite un file di intestazione precompilata, in genere denominato pch.h.You do this via a precompiled header file (usually named pch.h).
  • File .idl..idl files. Questi file contengono il linguaggio di definizione dell'interfaccia (IDL, Interface Definition Language).These files contain Interface Definition Language (IDL). È possibile considerare i file IDL come file di intestazione per i tipi di Windows Runtime.You can think of IDL as header files for Windows Runtime types. Per altre informazioni su IDL, vedi la sezione IDL per il tipo MainPage.We'll talk more about IDL in the section IDL for the MainPage type.

Scaricare e testare l'esempio ClipboardDownload and test the Clipboard sample

Visita la pagina Web Esempio Clipboard e fai clic su Scarica ZIP.Visit the Clipboard sample web page, and click Download ZIP. Decomprimi il file scaricato e osserva la struttura di cartelle.Unzip the downloaded file, and take a look at the folder structure.

  • La versione C# del codice sorgente di esempio è contenuta nella cartella denominata cs.The C# version of the sample source code is contained in the folder named cs.
  • La versione C++/WinRT del codice sorgente di esempio è contenuta nella cartella denominata cppwinrt.The C++/WinRT version of the sample source code is contained in the folder named cppwinrt.
  • Gli altri file, usati sia dalla versione C# che dalla versione C++/WinRT, sono reperibili nelle cartelle shared e SharedContent.Other files—used by both the C# version and the C++/WinRT version—can be found in the shared and SharedContent folders.

La procedura dettagliata in questo argomento illustra come puoi ricreare la versione C++/WinRT dell'esempio Clipboard convertendola dal codice sorgente C#.The walkthrough in this topic shows how you can recreate the C++/WinRT version of the Clipboard sample by porting it from the C# source code. Potrai così vedere come sia possibile convertire i progetti C# in C++/WinRT.That way, you can see how you can port your own C# projects to C++/WinRT.

Per avere un'idea del funzionamento dell'esempio, apri la soluzione C# (\Clipboard_sample\cs\Clipboard.sln), modifica la configurazione in base alle tue esigenze (ad esempio, in x64), compila ed esegui.To get a feel for what the sample does, open the C# solution (\Clipboard_sample\cs\Clipboard.sln), change the configuration as appropriate (perhaps to x64), build, and run. L'interfaccia utente dell'esempio illustra le varie funzionalità, una alla volta.The sample's own user interface (UI) guides you through its various features, step by step.

Creare un'app vuota (C++/WinRT) denominata ClipboardCreate a Blank App (C++/WinRT), named Clipboard

Nota

Per informazioni sull'installazione e sull'uso dell'Estensione C++/WinRT per Visual Studio (VSIX) e del pacchetto NuGet, che insieme forniscono il modello di progetto e il supporto della compilazione, vedi Supporto di Visual Studio per C++/WinRT.For info about installing and using the C++/WinRT Visual Studio Extension (VSIX) and the NuGet package (which together provide project template and build support), see Visual Studio support for C++/WinRT.

Inizia il processo di conversione creando un nuovo progetto C++/WinRT in Microsoft Visual Studio.Begin the porting process by creating a new C++/WinRT project in Microsoft Visual Studio. Crea un nuovo progetto usando il modello App vuota (C++/WinRT) .Create a new project using the Blank App (C++/WinRT) project template. Imposta il nome su Clipboard e, per fare in modo che la struttura di cartelle corrisponda a quella della procedura dettagliata, verifica che l'opzione Inserisci soluzione e progetto nella stessa directory sia deselezionata.Set its name to Clipboard, and (so that your folder structure will match the walkthrough) make sure that Place solution and project in the same directory is unchecked.

Per ottenere una baseline, verifica che questo nuovo progetto vuoto venga compilato ed eseguito.Just to get a baseline, make sure that this new, empty, project builds and runs.

Package.appxmanifest e file di assetPackage.appxmanifest, and asset files

Se non è necessario installare side-by-side nello stesso computer le versioni C# e C++/WinRT dell'esempio, i file di origine del manifesto del pacchetto dell'app (Package.appxmanifest) possono essere identici.If the C# and C++/WinRT versions of the sample don't need to be installed side by side on the same machine, then the two projects' app package manifest source files (Package.appxmanifest) can be identical. In tal caso devi solo copiare Package.appxmanifest dal progetto C# al progetto C++/WinRT.In that case, you can just copy Package.appxmanifest from the C# project to the C++/WinRT project, and you're done.

Perché le due versioni dell'esempio possano coesistere, devono avere identificatori diversi.For the two versions of the sample to coexist, they need different identifiers. In tal caso nel progetto C++/WinRT apri il file Package.appxmanifest in un editor XML e prendi nota di questi tre valori.In that case, in the C++/WinRT project, open the Package.appxmanifest file in an XML editor, and make a note of these three values.

  • Nell'elemento /Package/Identity prendi nota del valore dell'attributo Name.Inside the /Package/Identity element, note the value of the Name attribute. È il nome del pacchetto.This is the package name. Per un progetto appena creato, il progetto assegnerà un GUID univoco come valore iniziale.For a newly-created project, the project will give it an initial value of a unique GUID.
  • Nell'elemento /Package/Applications/Application prendere nota del valore dell'attributo Id.Inside the /Package/Applications/Application element, note the value of the Id attribute. È l'ID applicazione.This is the application id.
  • Nell'elemento /Package/mp:PhoneIdentity prendi nota del valore dell'attributo PhoneProductId.Inside the /Package/mp:PhoneIdentity element, note the value of the PhoneProductId attribute. Anche in questo caso, per un progetto appena creato, questo valore verrà impostato sullo stesso GUID su cui è impostato il nome del pacchetto.Again, for a newly-created project, this will be set to the same GUID as the package name is set to.

Copia quindi Package.appxmanifest dal progetto C# al progetto C++/WinRT.Then copy Package.appxmanifest from the C# project to the C++/WinRT project. Infine puoi ripristinare i tre valori di cui hai preso notaFinally, you can restore the three values that you noted. oppure puoi modificare i valori copiati per renderli univoci e/o appropriati per l'applicazione e per l'organizzazione (come faresti normalmente per un nuovo progetto).Or you can edit the copied values to make them unique and/or appropriate for the application and for your organization (as you ordinarily would for a new project). In questo caso, ad esempio, invece di ripristinare il valore del nome del pacchetto, puoi limitarti a modificare il valore copiato Microsoft.SDKSamples.Clipboard.CS sostituendolo con Microsoft.SDKSamples.Clipboard.CppWinRTFor example, in this case instead of restoring the value of the package name, we can just change the copied value from Microsoft.SDKSamples.Clipboard.CS to Microsoft.SDKSamples.Clipboard.CppWinRT. e lasciare l'ID dell'applicazione impostato su App.And we can leave the application id set to App. Finché il nome del pacchetto o l'ID dell'applicazione è diverso, le due applicazioni avranno un ID modello utente applicazione (AUMID) diverso.As long as either the package name or the application id are different, then the two applications will have different Application User Model IDs (AUMIDs).

Per questa procedura dettagliata, è utile apportare alcune altre modifiche in Package.appxmanifest.For the purposes of this walkthrough, it makes sense to make a few other changes in Package.appxmanifest. Sono presenti tre occorrenze della stringa Clipboard C# Sample.There are three occurrences of the string Clipboard C# Sample. Sostituiscile con Clipboard C++/WinRT Sample.Change that to Clipboard C++/WinRT Sample.

Nel progetto C++/WinRT il file Package.appxmanifest e il progetto ora non sono sincronizzati con i file di asset a cui fanno riferimento.In the C++/WinRT project, the Package.appxmanifest file and the project are now out of sync with respect to the asset files that they reference. Per risolvere il problema, rimuovi prima di tutto gli asset dal progetto C++/WinRT selezionando tutti i file nella cartella Assets (in Esplora soluzioni in Visual Studio) e rimuovendoli (scegli Elimina nella finestra di dialogo).To remedy that, first remove the assets from the C++/WinRT project by selecting all the files in the Assets folder (in Solution Explorer in Visual Studio) and removing them (choose Delete in the dialog).

Il progetto C# fa riferimento ai file di asset da una cartella condivisa.The C# project references asset files from a shared folder. Puoi eseguire la stessa operazione nel progetto C++/WinRT oppure puoi copiare i file come si farà in questa procedura dettagliata.You can do the same in the C++/WinRT project, or you can copy the files as we'll do in this walkthrough.

Passa alla cartella \Clipboard_sample\SharedContent\media.Navigate to the \Clipboard_sample\SharedContent\media folder. Seleziona i sette file inclusi nel progetto C# (da microsoft-sdk.png a windows-sdk.png), copiali e incollali nella cartella \Clipboard\Clipboard\Assets del nuovo progetto.Select the seven files that the C# project includes (microsoft-sdk.png through to windows-sdk.png), copy them, and paste them into the \Clipboard\Clipboard\Assets folder in the new project.

Fai clic con il pulsante destro del mouse sulla cartella Assets (in Esplora soluzioni nel progetto C++/WinRT) > Aggiungi > Elemento esistente e passa a \Clipboard\Clipboard\Assets.Right-click the Assets folder (in Solution Explorer in the C++/WinRT project) > Add > Existing item... and navigate to \Clipboard\Clipboard\Assets. In selezione file seleziona i sette file e fai clic su Aggiungi.In the file picker, select the seven files and click Add.

Package.appxmanifest ora è di nuovo sincronizzato con i file di asset del progetto.Package.appxmanifest is now back in sync with the project's asset files.

MainPage, inclusa la funzionalità di configurazione dell'esempioMainPage, including the functionality that configures the sample

L'esempio Clipboard, come tutti gli esempi di app UWP (Universal Windows Platform) è costituito da una raccolta di scenari che l'utente può analizzare uno alla volta.The Clipboard sample—like all of the Universal Windows Platform (UWP) app samples—consists of a collection of scenarios that the user can step through one at a time. La raccolta di scenari in un determinato esempio è configurata nel codice sorgente dell'esempio.The collection of scenarios in a given sample is configured in the sample's source code. Ogni scenario della raccolta è un elemento di dati che archivia un titolo, oltre al tipo della classe nel progetto che implementa lo scenario.Each scenario in the collection is a data item that stores a title, as well as the type of the class in the project that implements the scenario.

Nella versione C# dell'esempio, se osservi il file del codice sorgente SampleConfiguration.cs, vedrai due classi.In the C# version of the sample, if you look in the SampleConfiguration.cs source code file, you'll see two classes. La maggior parte della logica di configurazione si trova nella classe MainPage, che è una classe parziale. Forma infatti una classe completa se combinata con il markup in MainPage.xaml e con il codice imperativo in MainPage.xaml.cs.Most of the configuration logic is in the MainPage class, which is a partial class (it forms a complete class when combined with the markup in MainPage.xaml and the imperative code in MainPage.xaml.cs). L'altra classe di questo file di codice sorgente è Scenario, con le proprietà Title e ClassType.The other class in this source code file is Scenario, with its Title and ClassType properties.

Nelle prossime sottosezioni si vedrà come convertire MainPage e Scenario.Over the next few subsections, we'll look at how to port MainPage and Scenario.

IDL per il tipo MainPageIDL for the MainPage type

In questa parte iniziale della sezione si illustrerà brevemente il linguaggio di definizione dell'interfaccia (IDL) e si mostreranno i vantaggi offerti da questo linguaggio per la programmazione con C++/WinRT.Let's begin this section by talking briefly about Interface Definition Language (IDL), and how it helps us when we're programming with C++/WinRT. IDL è un tipo di codice sorgente che descrive la superficie chiamabile di un tipo di Windows Runtime.IDL is a kind of source code that describes the callable surface of a Windows Runtime type. La superficie chiamabile (o pubblica) di un tipo viene proiettata nel mondo esterno, in modo da consentirne l'utilizzo.The callable (or public) surface of a type is projected out into the world, so that the type can be consumed. Questa parte proiettata del tipo è in contrasto con la sua effettiva implementazione interna, che naturalmente non è chiamabile e non è pubblica.That projected portion of the type contrasts with the actual internal implementation of the type, which is of course not callable, and not public. In IDL viene definita soltanto la parte proiettata.It's only the projected portion that we define in IDL.

Dopo aver creato codice sorgente IDL (all'interno di un file .idl), puoi compilare il codice IDL in file di metadati leggibili dal computer, noti anche come metadati di Windows.Having authored IDL source code (within an .idl file), you can then compile the IDL into machine-readable metadata files (also known as Windows Metadata). Questi file di metadati hanno estensione .winmd e di seguito sono riportati alcuni dei possibili utilizzi.Those metadata files have the extension .winmd, and here are some of their uses.

  • Un file .winmd può descrivere i tipi di Windows Runtime in un componente.A .winmd can describe the Windows Runtime types in a component. Quando fai riferimento a un componente Windows Runtime (WRC) da un progetto di applicazione, quest'ultimo legge i metadati di Windows appartenenti al WRC (i metadati possono trovarsi in un file separato oppure essere inclusi nello stesso file del WRC), in modo da consentire l'utilizzo dei tipi del WRC dall'interno dell'applicazione.When you reference a Windows Runtime Component (WRC) from an application project, the application project reads the Windows Metadata belonging to the WRC (that metadata may be in a separate file, or it may be packaged into the same file as the WRC itself) so that you can consume the WRC's types from within the application.
  • Un file .winmd può descrivere i tipi di Windows Runtime in una parte dell'applicazione in modo che possano essere utilizzati da un'altra parte della stessa applicazione.A .winmd can describe the Windows Runtime types in one part of your application so that they can be consumed by a different part of the same application. È il caso, ad esempio, di un tipo di Windows Runtime utilizzato da una pagina XAML nella stessa app.For example, a Windows Runtime type that's consumed by a XAML page in the same app.
  • Per semplificare l'utilizzo dei tipi di Windows Runtime (incorporati o di terze parti), il sistema di compilazione di C++/WinRT usa i file .winmd per generare tipi di wrapper per rappresentare le parti proiettate dei tipi di Windows Runtime.To make it easier for you to consume Windows Runtime types (built-in or third party), the C++/WinRT build system uses .winmd files to generate wrapper types to represent the projected portions of those Windows Runtime types.
  • Per semplificare l'implementazione dei tipi di Windows Runtime personalizzati, il sistema di compilazione C++/WinRT converte il codice IDL in un file .winmd e quindi usa tale file per generare wrapper per la proiezione, nonché stub su cui basare l'implementazione. Informazioni più approfondite sugli stub verranno fornite più avanti in questo argomento.To make it easier for you to implement your own Windows Runtime types, the C++/WinRT build system turns your IDL into a .winmd file, and then uses that to generate wrappers for your projection, as well as stubs on which to base your implementation (we'll talk more about these stubs later in this topic).

La versione specifica del linguaggio IDL usata con C++/WinRT è Microsoft Interface Definition Language 3.0.The specific version of IDL that we use with C++/WinRT is Microsoft Interface Definition Language 3.0. Nella parte restante di questa sezione verrà esaminato in dettaglio il tipo MainPage C#.In the remainder of this section, we'll examine the C# MainPage type in some detail. Si deciderà quali parti di questo tipo devono essere incluse nella proiezione del tipo MainPage C++/WinRT, ovvero nella sua superficie chiamabile o pubblica, e quali parti possono appartenere soltanto alla sua implementazione.We'll decide which parts of it need to be in the projection of the C++/WinRT MainPage type (that is, in its callable, or public, surface), and which can be just part of its implementation. Questa distinzione è importante perché, quando verrà creato il file IDL (nella sezione successiva), nel file saranno definite soltanto le parti chiamabili.That distinction is important because when we come to author our IDL (which we'll do in the section after this one), we'll be defining only the callable parts in there.

I file del codice sorgente C# che insieme implementano il tipo MainPage sono: MainPage.xaml (che tra verrà convertito a breve copiandolo), MainPage.xaml.cs e SampleConfiguration.cs.The C# source code files that together implement the MainPage type are: MainPage.xaml (which we'll port soon, by copying it), MainPage.xaml.cs, and SampleConfiguration.cs.

Nella versione C++/WinRT il tipo MainPage verrà fattorizzato nei file del codice sorgente in modo simile.In the C++/WinRT version, we'll factor our MainPage type into source code files in a similar way. La logica in MainPage.xaml.cs verrà per la maggior parte convertita in MainPage.h e MainPage.cppWe'll take the logic in MainPage.xaml.cs and translate it for the most part to MainPage.h and MainPage.cpp. e la logica in SampleConfiguration.cs verrà convertita in SampleConfiguration.h e SampleConfiguration.cpp.And for the logic in SampleConfiguration.cs, we'll translate that to SampleConfiguration.h and SampleConfiguration.cpp.

Le classi in un'applicazione UWP (Universal Windows Platform) C# sono ovviamente tipi di Windows Runtime.The classes in a C# Universal Windows Platform (UWP) application are of course Windows Runtime types. Quando tuttavia crei un tipo in un'applicazione C++/WinRT, puoi scegliere se deve essere un tipo di Windows Runtime o una normale classe/struct/enumerazione C++.But when you author a type in a C++/WinRT application, you can choose whether that type is a Windows Runtime type, or a regular C++ class/struct/enumeration.

Qualsiasi pagina XAML nel progetto deve essere un tipo di Windows Runtime, quindi deve esserlo anche MainPage.Any XAML page in our project needs to be a Windows Runtime type, so MainPage needs to be a Windows Runtime type. Nel progetto C++/WinRT MainPage è già un tipo di Windows Runtime, quindi non è necessario modificarlo in tal senso.In the C++/WinRT project, MainPage is already a Windows Runtime type, so we don't need to change that aspect of it. Si tratta in particolare di una classe di runtime.Specifically, it's a runtime class.

  • Per informazioni più dettagliate sull'opportunità di creare o meno una classe di runtime per un determinato tipo, vedi l'argomento Creare API con C++/WinRT.For more details about whether or not you should author a runtime class for a given type, see the topic Author APIs with C++/WinRT.
  • In C++/WinRT l'implementazione interna di una classe di runtime e le relative parti proiettate (pubbliche) esistono nella forma di due classi diverse,In C++/WinRT, the internal implementation of a runtime class, and the projected (public) parts of it, exist in the form of two different classes. note rispettivamente come tipo di implementazione e tipo proiettato.These are known as the implementation type and the projected type. Per altre informazioni su questi tipi, vedi l'argomento citato nel punto elenco precedente e l'argomento Utilizzare API con C++/WinRT.You can learn more about them in the topic mentioned in the bullet-point above, and also in Consume APIs with C++/WinRT.
  • Per informazioni sulla connessione tra classi di runtime e IDL (file .idl), puoi leggere e seguire l'argomento Controlli XAML, binding a una proprietà C++/WinRT,For more info about the connection between runtime classes and IDL (.idl files), you can read and follow along with the topic XAML controls; bind to a C++/WinRT property. che illustra il processo di creazione di una nuova classe di runtime, in cui il primo passaggio consiste nell'aggiungere un nuovo elemento File Midl (.idl) al progetto.That topic walks through the process of authoring a new runtime class, the first step of which is to add a new Midl File (.idl) item to the project.

Per MainPage, il file MainPage.idl necessario è in realtà già presente nel progetto C++/WinRT,For MainPage, we actually have the necessary MainPage.idl file already in the C++/WinRT project. poiché è già stato creato automaticamente dal modello di progetto.That's because the project template created it for us. Più avanti in questa procedura dettagliata, si aggiungeranno altri file .idl al progetto.But later in this walkthrough we'll be adding further .idl files to the project.

A breve vedrai esattamente quale IDL è necessario aggiungere al file MainPage.idl esistente,We'll shortly see a listing of exactly what IDL we need to add to the existing MainPage.idl file. ma prima è importante capire che cosa inserire nel file IDL e cosa no.Before that, we have some reasoning to do about what does, and what doesn't, need to go in the IDL.

Per determinare quali membri di MainPage è necessario dichiarare in MainPage.idl (in modo che diventino parte della classe di runtime MainPage) e quali possono essere semplicemente membri del tipo di implementazione MainPage, si creerà un elenco di membri della classe MainPage C#.To determine which members of MainPage we need to declare in MainPage.idl (so that they become part of the MainPage runtime class), and which can simply be members of the MainPage implementation type, let's make a list of the members of the C# MainPage class. Per trovare tali membri, cerca in MainPage.xaml.cs e in SampleConfiguration.cs.We find those members by looking in MainPage.xaml.cs and in SampleConfiguration.cs.

Troverai un totale di dodici campi e metodi protected e privateWe find a total of twelve protected and private fields and methods. e i membri public seguenti.And we find the following public members.

  • Il costruttore predefinito MainPage().The default constructor MainPage().
  • I campi statici Current e FEATURE_NAME.The static fields Current and FEATURE_NAME.
  • Le proprietà IsClipboardContentChangedEnabled e Scenarios.The properties IsClipboardContentChangedEnabled and Scenarios.
  • I metodi BuildClipboardFormatsOutputString, DisplayToast, EnableClipboardContentChangedNotifications e NotifyUser.The methods BuildClipboardFormatsOutputString, DisplayToast, EnableClipboardContentChangedNotifications, and NotifyUser.

Poiché questi membri public sono candidati per la dichiarazione in MainPage.idl,It's those public members that are candidates for declaring in MainPage.idl. verranno esaminati singolarmente per stabilire se devono far parte della classe di runtime MainPage o solo dell'implementazione.So let's examine each one and see whether they need to be part of the MainPage runtime class, or whether they need only to be part of its implementation.

  • Il costruttore predefinito MainPage().The default constructor MainPage(). Per una pagina XAML, è normale dichiarare un costruttore predefinito nel file IDL.For a XAML Page, it's normal to declare a default constructor in its IDL. In questo modo, il framework interfaccia utente XAML può attivare il tipo.That way, the XAML UI framework can activate the type.
  • Il campo statico Current viene usato dalle singole pagine XAML dello scenario per accedere all'istanza di MainPage dell'applicazione.The static field Current is used from within the individual scenario XAML pages to access the application's instance of MainPage. Poiché Current non viene usato per interagire con il framework XAML (né nelle unità di compilazione), è possibile riservarlo esclusivamente come membro del tipo di implementazione.Since Current isn't being used to interoperate with the XAML framework (nor is it used across compilation units), we could reserve it to be solely a member of the implementation type. Potresti scegliere di farlo con i tuoi progetti in casi come questo.With your own projects in cases like this, you might choose to do that. Poiché tuttavia il campo è un'istanza del tipo proiettato, è logico dichiararlo nel file IDLBut since the field is an instance of the projected type, it feels logical to declare it in the IDL. ed è proprio ciò che si farà qui, semplificando anche il codice.So that's what we'll do here (and doing so also makes the code slightly cleaner).
  • Il caso del campo FEATURE_NAME statico, accessibile nel tipo MainPage, è simile.It's a similar case for the static FEATURE_NAME field, which is accessed within the MainPage type. Anche in questo caso, scegliendo di dichiararlo nel file IDL, il codice risulta più semplice.Again, choosing to declare it in the IDL makes our code slightly cleaner.
  • La proprietà IsClipboardContentChangedEnabled viene usata solo nella classe OtherScenarios,The property IsClipboardContentChangedEnabled is used only in the OtherScenarios class. quindi durante la conversione, per semplificare l'operazione, verrà impostata come campo privato della classe di runtime OtherScenariosSo during the port, we'll simplify things a little, and make it a private field of the OtherScenarios runtime class. e non verrà inserita nel file IDL.So that one won't go in the IDL.
  • La proprietà Scenarios è una raccolta di oggetti di tipo Scenario (citato in precedenza).The property Scenarios is a collection of objects of type Scenario (a type that we mentioned earlier). Si parlerà del tipo Scenario nella sottosezione successiva, quindi anche la proprietà Scenarios per il momento non verrà presa in considerazione.We'll talk about Scenario in the next subsection, so let's leave the Scenarios property until then, too.
  • I metodi BuildClipboardFormatsOutputString, DisplayToast ed EnableClipboardContentChangedNotifications sono funzioni di utilità relative allo stato generale dell'esempio più che alla pagina principale.The methods BuildClipboardFormatsOutputString, DisplayToast, and EnableClipboardContentChangedNotifications are utility functions that feel more to do with the general state of the sample than about the main page. Durante la conversione, verrà quindi effettuato il refactoring di questi tre metodi in un nuovo tipo di utilità denominato SampleState (che non deve necessariamente essere un tipo di Windows Runtime).So during the port, we'll refactor these three methods onto a new utility type named SampleState (which doesn't need to be a Windows Runtime type). Per tale motivo, questi tre metodi non vengono inseriti nel file IDL.For that reason, these three methods won't go in the IDL.
  • Il metodo NotifyUser viene chiamato dalle singole pagine XAML dello scenario sull'istanza di MainPage restituita dal campo Current statico.The method NotifyUser is called from within the individual scenario XAML pages on the instance of MainPage that's returned from the static Current field. Poiché (come si è già sottolineato) Current è un'istanza del tipo proiettato, è necessario dichiarare NotifyUser nel file IDL.Since (as already noted) Current is an instance of the projected type, we need to declare NotifyUser in the IDL. NotifyUser accetta un parametro di tipo NotifyType,NotifyUser takes a parameter of type NotifyType. di cui si parlerà nella sottosezione successiva.We'll talk about that in the next subsection.

Anche tutti i membri a cui vuoi associare dati devono essere dichiarati in IDL, indipendentemente dal fatto che venga usato {x:Bind} o {Binding}.Any member that you want to databind to also needs to be declared in IDL (whether you're using {x:Bind} or {Binding}). Per altre informazioni, vedi Data binding.For more info, see Data binding.

Si sta creando elenco dei membri da aggiungere e di quelli da non aggiungere al file MainPage.idl,We're making progress: we're developing a list of which members to add, and which not to add, to the MainPage.idl file. ma restano ancora da prendere in esame la proprietà Scenarios e il tipo NotifyType,But we still have to discuss the Scenarios property, and the NotifyType type. che saranno gli argomenti della sezione successiva.So let's do that next.

IDL per i tipi Scenario e NotifyTypeIDL for the Scenario and NotifyType types

La classe Scenario è definita in SampleConfiguration.cs.The Scenario class is defined in SampleConfiguration.cs. È necessario decidere come convertire la classe in C++/WinRT.We have a decision to make about how to port that class to C++/WinRT. Per impostazione predefinita, è probabile che si creerebbe una normale struct C++,By default, we would probably make it an ordinary C++ struct. ma, se Scenario viene usato nei file binari o per interagire con il framework XAML, è necessario dichiararlo nel file IDL come tipo di Windows Runtime.But if Scenario is being used across binaries, or to interoperate with the XAML framework, then it needs to be declared in IDL as a Windows Runtime type.

Se si esamina il codice sorgente C#, si può notare che Scenario viene usato in questo contesto.Studying the C# source code, we find that Scenario is used in this context.

<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;

Una raccolta di oggetti Scenario viene assegnata alla proprietà ItemsSource di ListBox (un controllo di elementi).A collection of Scenario objects is being assigned to the ItemsSource property of a ListBox (which is an items control). Poiché è necessario che Scenario interagisca con XAML, deve essere un tipo di Windows Runtime,Since Scenario does need to interoperate with XAML, it needs to be a Windows Runtime type. quindi deve essere definito nel file IDL.So it needs to be defined in IDL. In seguito alla definizione del tipo Scenario nel file IDL, il sistema di compilazione C++/WinRT genera automaticamente una definizione del codice sorgente di Scenario in un file di intestazione in background. Il nome e la posizione di questo file non sono importanti per questa procedura dettagliata.Defining the Scenario type in IDL causes the C++/WinRT build system to generate a source code definition of Scenario for you in a behind-the-scenes header file (the name and location of which are not important for this walkthrough).

Come ricorderai, MainPage.Scenarios è una raccolta di oggetti Scenario, che, come si è detto, devono essere presenti nel file IDL.And you'll recall that MainPage.Scenarios is a collection of Scenario objects, which we've just said need to be in IDL. Per questo motivo, è necessario dichiarare nel file IDL anche MainPage.Scenarios.For that reason, MainPage.Scenarios also needs to be declared in the IDL.

NotifyType è un elemento enum dichiarato nel file MainPage.xaml.cs di C#.NotifyType is an enum declared in C#'s MainPage.xaml.cs. Poiché NotifyType viene passato a un metodo appartenente alla classe di runtime MainPage, anche NotifyType deve essere un tipo di Windows Runtime e deve essere definito in MainPage.idl.Because we pass NotifyType to a method belonging to the MainPage runtime class, NotifyType too needs to be a Windows Runtime type; and it needs to be defined in MainPage.idl.

Ora aggiungiamo al file MainPage.idl i nuovi tipi e il nuovo membro di Mainpage che si è deciso di dichiarare nel file IDL.Now let's add to the MainPage.idl file the new types and the new member of Mainpage that we've decided to declare in IDL. Contemporaneamente si rimuoveranno dal file IDL i membri segnaposto di Mainpage creati dal modello di progetto di Visual Studio.At the same time, we'll remove from the IDL the placeholder members of Mainpage that the Visual Studio project template gave us.

Nel progetto C++/WinRT apri MainPage.idl e modificalo in modo che sia simile al listato seguente.So, in your C++/WinRT project, open MainPage.idl, and edit it so that it looks like the listing below. Nota che una delle modifiche prevede la sostituzione del nome dello spazio dei nomi Clipboard con SDKTemplate.Note that one of the edits is to change the namespace name from Clipboard to SDKTemplate. Se vuoi, puoi sostituire l'intero contenuto di MainPage.idl con il codice seguente.If you like, you can just replace the entire contents of MainPage.idl with the following code. Nota anche che il nome di Scenario::ClassType viene sostituito con Scenario::ClassName.Another tweak to note is that we're changing the name of Scenario::ClassType to Scenario::ClassName.

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

    enum NotifyType
    {
        StatusMessage,
        ErrorMessage
    };

    [default_interface]
    runtimeclass MainPage : Windows.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);
    };
}

Nota

Per altre informazioni sul contenuto di un file .idl in un progetto C++/WinRT, vedi Microsoft Interface Definition Language 3.0.For more info about the contents of an .idl file in a C++/WinRT project, see Microsoft Interface Definition Language 3.0.

È possibile che nella tua procedura di conversione tu non voglia o non abbia bisogno di modificare il nome dello spazio dei nomi come è stato fatto sopra.With your own porting work, you may not want nor need to change the namespace name like we did above. In questo caso, il nome viene modificato perché lo spazio dei nomi predefinito del progetto C# da convertire è SDKTemplate, mentre il nome del progetto dell'assembly è Clipboard.We're doing it here only because the default namespace of the C# project that we're porting is SDKTemplate; while the name of the project and of the assembly is Clipboard.

Nel corso della conversione eseguita in questa procedura dettagliata, ogni occorrenza del nome dello spazio dei nomi Clipboard nel codice sorgente verrà tuttavia sostituita con SDKTemplate.But, as we proceed with the port in this walkthrough, we'll be changing every occurrence in source code of the Clipboard namespace name to SDKTemplate. Lo spazio dei nomi Clipboard compare anche tra le proprietà del progetto C++/WinRT, quindi anche questa occorrenza verrà modificata ora.There's also a place in C++/WinRT project properties where the Clipboard namespace name appears, so we'll take the opportunity to change that now.

In Visual Studio, per il progetto C++/WinRT, impostare la proprietà del progetto Proprietà comuni > C++/WinRT > Spazio dei nomi radice sul valore SDKTemplate.In Visual Studio, for the C++/WinRT project, set the project property Common Properties > C++/WinRT > Root Namespace to the value SDKTemplate.

Salvare il file IDL e generare nuovamente i file stubSave the IDL, and re-generate stub files

L'argomento Controlli XAML, binding a una proprietà C++/WinRT introduce la nozione di file stub e illustra una procedura dettagliata relativa all'uso di tali file.The topic XAML controls; bind to a C++/WinRT property introduces the notion of stub files, and shows you a walkthrough of them in action. Si è già fatto riferimento agli stub in precedenza in questo argomento, quando è stato spiegato che il sistema di compilazione C++/WinRT converte il contenuto dei file .idl in metadati di Windows e quindi, da tali metadati, uno strumento denominato cppwinrt.exe genera stub su cui puoi basare la tua implementazione.We also mentioned stubs earlier in this topic when we mentioned that C++/WinRT build system turns the contents of your .idl files into Windows Metadata, and then from that metadata a tool named cppwinrt.exe generates stubs on which you can base your implementation.

Ogni volta che aggiungi, rimuovi o modifichi elementi nel codice IDL ed esegui la compilazione, il sistema di compilazione aggiorna le implementazioni di stub nei file stub.Each time you add, remove, or change something in your IDL, and build, the build system updates the stub implementations in those stubs files. Pertanto, ogni volta che modifichi il file IDL ed esegui la compilazione, è consigliabile visualizzare i file stub, copiare eventuali firme modificate e incollarle nel progetto.So each time you change your IDL and build, we recommend that you view those stubs files, copy any changed signatures, and paste them into your project. Di seguito verranno fornite informazioni più specifiche e saranno riportati esempi di come eseguire questa operazione.We'll give more specifics and examples of exactly how to do that in a moment. Il vantaggio di questa operazione consiste tuttavia nel fornire un metodo senza errori per conoscere in qualsiasi momento come devono essere la forma del tipo di implementazione e la firma dei relativi metodi.But the advantage of doing this is to give you an error-free way of knowing at all times what the shape of your implementation type should be, and what the signature of its methods should be.

A questo punto della procedura dettagliata, hai terminato di modificare il file MainPage.idl per il momento, quindi ora è opportuno salvarlo.At this point in the walkthrough, we're done editing the MainPage.idl file for the time being, so you should save it now. Per il momento il progetto non verrà compilato completamente, ma l'esecuzione di una compilazione a questo punto è utile perché genera di nuovo i file stub per MainPage.The project won't build to completion at the moment, but performing a build now is a useful thing to do because it regenerates the stub files for MainPage.

Per questo progetto C++/WinRT, i file stub vengono generati nella cartella \Clipboard\Clipboard\Generated Files\sources,For this C++/WinRT project, the stub files are generated in the \Clipboard\Clipboard\Generated Files\sources folder. dove saranno disponibili al termine della compilazione parziale. La compilazione, come previsto, non verrà eseguita interamente,You'll find them there after the partial build has completed (again, as expected, the build won't succeed entirely. ma il passaggio che ti interessa, la generazione degli stub, avrà esito positivo.But the step that we're interested in—generating stubs—will have succeeded). I file interessati sono MainPage.h e MainPage.cpp.The files we're interested in are MainPage.h and MainPage.cpp.

In questi due file stub vedrai nuove implementazioni di stub dei membri di MainPage che sono stati aggiunti al file IDL (ad esempio, Current e FEATURE_NAME).In those two stub files, you'll see new stub implementations of the members of MainPage that we added to the IDL (Current and FEATURE_NAME, for example). È consigliabile copiare queste implementazioni di stub nei file MainPage.h e MainPage.cpp già presenti nel progetto.You'll want to copy those stub implementations into the MainPage.h and MainPage.cpp files that are already in the project. Contemporaneamente, come è stato fatto con il file IDL, rimuoverai dai file esistenti i membri segnaposto di Mainpage presenti nel modello di progetto di Visual Studio (la proprietà fittizia MyProperty e il gestore dell'evento denominato ClickHandler).At the same time, just as we did with the IDL, we'll remove from those existing files the placeholder members of Mainpage that the Visual Studio project template gave us (the dummy property named MyProperty, and the event handler named ClickHandler).

In realtà l'unico membro della versione corrente di MainPage da conservare è il costruttore.In fact, the only member of the current version of MainPage that we want to keep is the constructor.

Dopo aver copiato i nuovi membri dai file stub, eliminato i membri non desiderati e aggiornato lo spazio dei nomi, i file MainPage.h e MainPage.cpp del progetto dovrebbero avere un aspetto simile a quello degli elenchi di codice seguenti.Once you've copied the new members from the stub files, deleted the members we don't want, and updated the namespace, the MainPage.h and MainPage.cpp files in your project should look like the code listings below. Si noti che esistono due tipi di MainPage:Notice that there are two MainPage types. uno nello spazio dei nomi implementation e un altro nello spazio dei nomi factory_implementation.One in the implementation namespace, and a second one in the factory_implementation namespace. L'unica modifica apportata al tipo factory_implementation è l'aggiunta di SDKTemplate al relativo spazio dei nomi.The only change we've made to the factory_implementation one is to add SDKTemplate to its namespace.

// 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();
    }
}

Per le stringhe, C# usa System.String.For strings, C# uses System.String. Per un esempio, vedi il metodo MainPage.NotifyUser.See the MainPage.NotifyUser method for an example. Nel file IDL dichiariamo una stringa con String e, quando lo strumento cppwinrt.exe genera il codice C++/WinRT, usa il tipo winrt::hstring.In our IDL, we declare a string with String, and when the cppwinrt.exe tool generates C++/WinRT code for us, it uses the winrt::hstring type. Ogni volta che incontrerai una stringa nel codice C#, la convertirai in winrt::hstring.Any time we come across a string in C# code, we'll port that to winrt::hstring. Per altre informazioni, vedi Gestione delle stringhe in C++/WinRT.For more info, see String handling in C++/WinRT.

Per una spiegazione dei parametri const& nelle firme del metodo, vedi Passaggio di parametri.For an explanation of the const& parameters in the method signatures, see Parameter-passing.

Aggiornare tutte le dichiarazioni o i riferimenti rimanenti dello spazio dei nomi e compilareUpdate all remaining namespace declarations/references, and build

Prima di compilare il progetto C++/WinRT, trova tutte le dichiarazioni dello spazio dei nomi Clipboard (e i riferimenti a tale spazio dei nomi) e sostituiscili con SDKTemplate.Before building the C++/WinRT project, find any declarations of (and references to) the Clipboard namespace, and change them to SDKTemplate.

  • MainPage.xaml e App.xaml.MainPage.xaml and App.xaml. Lo spazio dei nomi viene visualizzato nei valori degli attributi x:Class e xmlns:local.The namespace appears in the values of the x:Class and xmlns:local attributes.
  • App.idl.App.idl.
  • App.h.App.h.
  • App.cpp.App.cpp. Esistono due direttive using namespace (cerca la sottostringa using namespace Clipboard) e due qualifiche del tipo MainPage (cerca Clipboard::MainPage),There are two using namespace directives (search for the substring using namespace Clipboard), and two qualifications of the MainPage type (search for Clipboard::MainPage). che è necessario modificare.Those need changing.

Poiché hai rimosso il gestore dell'evento da MainPage, passa anche a MainPage.xaml ed elimina l'elemento Button dal markup.Since we removed the event handler from MainPage, also go into MainPage.xaml and delete the Button element from the markup.

Salva tutti i file.Save all the files. Pulisci (Compila > Pulisci Clipboard) e quindi compila la soluzione.Clean the solution (Build > Clean Clipboard), and then build it. La compilazione avrà esito positivo se le modifiche apportate fino a questo momento sono valide.The build will succeed if the changes you've made so far are valid.

Implementa i membri di MainPage dichiarati nel file IDLImplement the MainPage members that we declared in IDL

Costruttore Current e FEATURE_NAMEThe constructor, Current, and FEATURE_NAME

Ecco il codice pertinente (dal progetto C#) da convertire.Here's the relevant code (from the C# project) that we need to port.

<!-- 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";
...
}
...

A breve riutilizzerai interamente MainPage.xaml (copiandolo).Soon, we'll be re-using MainPage.xaml in its entirety (by copying it). Per il momento, aggiungerai temporaneamente un elemento TextBlock, con il nome appropriato, nel file MainPage.xaml del progetto C++/WinRT.For now, we'll temporarily add a TextBlock element, with the appropriate name, into the MainPage.xaml of the C++/WinRT project.

FEATURE_NAME è un campo statico di MainPage (un campo const C# ha un comportamento essenzialmente statico), definito in SampleConfiguration.cs.FEATURE_NAME is a static field of MainPage (a C# const field is essentially static in its behavior), defined in SampleConfiguration.cs. Per C++/WinRT, invece che come campo (statico), lo imposterai come espressione C++/WinRT di una proprietà di sola lettura (statica).For C++/WinRT, instead of a (static) field, we'll make it the C++/WinRT expression of a (static) read-only property. C++/WinRT esprime il getter di una proprietà come funzione che restituisce il valore di una proprietà e non accetta parametri (funzione di accesso).The C++/WinRT way of expressing a property getter is as a function that returns the property value, and takes no parameters (an accessor). Il campo statico FEATURE_NAME C# diventa quindi la funzione di accesso statica FEATURE_NAME C++/WinRT (in questo caso, restituisce il valore letterale della stringa).So the C# FEATURE_NAME static field becomes the C++/WinRT FEATURE_NAME static accessor function (in this case, returning the string literal).

Per inciso, sarebbe possibile eseguire la stessa operazione se si convertisse una proprietà di sola lettura C#.Incidentally, we'd do the same thing if we were porting a C# read-only property. Per una proprietà scrivibile C#, C++/WinRT esprime il setter di una proprietà come funzione void che accetta il valore della proprietà come parametro (mutatore).For a C# writeable property, the C++/WinRT way of expressing a property setter is as a void function that takes the property value as a parameter (a mutator). In entrambi i casi, se il campo o la proprietà C# è statica, lo sarà anche la funzione di accesso e/o il mutatore C++/WinRT.In either case, if the C# field or property is static, then so is the C++/WinRT accessor and/or mutator.

Current è un campo statico (non costante) di MainPage.Current is a static (not a constant) field of MainPage. Anche in questo caso, verrà impostato come (espressione C++/WinRT di) una proprietà di sola lettura e, anche in questo caso, statica.Again, we'll make it (the C++/WinRT expression of) a read-only property, and again make it static. Se FEATURE_NAME è costante, Current non lo è,Where FEATURE_NAME is constant, Current is not. perciò in C++/WinRT sarà necessario un campo sottostante che verrà restituito dalla funzione di accesso.So in C++/WinRT we'll need a backing field, and our accessor will return that. Nel progetto C++/WinRT dichiariamo quindi in MainPage.h un campo privato statico denominato current, definiamo/inizializziamo current in MainPage.cpp (perché la durata di archiviazione è statica) e vi accediamo tramite una funzione di accesso statica pubblica denominata Current.So, in the C++/WinRT project, we'll declare in MainPage.h a private static field named current, we'll define/initialize current in MainPage.cpp (because it has static storage duration), and we'll access it via a public static accessor function named Current.

Il costruttore stesso esegue un paio di assegnazioni, che sono semplici da convertire.The constructor itself performs a couple of assignments, which are straightforward to port.

Nel progetto C++/WinRT aggiungi un nuovo elemento Visual C++ > Codice > File di C++ (.cpp) con il nome SampleConfiguration.cpp.In the C++/WinRT project, add a new Visual C++ > Code > C++ File (.cpp) item with the name SampleConfiguration.cpp.

Modifica MainPage.xaml, MainPage.h, MainPage.cpp e SampleConfiguration.cpp in modo che corrispondano ai listati seguenti.Edit MainPage.xaml, MainPage.h, MainPage.cpp, and SampleConfiguration.cpp to match the listings below.

<!-- 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";
}

Assicurati anche di eliminare i corpi delle funzioni esistenti da MainPage.cpp per MainPage::Current() e MainPage::FEATURE_NAME() , perché ora stiamo definendo tali metodi altrove.Also, be sure to delete the existing function bodies from MainPage.cpp for MainPage::Current() and MainPage::FEATURE_NAME(), because we're now defining those methods elsewhere.

Come puoi vedere, MainPage::current viene dichiarato come di tipo SDKTemplate::MainPage, ovvero il tipo proiettato.As you can see, MainPage::current is declared as being of type SDKTemplate::MainPage, which is the projected type. Non è di tipo SDKTemplate::implementation::MainPage, che è il tipo di implementazione.It's not of type SDKTemplate::implementation::MainPage, which is the implementation type. Il tipo proiettato è pensato per essere utilizzato all'interno del progetto per l'interoperatività XAML o nei file binari.The projected type is the one that's designed to be consumed either within the project for XAML interoperation, or across binaries. Il tipo di implementazione è quello che usi per implementare le strutture che hai esposto nel tipo proiettato.The implementation type is what you use to implement the facilities that you've exposed on your projected type. Poiché la dichiarazione di MainPage::current (in MainPage.h) viene visualizzata nello spazio dei nomi di implementazione (winrt::SDKTemplate::implementation), un elemento MainPage non qualificato avrebbe fatto riferimento al tipo di implementazione.Because the declaration of MainPage::current (in MainPage.h) appears within the implementation namespace (winrt::SDKTemplate::implementation), an unqualified MainPage would have referred to the implementation type. Eseguiamo quindi la qualifica con SDKTemplate:: perché risulti chiaro che MainPage::current deve essere un'istanza del tipo proiettato winrt::SDKTemplate::MainPage.So, we qualify with SDKTemplate:: in order to be clear that we want MainPage::current to be an instance of the projected type winrt::SDKTemplate::MainPage.

Nel costruttore sono presenti alcuni punti correlati a MainPage::current = *this; che richiedono una spiegazione.In the constructor, there are some points related to MainPage::current = *this; that deserve an explanation.

  • Quando usi il puntatore this all'interno di un membro del tipo di implementazione, il puntatore this è ovviamente un puntatore al tipo di implementazione.When you use the this pointer inside a member of the implementation type, the this pointer is of course a pointer to the implementation type.
  • Per convertire il puntatore this nel tipo proiettato corrispondente, dereferenzialo.To convert the this pointer to the corresponding projected type, dereference it. Se generi il tipo di implementazione dal file IDL (come in questo caso), il tipo di implementazione ha un operatore di conversione che esegue la conversione nel tipo proiettato.Provided you generate your implementation type from IDL (as we have here), the implementation type has a conversion operator that converts to its projected type. Ecco perché in questo caso l'assegnazione funziona.That's why the assignment here works.

Per altre informazioni su tali dettagli, vedi Creazione di istanze e restituzione di interfacce e tipi di implementazione.For more info about those details, see Instantiating and returning implementation types and interfaces.

Nel costruttore è presente anche SampleTitle().Text(FEATURE_NAME());.Also in the constructor is SampleTitle().Text(FEATURE_NAME());. La parte SampleTitle() è una chiamata a una funzione di accesso semplice denominata SampleTitle, che restituisce l'oggetto TextBlock aggiunto al file XAML.The SampleTitle() part is a call to a simple accessor function named SampleTitle, which returns the TextBlock that we added to the XAML. Ogni volta che si applica x:Name a un elemento XAML, il compilatore XAML genera automaticamente una funzione di accesso denominata per l'elemento.Whenever you x:Name a XAML element, the XAML compiler generates an accessor for you that's named for the element. La parte .Text(...) chiama la funzione del mutatore Text sull'oggetto TextBlock restituito dalla funzione di accesso SampleTitle.The .Text(...) part calls the Text mutator function on the TextBlock object that the SampleTitle accessor returned. FEATURE_NAME() chiama la funzione di accesso MainPage::FEATURE_NAME statica per restituire il valore letterale della stringa.And FEATURE_NAME() calls our static MainPage::FEATURE_NAME accessor function to return the string literal. Nel complesso, tale riga di codice imposta la proprietà Text dell'oggetto TextBlock denominato SampleTitle.Altogether, that line of code sets the Text property of the TextBlock named SampleTitle.

Tieni presente che, poiché le stringhe in Windows Runtime sono di tipo wide, per convertire un valore letterale di una stringa, aggiungiamo il prefisso di codifica in caratteri wide L.Note that since strings are wide in the Windows Runtime, to port a string literal we prefix it with the wide-char encoding prefix L. Sostituiamo quindi (ad esempio) "a string literal" con L"a string literal".So we change (for example) "a string literal" to L"a string literal". Vedi anche Valori letterali di stringa di tipo wide.Also see Wide string literals.

ScenariScenarios

Ecco il codice C# pertinente da convertire.Here's the relevant C# code that we need to port.

// 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) }
    };
...
}
...

Dall'analisi precedente, sappiamo che questa raccolta di oggetti Scenario viene visualizzata in un elemento ListBox.From our earlier investigation, we know that this collection of Scenario objects is being displayed in a ListBox. In C++/WinRT sono previsti limiti per il tipo di raccolta che è possibile assegnare alla proprietà ItemsSource di un controllo di elementi.In C++/WinRT, there are limits to the kind of collection that we can assign to the ItemsSource property of an items control. La raccolta deve essere un vettore o un vettore osservabile e i suoi elementi devono essere uno dei seguenti.The collection must be either a vector or an observable vector, and its elements must be one of the following.

Nel caso di IInspectable, se gli elementi non sono classi di runtime, tali elementi devono essere di tipo boxed o unboxed in e da IInspectable.For the IInspectable case, if the elements are not themselves runtime classes, then those elements need to be of a kind that can be boxed and unboxed to and from IInspectable. Devono quindi essere tipi di Windows Runtime. Vedi Boxing e unboxing dei valori scalari in IInspectable.And that means that they have to be Windows Runtime types (see Boxing and unboxing scalar values to IInspectable).

Anche se per questo case study non abbiamo impostato Scenario come classe di runtime,For this case study, we didn't make Scenario a runtime class. si tratta comunque di un'opzione ragionevole.That is still a reasonable option, though. In alcuni casi, durante la procedura di conversione reale, una classe di runtime sarà sicuramente il modo più corretto di procedere.And there'll be cases in your own porting work where a runtime class will definitely be the way to go. Se ad esempio devi impostare il tipo di elemento come observable (vedi Controlli XAML, binding a una proprietà C++/WinRT) o se per qualche motivo l'elemento deve avere dei metodi, si tratta di qualcosa di più di un semplice set di membri dei dati.For example, if you need to make the element type observable (see XAML controls; bind to a C++/WinRT property), or if the element needs to have methods for any other reason, and it's more than just a set of data members.

Poiché in questa procedura dettagliata non useremo una classe di runtime per il tipo Scenario, è necessario prendere in considerazione il boxing.Since, in this walkthrough, we're not going with a runtime class for the Scenario type, then we need to think about boxing. Se Scenario fosse una normale struct C++, non sarebbe possibile eseguirne il boxing.If we'd made Scenario a regular C++ struct, then we wouldn't be able to box it. Poiché tuttavia Scenario è stato dichiarato come struct nel file IDL, è possibile eseguirne il boxing.But we declared Scenario as a struct in IDL, and so we can box it.

È possibile scegliere di eseguire il boxing di Scenario in anticipo o attendere il momento dell'assegnazione a ItemsSource ed eseguire il boxing in modalità JIT.We're left with the choice of boxing the Scenario ahead of time, or waiting until we're about to assign to the ItemsSource, and box them on a just-in-time basis. Di seguito sono riportate alcune considerazioni relative a queste due opzioni.Here are some considerations regarding those two options.

  • Boxing in anticipo.Boxing ahead of time. Per questa opzione, il membro dati è una raccolta di IInspectable pronto per l'assegnazione all'interfaccia utente.For this option, our data member is a collection of IInspectable ready to assign to the UI. Durante l'inizializzazione eseguiamo il boxing degli oggetti Scenario nel membro dati.On initialization, we box the Scenario objects into that data member. Serve una sola copia della raccolta, ma per leggere i campi di un elemento è necessario eseguirne ogni volta l'unboxing.We need only one copy of that collection, but we have to unbox an element every time we needed to read its fields.
  • Boxing JIT.Boxing just in time. Per questa opzione, il membro dati è una raccolta di Scenario.For this option, our data member is a collection of Scenario. Quando è necessario eseguire l'assegnazione all'interfaccia utente, si esegue il boxing degli oggetti Scenario dal membro dati in una nuova raccolta di IInspectable.When the time comes to assign to the UI, we box the Scenario objects from the data member into a new collection of IInspectable. È possibile leggere i campi degli elementi del membro dati senza unboxing, ma sono necessarie due copie della raccolta.We can read the fields of the elements in the data member without unboxing, but we need two copies of the collection.

Come puoi notare, per una raccolta di piccole dimensioni come questa, i pro e i contro si equivalgono,As you can see, for a small collection like this, the pros and cons make it something of a wash. quindi, per questo case study, si userà l'opzione JIT.So, for this case study, we'll go with the just-in-time option.

Il membro scenarios è un campo di MainPage, definito e inizializzato in SampleConfiguration.cs,The scenarios member is a field of MainPage, defined and initialized in SampleConfiguration.cs. e Scenarios è una proprietà di sola lettura di MainPage, definita in MainPage.xaml.cs (e implementata per restituire semplicemente il campo scenarios).And Scenarios is a read-only property of MainPage, defined in MainPage.xaml.cs (and implemented to simply return the scenarios field). Nel progetto C++/WinRT si procederà in modo simile, ma i due membri saranno statici (dal momento che è necessaria una sola istanza nell'applicazione e quindi è possibile accedervi senza bisogno di un'istanza della classe).We'll do something similar in the C++/WinRT project; but we'll make the two members static (since we need only one instance across the application; and so that we can access them without needing a class instance). Li chiameremo rispettivamente scenariosInner e scenarios.And we'll name them scenariosInner and scenarios, respectively. Dichiariamo scenariosInner in MainPage.hWe'll declare scenariosInner in MainPage.h. e, poiché ha una durata di archiviazione statica, lo definiamo/inizializziamo in un file .cpp (in questo caso, SampleConfiguration.cpp).And, because it has static storage duration, we'll define/initialize it in a .cpp file (SampleConfiguration.cpp, in this case).

Modifica MainPage.h e SampleConfiguration.cpp in modo che corrispondano ai listati seguenti.Edit MainPage.h and SampleConfiguration.cpp to match the listings below.

// 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>() },
});

Assicurati anche di eliminare il corpo della funzione esistente da MainPage.cpp per MainPage::scenarios() , perché tale metodo ora viene definito nel file di intestazione.Also, be sure to delete the existing function body from MainPage.cpp for MainPage::scenarios(), because we're now defining that method in the header file.

Come puoi vedere, in SampleConfiguration.cpp inizializziamo il membro dati statico scenariosInner chiamando una funzione helper C++/WinRT denominata winrt::single_threaded_observable_vector.As you can see, in SampleConfiguration.cpp, we initialize the static data member scenariosInner by calling a C++/WinRT helper function named winrt::single_threaded_observable_vector. Tale funzione crea automaticamente un nuovo oggetto raccolta di Windows Runtime e restituisce un'interfaccia IObservableVector.That function creates a new Windows Runtime collection object for us, and returns it as an IObservableVector interface. Poiché in questo esempio la raccolta non è observable (non è necessario che lo sia perché non aggiunge né rimuove elementi dopo l'inizializzazione), sarebbe stato possibile chiamare invece winrt::single_threaded_vector.Since, in this sample, the collection is not observable (it doesn't need to be, because it doesn't add nor remove elements after initialization), we could instead have opted to call winrt::single_threaded_vector. Tale funzione restituisce la raccolta come interfaccia IVector.That function returns the collection as an IVector interface.

Per altre informazioni sulle raccolte e sull'associazione, vedi Controlli di elementi XAML, binding a una raccolta C++/WinRT e Raccolte con C++/WinRT.For more info about collections, and binding to them, see XAML items controls; bind to a C++/WinRT collection, and Collections with C++/WinRT.

Il codice di inizializzazione appena aggiunto fa riferimento a tipi che non sono ancora nel progetto, ad esempio winrt::SDKTemplate::CopyText.The initialization code you just added references types that aren't yet in the project (for example, winrt::SDKTemplate::CopyText. Per risolvere questo problema, prosegui aggiungendo cinque nuove pagine XAML vuote al progetto.To remedy that, let's go ahead and add five new blank XAML pages to the project.

Aggiungere cinque nuove pagine XAML vuoteAdd five new blank XAML pages

Aggiungere un nuovo elemento XAML > Blank Page (C++/WinRT) (Pagina vuota - C++/WinRT) al progetto. Assicurarsi che si tratti del modello di elemento Blank Page (C++/WinRT) (Pagina vuota - C++/WinRT) e non di Pagina vuota.Add a new XAML > Blank Page (C++/WinRT) item to the project (be certain that it's the Blank Page (C++/WinRT) item template, and not the Blank Page one). Denominarlo CopyText.Name it CopyText. La nuova pagina XAML è definita, come previsto, nello spazio dei nomi SDKTemplate.The new XAML page is defined within the SDKTemplate namespace, which is what we want.

Ripeti il processo precedente altre quattro volte e assegna alla pagine XAML i nomi CopyImage, CopyFiles, HistoryAndRoaming e OtherScenarios.Repeat the above process another four times, and named the XAML pages CopyImage, CopyFiles, HistoryAndRoaming, and OtherScenarios.

Ora puoi compilare di nuovo l'app se vuoi.You'll now be able to build again, if you wish.

NotifyUserNotifyUser

Nel progetto C# troverai l'implementazione del metodo MainPage.NotifyUser in MainPage.xaml.cs.In the C# project, you'll find the implementation of the MainPage.NotifyUser method in MainPage.xaml.cs. MainPage.NotifyUser ha una dipendenza da MainPage.UpdateStatus e tale metodo a sua volta ha dipendenze da elementi XAML non ancora convertiti.MainPage.NotifyUser has a dependency on MainPage.UpdateStatus, and that method in turn has dependencies on XAML elements that we haven't yet ported. Per il momento ci limiteremo quindi a impostare come stub nel progetto C++/WinRT un metodo UpdateStatus che verrà convertito più avanti.So for now we'll just stub out an UpdateStatus method in the C++/WinRT project, and we'll port that later.

Ecco il codice C# pertinente da convertire.Here's the relevant C# code that we need to port.

// 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) { ... }{
...

NotifyUser usa l'enumerazione Windows.UI.Core.CoreDispatcherPriority.NotifyUser uses the Windows.UI.Core.CoreDispatcherPriority enum. In C++/WinRT, ogni volta che vuoi usare un tipo da uno spazio dei nomi Windows, devi includere il file di intestazione dello spazio dei nomi Windows C++/WinRT corrispondente. Per altre informazioni, vedi Introduzione a C++/WinRT.In C++/WinRT, whenever you want to use a type from a Windows namespaces, you need to include the corresponding C++/WinRT Windows namespace header file (for more info about that, see Get started with C++/WinRT). In questo caso, come puoi vedere nel listato di codice seguente, l'intestazione è winrt/Windows.UI.Core.h, che verrà inclusa in pch.h.In this case, as you'll see in the code listing below, the header is winrt/Windows.UI.Core.h, and we'll include it in pch.h.

UpdateStatus è privato,UpdateStatus is private. quindi verrà impostato come metodo privato nel tipo di implementazione MainPage.So we'll make that a private method on our MainPage implementation type. Poiché UpdateStatus non deve essere chiamato sulla classe di runtime, non verrà dichiarato nel file IDL.UpdateStatus isn't meant to be called on the runtime class, so we won't declare it in IDL.

Dopo la conversione di MainPage.NotifyUser e l'impostazione come stub di MainPage.UpdateStatus, ecco che cosa si ottiene nel progetto C++/WinRT.After porting MainPage.NotifyUser, and stubbing out MainPage.UpdateStatus, this is what we have in the C++/WinRT project. Dopo questo listato di codice verranno esaminati alcuni dettagli.After this code listing, we'll examine some of the details.

// pch.h
...
#include <winrt/Windows.UI.Core.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 (Dispatcher().HasThreadAccess())
    {
        UpdateStatus(strMessage, type);
    }
    else
    {
        Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [strMessage, type, this]()
            {
                UpdateStatus(strMessage, type);
            });
    }
}
void MainPage::UpdateStatus(hstring const& strMessage, SDKTemplate::NotifyType const& type)
{
    throw hresult_not_implemented();
}
...

In C# puoi usare la notazione del punto per accedere alle proprietà annidate.In C#, you can use dot notation to dot into nested properties. Il tipo MainPage C# può quindi accedere alla proprietà Dispatcher con la sintassi Dispatcher.So, the C# MainPage type can access its own Dispatcher property with the syntax Dispatcher. In C# si possono inserire altri punti in tale valore, ad esempio con la sintassi Dispatcher.HasThreadAccess.And C# can further dot into that value with syntax such as Dispatcher.HasThreadAccess. In C++/WinRT le proprietà vengono implementate come funzioni di accesso, quindi la sintassi differisce solo per il fatto che aggiungi le parentesi per ogni chiamata di funzione.In C++/WinRT, properties are implemented as accessor functions, so the syntax differs only in that you add parentheses for each function call.

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

Quando la versione C# di NotifyUser chiama CoreDispatcher.RunAsync, implementa il delegato di callback asincrono come funzione lambda.When the C# version of NotifyUser calls CoreDispatcher.RunAsync, it implements the asynchronous callback delegate as a lambda function. La versione C++/WinRT si comporta nello stesso modo, ma la sintassi è leggermente diversa.The C++/WinRT version does the same thing, but the syntax is a little different. In C++/WinRT si acquisiscono i due parametri da usare, oltre al puntatore this (perché chiameremo una funzione membro).In C++/WinRT, we capture the two parameters that we're going to use, as well as the this pointer (since we're going to call a member function). Per altre informazioni sull'implementazione dei delegati come funzioni lambda e per esempi di codice, vedi l'argomento Gestire eventi mediante i delegati in C++/WinRT.There's more info about implementing delegates as lambdas, and code examples, in the topic Handle events by using delegates in C++/WinRT. In questo caso specifico, si può anche ignorare la parte var task =.Also, we can disregard the var task = part in this particular case. Non è attesa la restituzione dell'oggetto asincrono, quindi non è necessario archiviarlo.We're not waiting on the returned asynchronous object, so there's no need to store it.

Implementare i membri rimanenti di MainPageImplement the remaining MainPage members

Creiamo un elenco completo dei membri di MainPage (implementati in MainPage.xaml.cs e in SampleConfiguration.cs) per verificare quali sono stati convertiti fino a questo momento e quali devono ancora essere convertiti.Let's make a full list of the members of MainPage (implemented across MainPage.xaml.cs and SampleConfiguration.cs) so that we can see which ones we've ported so far, and which ones are yet to do.

MembroMember AccessoAccess StatoStatus
Costruttore MainPageMainPage constructor public ConvertitoPorted
Proprietà CurrentCurrent property public ConvertitoPorted
Proprietà FEATURE_NAMEFEATURE_NAME property public ConvertitoPorted
Proprietà IsClipboardContentChangedEnabledIsClipboardContentChangedEnabled property public Non avviatoNot started
Proprietà ScenariosScenarios property public ConvertitoPorted
Metodo BuildClipboardFormatsOutputStringBuildClipboardFormatsOutputString method public Non avviatoNot started
Metodo DisplayToastDisplayToast method public Non avviatoNot started
Metodo EnableClipboardContentChangedNotificationsEnableClipboardContentChangedNotifications method public Non avviatoNot started
Metodo NotifyUserNotifyUser method public ConvertitoPorted
Metodo OnNavigatedToOnNavigatedTo method protected Non avviatoNot started
Campo isApplicationWindowActiveisApplicationWindowActive field private Non avviatoNot started
Campo needToPrintClipboardFormatneedToPrintClipboardFormat field private Non avviatoNot started
Campo scenariosscenarios field private ConvertitoPorted
Metodo Button_ClickButton_Click method private Non avviatoNot started
Metodo DisplayChangedFormatsDisplayChangedFormats method private Non avviatoNot started
Metodo Footer_ClickFooter_Click method private Non avviatoNot started
Metodo HandleClipboardChangedHandleClipboardChanged method private Non avviatoNot started
Metodo OnClipboardChangedOnClipboardChanged method private Non avviatoNot started
Metodo OnWindowActivatedOnWindowActivated method private Non avviatoNot started
Metodo ScenarioControl_SelectionChangedScenarioControl_SelectionChanged method private Non avviatoNot started
Metodo UpdateStatusUpdateStatus method private Impostato come stubStubbed out

Nelle sottosezioni successive si parlerà dei membri non ancora convertiti.We'll talk about the as-yet-unported members in the next few subsections, then.

Nota

Di volta in volta si incontreranno nel codice sorgente riferimenti a elementi dell'interfaccia utente nel markup XAML (in MainPage.xaml).From time to time, we'll come across references in the source code to UI elements in the XAML markup (in MainPage.xaml). In questi casi, si aggiungeranno al codice XAML semplici elementi segnaposto come soluzione temporanea.As we come to these references, we'll temporarily work around them by adding simple placeholder elements to the XAML. In questo modo, il progetto continuerà a essere compilato dopo ogni sottosezione.That way, the project will continue to build after each subsection. L'alternativa è risolvere i riferimenti copiando ora l'intero contenuto di MainPage.xaml dal progetto C# al progetto C++/WinRT,The alternative is to resolve the references by copying the entire contents of MainPage.xaml from the C# project to the C++/WinRT project now. ma in tal caso, poiché passerà molto tempo prima di potersi fermare ed eseguire una nuova compilazione, si rischierebbe di ignorare eventuali errori di digitazione o di altro tipo.But if we do that then it'll be a long time before we can come to a pit stop and build again (thus potentially obscuring any typos or other errors that we make along the way).

Solo dopo aver completato la conversione del codice imperativo per la classe MainPage, potremo copiare il contenuto del file XAML con la certezza che il progetto continuerà a essere compilato.Once we're done porting the imperative code for the MainPage class, then we'll copy the contents of the XAML file and be confident that the project will still build.

IsClipboardContentChangedEnabledIsClipboardContentChangedEnabled

Si tratta di una proprietà C# get-set la cui impostazione predefinita è false.This is a get-set C# property that defaults to false. È un membro di MainPage ed è definita in SampleConfiguration.cs.It's a member of MainPage, and is defined in SampleConfiguration.cs.

Per C++/WinRT sono necessari una funzione di accesso, una funzione mutatore e un membro dati sottostante come campo.For C++/WinRT, we'll need an accessor function, a mutator function, and a backing data member as a field. Poiché IsClipboardContentChangedEnabled rappresenta lo stato di uno degli scenari dell'esempio e non lo stato di MainPage, creeremo i nuovi membri in un nuovo tipo di utilità denominato SampleState,Since IsClipboardContentChangedEnabled represents the state of one of the scenarios in the sample, rather than the state of MainPage itself, we'll create the new members on a new utility type called SampleState. che verrà implementata nel file di codice sorgente SampleConfiguration.cpp e i membri saranno static (dal momento che è necessaria una sola istanza nell'applicazione e quindi è possibile accedervi senza bisogno di un'istanza della classe).And we'll implement that in our SampleConfiguration.cpp source code file, and we'll make the members static (since we need only one instance across the application; and so that we can access them without needing a class instance).

Per trasferire SampleConfiguration.cpp nel progetto C++/WinRT, aggiungi un nuovo elemento Visual C++ > Codice > File di intestazione (.h) con il nome SampleConfiguration.h.To accompany our SampleConfiguration.cpp in the C++/WinRT project, add a new Visual C++ > Code > Header File (.h) item with the name SampleConfiguration.h. Modifica SampleConfiguration.h e SampleConfiguration.cpp in modo che corrispondano ai listati seguenti.Edit SampleConfiguration.h and SampleConfiguration.cpp to match the listings below.

// 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;
    }
}

Ancora una volta è necessario definire un campo con risorsa di archiviazione static (ad esempio, SampleState::isClipboardContentChangedEnabled) nell'applicazione e un file .cpp è l'ideale (in questo caso, SampleConfiguration.cpp).Again, a field with static storage (such as SampleState::isClipboardContentChangedEnabled) must be defined once in the application, and a .cpp file is a good place for that (SampleConfiguration.cpp in this case).

BuildClipboardFormatsOutputStringBuildClipboardFormatsOutputString

Questo metodo è un membro pubblico di MainPage ed è definito in SampleConfiguration.cs.This method is a public member of MainPage, and it's defined in 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();
}
...

In C++/WinRT BuildClipboardFormatsOutputString sarà un metodo statico pubblico di SampleState.In C++/WinRT, we'll make BuildClipboardFormatsOutputString a public static method of SampleState. È possibile impostarlo come static perché non accede a nessun membro dell'istanza.We can make it static because it doesn't access any instance members.

Per usare i tipi Clipboard e DataPackageView in C++/WinRT, dovremo includere il file di intestazione dello spazio dei nomi Windows C++/WinRT winrt/Windows.ApplicationModel.DataTransfer.h.To use the Clipboard and DataPackageView types in C++/WinRT, we'll need to include the C++/WinRT Windows namespace header file winrt/Windows.ApplicationModel.DataTransfer.h.

In C# la proprietà DataPackageView.AvailableFormats è un elemento IReadOnlyList, quindi è possibile accedere alla sua proprietà Count.In C#, the DataPackageView.AvailableFormats property is an IReadOnlyList, so we can access the Count property of that. In C++/WinRT la funzione di accesso DataPackageView::AvailableFormats restituisce un elemento IVectorView, che ha una funzione di accesso Size che è possibile chiamare.In C++/WinRT, the DataPackageView::AvailableFormats accessor function returns an IVectorView, which has a Size accessor function that we can call.

Per convertire l'uso del tipo System.Text.StringBuilder C#, useremo il tipo C++ standard std::wostringstream.To port the use of the C# System.Text.StringBuilder type, we'll make use of the standard C++ type std::wostringstream. Questo tipo è un flusso di output per le stringhe wide e per usarlo, è necessario includere il file di intestazione sstream.That type is an output stream for wide strings (and to use it we'll need to include the sstream header file). Invece di usare un metodo Append come con StringBuilder, usi l'operatore di inserimento (<<) con un flusso di output, ad esempio wostringstream.Instead of using an Append method like you do with a StringBuilder, you use the insertion operator (<<) with an output stream such as wostringstream. Per altre informazioni, vedi Programmazione di iostream e Formattazione di stringhe C++/WinRT.For more info, see iostream programming, and Formatting C++/WinRT strings.

Il codice C# costruisce un elemento StringBuilder con la parola chiave new.The C# code constructs a StringBuilder with the new keyword. In C# gli oggetti sono, per impostazione predefinita, tipi di riferimento dichiarati nell'heap con new.In C#, objects are reference types by default, declared on the heap with new. Nel linguaggio C++ standard moderno gli oggetti sono, per impostazione predefinita, tipi di valori dichiarati nello stack (senza usare new).In modern standard C++, objects are value types by default, declared on the stack (without using new). Convertiamo quindi StringBuilder output = new StringBuilder(); in C++/WinRT semplicemente come std::wostringstream output;.So we port StringBuilder output = new StringBuilder(); to C++/WinRT as simply std::wostringstream output;.

La parola chiave var C# chiede al compilatore di dedurre un tipo.The C# var keyword asks the compiler to infer a type. Converti var ad auto in C++/WinRT.You port var to auto in C++/WinRT. Tuttavia, in C++/WinRT esistono casi in cui (per evitare copie) vuoi un riferimento a un tipo dedotto ed esprimi un riferimento lvalue a un tipo dedotto con auto&.But in C++/WinRT, there are cases where (in order to avoid copies) you want a reference to an inferred (or deduced) type, and you express an lvalue reference to a deduced type with auto&. Esistono anche casi in cui vuoi un tipo speciale di riferimento che esegua correttamente il binding, indipendentemente dal fatto che venga inizializzato con lvalue o con rvalue.There are also cases where you want a special kind of reference that binds correctly whether it's initialized with an lvalue or with an rvalue. Puoi esprimerlo con auto&&.And you express that with auto&&. Questa è la forma che vedi usata nel ciclo for nel codice convertito riportato di seguito.That's the form that you see used in the for loop in the ported code below. Per un'introduzione agli lvalue e rvalue, vedi Categorie di valori e riferimenti.For an introduction to lvalues and rvalues, see Value categories, and references to them.

Modifica pch.h, SampleConfiguration.h e SampleConfiguration.cpp in modo che corrispondano ai listati seguenti.Edit pch.h, SampleConfiguration.h, and SampleConfiguration.cpp to match the listings below.

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

// SampleConfiguration.h
...
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() };
}

Nota

La sintassi nella riga di codice DataPackageView clipboardContent{ Clipboard::GetContent() }; usa una funzionalità del linguaggio C++ standard moderno, denominata inizializzazione uniforme, con il suo uso caratteristico di parentesi graffe al posto di un segno =.The syntax in the line of code DataPackageView clipboardContent{ Clipboard::GetContent() }; uses a feature of modern standard C++ called uniform initialization, with its characteristic use of curly brackets instead of an = sign. Questa sintassi rende chiaro che sia in corso un'inizializzazione anziché un'assegnazione.That syntax makes it clear that initialization, rather than assignment, is taking place. Se preferisci la forma di sintassi che sembra un'assegnazione (anche se in realtà non lo è), puoi sostituire la sintassi precedente con il codice DataPackageView clipboardContent = Clipboard::GetContent(); equivalente.If you prefer the form of syntax that looks like assignment (but actually isn't), then you can replace the syntax above with the equivalent DataPackageView clipboardContent = Clipboard::GetContent();. È tuttavia consigliabile acquisire familiarità con entrambe le modalità di espressione dell'inizializzazione, poiché è probabile che vengano spesso usate tutte e due nel codice che trovi.It's a good idea to become comfortable with both ways of expressing initialization, though, because you're likely to see both used frequently in the code you encounter.

DisplayToastDisplayToast

DisplayToast è un metodo statico pubblico della classe MainPage C#, che viene definito in SampleConfiguration.cs.DisplayToast is a public static method of the C# MainPage class, and you'll find it defined in SampleConfiguration.cs. In C++/WinRT si creerà un metodo statico pubblico di SampleState.In C++/WinRT, we'll make it a public static method of SampleState.

Abbiamo già visto la maggior parte dei dettagli e delle tecniche rilevanti per la conversione di questo metodo.We've already encountered most of the details and techniques that are relevant to porting this method. Un nuovo elemento da osservare è la conversione di un valore letterale stringa verbatim C# (@) in un valore letterale stringa non elaborato C++ standard (LR).One new item to note is that you port a C# verbatim string literal (@) to a standard C++ raw string literal (LR).

Inoltre quando fai riferimento ai tipi ToastNotification e XmlDocument in C++/WinRT, puoi qualificarli in base al nome dello spazio dei nomi oppure modificare SampleConfiguration.cpp e aggiungere le direttive using namespace, come nell'esempio seguente.Also, when you reference the ToastNotification and XmlDocument types in C++/WinRT, you can either qualify them by namespace name, or you can edit SampleConfiguration.cpp and add using namespace directives such as the following example.

using namespace Windows::UI::Notifications;

Hai la stessa possibilità quando fai riferimento al tipo XmlDocument o a qualsiasi altro tipo di Windows Runtime.You have the same choice when you reference the XmlDocument type, and whenever you reference any other Windows Runtime type.

A eccezione di questi elementi, è sufficiente seguire le stesse indicazioni di prima anche per i passaggi seguenti.Apart from those items, just follow the same guidance that you did previously to accomplish the following steps.

  • Dichiara il metodo in SampleConfiguration.h e definiscilo in SampleConfiguration.cpp.Declare the method in SampleConfiguration.h, and define it in SampleConfiguration.cpp.
  • Modifica pch.h per includere i file di intestazione dello spazio dei nomi Windows C++/WinRT necessari.Edit pch.h to include any necessary C++/WinRT Windows namespace header files.
  • Costruisci gli oggetti C++/WinRT nello stack, non nell'heap.Construct C++/WinRT objects on the stack, not on the heap.
  • Sostituisci le chiamate alle funzioni di accesso get delle proprietà con la sintassi di chiamata alle funzioni (()).Replace calls to property get accessors with function-call syntax (()).

Dimenticare di includere i file di intestazione dello spazio dei nomi Windows C++/WinRT necessari è molto spesso causa di errori del compilatore/linker.A very common cause of compiler/linker errors is forgetting to include the C++/WinRT Windows namespace header files that you need. Per altre informazioni su un possibile errore, vedi Per quale motivo il linker restituisce un errore "LNK2019: Unresolved external symbol" (Simbolo esterno non risolto)?.For more info about one possible error, see Why is the linker giving me a "LNK2019: Unresolved external symbol" error?.

Per continuare con la procedura dettagliata e convertire da solo DisplayToast, è possibile confrontare i risultati con il codice nella versione C++/WinRT nel file ZIP del codice sorgente dell'esempio Clipboard scaricato.If you want to follow along with the walkthrough and port DisplayToast yourself, then you can compare your results to the code in the C++/WinRT version in the ZIP of the Clipboard sample source code that you downloaded.

EnableClipboardContentChangedNotificationsEnableClipboardContentChangedNotifications

EnableClipboardContentChangedNotifications è un metodo statico pubblico della classe MainPage C# ed è definito in SampleConfiguration.cs.EnableClipboardContentChangedNotifications is a public static method of the C# MainPage class, and it's defined in SampleConfiguration.cs.

// 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) { ... }
...

In C++/WinRT si creerà un metodo statico pubblico di SampleState.In C++/WinRT, we'll make it a public static method of SampleState.

In C# usi la sintassi degli operatori += e -= per registrare e revocare i delegati di gestione degli eventi.In C#, you use the += and -= operator syntax to register and revoke event-handling delegates. In C++/WinRT puoi scegliere tra più opzioni sintattiche per registrare/revocare un delegato, come illustrato in Gestire eventi mediante i delegati in C++/WinRT.In C++/WinRT, you have several syntactic options to register/revoke a delegate, as described in Handle events by using delegates in C++/WinRT. Tuttavia, in genere esegui la registrazione e la revoca con chiamate a una coppia di funzioni specificate per l'evento.But the general form is that you register and revoke with calls to a pair of functions named for the event. Per la registrazione, passi il delegato alla funzione di registrazione e in cambio recuperi un token di revoca (winrt::event_token).To register, you pass your delegate to the registering function, and you retrieve a revocation token in return (a winrt::event_token). Per la revoca, passi questo token alla funzione di revoca.To revoke, you pass that token to the revocation function. In questo caso, il gestore è statico e (come puoi vedere nel listato di codice seguente) la sintassi della chiamata alla funzione è semplice.In this case, the hander is static and (as you can see in the following code listing) the function call syntax is straightforward.

Token simili vengono effettivamente usati, in background, in C#.Similar tokens are actually used, behind the scenes, in C#. Il linguaggio, tuttavia, rende implicito il dettaglio.But the language makes that detail implicit. C++/WinRT lo rende esplicito.C++/WinRT makes it explicit.

Il tipo object appare nelle firme dei gestori di eventi C#.The object type appears in the C# event handler signatures. Nel linguaggio C# object è un alias per il tipo System.Object .NET.In the C# language, object is an alias for the .NET System.Object type. L'equivalente in C++/WinRT è winrt::Windows::Foundation::IInspectable.The equivalent in C++/WinRT is winrt::Windows::Foundation::IInspectable. Vedrai quindi IInspectable nei gestori di eventi C++/WinRT.So, you'll see IInspectable in the C++/WinRT event handlers.

Modifica SampleConfiguration.h e SampleConfiguration.cpp in modo che corrispondano ai listati seguenti.Edit SampleConfiguration.h and SampleConfiguration.cpp to match the listings below.

// SampleConfiguration.h
...
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, Windows::UI::Core::WindowActivatedEventArgs const& e);
...

// SampleConfiguration.cpp
...
using namespace Windows::Foundation;
using namespace Windows::UI::Core;
using namespace Windows::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){}

Per il momento lascia i delegati di gestione degli eventi (OnClipboardChanged e OnWindowActivated) come stub.Leave the event-handling delegates themselves (OnClipboardChanged and OnWindowActivated) as stubs for now. Sono già presenti nell'elenco dei membri da convertire, quindi ce ne occuperemo nelle sottosezioni successive.They're already on our list of members to port, so we'll get to them in later subsections.

OnNavigatedToOnNavigatedTo

OnNavigatedTo è un metodo protetto della classe C# MainPage ed è definito in MainPage.xaml.cs.OnNavigatedTo is a protected method of the C# MainPage class, and it's defined in MainPage.xaml.cs. Eccolo, con l'elemento ListBox XAML a cui fa riferimento.Here it is, together with XAML ListBox that it references.

<!-- 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;
    }
}

È un metodo importante e interessante, perché consente di assegnare la raccolta di oggetti Scenario all'interfaccia utente.It's an important and interesting method, because here's where our collection of Scenario objects is assigned to the UI. Il codice C# compila una classe System.Collections.Generic.List di oggetti Scenario e la assegna alla proprietà ItemsSource di ListBox (un controllo di elementi).The C# code builds a System.Collections.Generic.List of Scenario objects, and assigns that to the ItemsSource property of a ListBox (which is an items control). In C# usiamo l'interpolazione di stringhe per compilare il titolo per ogni oggetto Scenario. Nota l'uso del carattere speciale $.And, in C#, we use string interpolation to build the title for each Scenario object (note the use of the The $ special character).

In C++/WinRT imposteremo OnNavigatedTo come metodo pubblico di MainPageIn C++/WinRT, we'll make OnNavigatedTo a public method of MainPage. e aggiungeremo un elemento ListBox al codice XAML per eseguire correttamente la compilazione.And we'll add a stub ListBox element to the XAML so that a build will succeed. Dopo il listato di codice, verranno esaminati alcuni dettagli.After the code listing, we'll examine some of the details.

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

// MainPage.cpp
...
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::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);
    }
}
...

Chiamiamo ancora la funzione winrt::single_threaded_observable_vector, ma questa volta per creare una raccolta di IInspectable,Again, we're calling the winrt::single_threaded_observable_vector function, but this time to create a collection of IInspectable. che abbiamo visto quando è stata presa la decisione di eseguire il boxing degli oggetti Scenario in modalità JIT.That was part of the decision we made to perform the boxing of our Scenario objects on a just-in-time basis.

Inoltre, invece dell'uso dell'interpolazione di stringhe da parte di C# qui, viene usata una combinazione della funzione to_hstring e dell'operatore di concatenazione di winrt::hstring.And, in place of C#'s use of string interpolation here, we use a combination of the to_hstring function and the concatenation operator of winrt::hstring.

isApplicationWindowActiveisApplicationWindowActive

In C# isApplicationWindowActive è un semplice campo bool privato appartenente alla classe MainPage, definito in SampleConfiguration.cs.In C#, isApplicationWindowActive is a simple private bool field belonging to the MainPage class, and it's defined in SampleConfiguration.cs. Il valore predefinito è false.It defaults to false. In C++/WinRT verrà impostato come campo statico pubblico di SampleState (per i motivi già visti) nei file SampleConfiguration.h e SampleConfiguration.cpp, con la stessa impostazione predefinita.In C++/WinRT, we'll make it a public static field of SampleState (for the reasons we've already described) in the SampleConfiguration.h and SampleConfiguration.cpp files, with the same default.

Abbiamo già visto come dichiarare, definire e inizializzare un campo statico.We've already seen how to declare, define, and initialize a static field. Se necessario, ricontrolla la procedura seguita prima per il campo isClipboardContentChangedEnabled e ripetila per isApplicationWindowActive.For a refresher, look back to what we did with the isClipboardContentChangedEnabled field, and do the same with isApplicationWindowActive.

needToPrintClipboardFormatneedToPrintClipboardFormat

Il modello è uguale a quello di isApplicationWindowActive. Vedi l'intestazione subito prima di questa.Same pattern as isApplicationWindowActive (see the heading immediately before this one).

Button_ClickButton_Click

Button_Click è un metodo privato (di gestione degli eventi) della classe MainPage C#, definito in MainPage.xaml.cs.Button_Click is a private (event-handling) method of the C# MainPage class, and it's defined in MainPage.xaml.cs. Di seguito è illustrato questo metodo, insieme a SplitView XAML a cui fa riferimento e a ToggleButton che lo registra.Here it is, together with the XAML SplitView that it references, and the ToggleButton that registers it.

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

Questo è il codice equivalente, convertito in C++/WinRT.And here's the equivalent, ported to C++/WinRT. Nota che nella versione C++/WinRT il gestore dell'evento è public. Come puoi vedere, lo dichiari prima delle dichiarazioni private:.Note that in the C++/WinRT version, the event handler is public (as you can see, you declare it before the private:declarations). Infatti, un gestore eventi registrato nel markup XAML come questo, deve essere public in C++/WinRT per consentire al markup XAML di accedervi.This is because an event handler that's registered in XAML markup, like this one is, needs to be public in C++/WinRT in order for the XAML markup to access it. Se registri un gestore eventi nel codice imperativo, come abbiamo fatto in precedenza in MainPage::EnableClipboardContentChangedNotifications, non è necessario che il gestore eventi sia public.If you register an event handler in imperative code (like we did in MainPage::EnableClipboardContentChangedNotifications earlier), then the event handler doesn't need to be public.

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

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

DisplayChangedFormatsDisplayChangedFormats

In C# DisplayChangedFormats è un metodo privato appartenente alla classe MainPage, definito in SampleConfiguration.cs.In C#, DisplayChangedFormats is a private method belonging to the MainPage class, and it's defined in SampleConfiguration.cs.

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

In C++/WinRT verrà impostato come campo statico privato di SampleState (non accede a nessun membro dell'istanza), nei file SampleConfiguration.h e SampleConfiguration.cpp.In C++/WinRT, we'll make it a private static field of SampleState (it doesn't access any instance members), in the SampleConfiguration.h and SampleConfiguration.cpp files. Il codice C# per questo metodo non usa System.Text.StringBuilder, ma la formattazione delle stringhe è tale che, per la versione C++/WinRT, è consigliabile usare std::wostringstream.The C# code for this method doesn't use System.Text.StringBuilder; but it does enough string formatting that for the C++/WinRT version this is another good place to use std::wostringstream.

Invece della proprietà statica System.Environment.NewLine, usata nel codice C#, inseriremo il carattere di nuova riga C++ standard std::endl nel flusso di output.Instead of the static System.Environment.NewLine property, which is used in the C# code, we'll insert the standard C++ std::endl (a newline character) into the output stream.

// 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);
}

La progettazione della versione C++/WinRT precedente presenta una piccola inefficienza.There is a small inefficiency in the design of the C++/WinRT version above. Prima creiamo un elemento std::wostringstream,First, we create a std::wostringstream. ma chiamiamo anche il metodo BuildClipboardFormatsOutputString, che abbiamo convertito in precedenza.But we also call the BuildClipboardFormatsOutputString method (which we ported earlier). Tale metodo crea il proprio std::wostringstream,That method creates its own std::wostringstream. quindi trasforma il flusso in winrt::hstring e lo restituisce.And it turns its stream into a winrt::hstring and returns that. Chiamiamo la funzione hstring::c_str per trasformare nuovamente tale hstring restituita in una stringa di tipo C e quindi la inseriamo nel flusso.We call the hstring::c_str function to turn that returned hstring back into a C-style string, and then we insert that into our stream. Sarebbe più efficiente creare un solo std::wostringstream e passarlo (o passare un riferimento) in modo che i metodi possano inserirvi direttamente le stringhe.It would be more efficient to create just one std::wostringstream, and pass (a reference to) that around, so that methods can insert strings into it directly.

Ed è proprio ciò che accade nella versione C++/WinRT del codice sorgente dell'esempio Clipboard (nel file ZIP scaricato).That's what we do in the C++/WinRT version of the Clipboard sample source code (in the ZIP that you downloaded). In tale codice sorgente è presente un nuovo metodo statico privato denominato SampleState::AddClipboardFormatsOutputString, che usa un riferimento a un flusso di output.In that source code, there's a new private static method named SampleState::AddClipboardFormatsOutputString, which takes and operates on a reference to an output stream. Viene quindi effettuato il refactoring dei metodi SampleState::DisplayChangedFormats e SampleState::BuildClipboardFormatsOutputString per chiamare il nuovo metodo.And then the methods SampleState::DisplayChangedFormats and SampleState::BuildClipboardFormatsOutputString are refactored to call that new method. Dal punto di vista funzionale equivale ai listati di codice di questo argomento, ma è più efficiente.It's functionally equivalent to the code listings in this topic, but it's more efficient.

Footer_Click è un gestore di eventi asincrono appartenente alla classe MainPage C#, definito in MainPage.xaml.cs.Footer_Click is an asynchronous event handler belonging to the C# MainPage class, and it's defined in MainPage.xaml.cs. Il listato di codice riportato di seguito dal punto di vista funzionale equivale al metodo del codice sorgente scaricato,The code listing below is functionally equivalent to the method in the source code that you downloaded. ma qui è stato decompresso in quattro righe, per capire meglio come funziona e quindi decidere come convertirlo.But here I've unpacked it from one line to four, to make it easier to see what it's doing, and consequently how we should port it.

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);
}

Anche se tecnicamente il metodo è asincrono, non esegue operazioni dopo await, quindi non necessita di await (né della parola chiave async).While, technically, the method is asynchronous, it doesn't do anything after the await, so it doesn't need the await (nor the async keyword). È probabile che li usi per evitare il messaggio di IntelliSense in Visual Studio.It probably uses them in order to avoid the IntelliSense message in Visual Studio.

Anche il metodo C++/WinRT equivalente sarà asincrono (perché chiama Launcher.LaunchUriAsync),The equivalent C++/WinRT method will also be asynchronous (because it calls Launcher.LaunchUriAsync). ma non necessita dell'istruzione co_await né deve restituire un oggetto asincrono.But it doesn't need to co_await, nor to return an asynchronous object. Per informazioni su co_await e sugli oggetti asincroni, vedi Concorrenza e operazioni asincrone con C++/WinRT.For info about co_await and asynchronous objects, see Concurrency and asynchronous operations with C++/WinRT.

A questo punto si parlerà del funzionamento del metodo.Now let's talk about what the method is doing. Poiché si tratta di un gestore per l'evento Click di un HyperlinkButton, l'oggetto denominato sender è effettivamente un HyperlinkButton.Because this is an event handler for the Click event of a HyperlinkButton, the object named sender is actually a HyperlinkButton. La conversione dei tipi è quindi sicura. In alternativa, potremmo esprimere questa conversione come sender as HyperlinkButton.So the type conversion is safe (we could alternatively have expressed this conversion as sender as HyperlinkButton). Ora si recupererà il valore della proprietà Tag. Se osservi il markup XAML nel progetto C#, vedrai che è impostata su una stringa che rappresenta un URL Web.Next, we retrieve the value of the Tag property (if you look at the XAML markup in the C# project, you'll see that this is set to a string representing a web url). Anche se la proprietà FrameworkElement.Tag (HyperlinkButton è un FrameworkElement) è di tipo object, in C# è possibile convertirla in stringa con Object.ToString.Although the FrameworkElement.Tag property (HyperlinkButton is a FrameworkElement) is of type object, in C# we can stringify that with Object.ToString. Dalla stringa risultante si costruisce un oggetto Uri.From the resulting string, we construct a Uri object. Infine, con l'aiuto della shell, si avvia un browser e si passa all'URL.And finally (with the help of the Shell) we launch a browser and navigate to the url.

Ecco il metodo convertito in C++/WinRT (anche in questo caso, espanso per maggiore chiarezza), a cui segue una descrizione dei dettagli.Here's the method ported to C++/WinRT (again, expanded for clarity), after which is a description of the details.

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

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

// MainPage.cpp
...
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::UI::Xaml::Controls;
...
void MainPage::Footer_Click(Windows::Foundation::IInspectable const& sender, Windows::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);
}

Come sempre, impostiamo come public il gestore dell'evento.As always, we make the event handler public. Usiamo la funzione as sull'oggetto sender per convertirlo in HyperlinkButton.We use the as function on the sender object to convert it to HyperlinkButton. In C++/WinRT la proprietà Tag è un'interfaccia IInspectable (l'equivalente di Object),In C++/WinRT, the Tag property is an IInspectable (the equivalent of Object). ma non esiste alcuna Tostring in IInspectable.But there's no Tostring on IInspectable. È invece necessario eseguire l'unboxing di IInspectable in un valore scalare (in questo caso, una stringa).Instead, we have to unbox the IInspectable to a scalar value (a string, in this case). Per altre informazioni su boxing e unboxing, vedi ancora una volta Boxing e unboxing dei valori scalari in IInspectable.Again, for more info on boxing and unboxing, see Boxing and unboxing scalar values to IInspectable.

Le ultime due righe ripetono i modelli di conversione visti prima e sono molto simili a quelle della versione C#.The last two lines repeat porting patterns we've seen before, and they pretty much echo the C# version.

HandleClipboardChangedHandleClipboardChanged

Non c'è niente di nuovo per la conversione di questo metodo.There's nothing new involved in porting this method. È possibile confrontare le versioni C# e C++ /WinRT nel file ZIP del codice sorgente dell'esempio Clipboard scaricato.You can compare the C# and C++/WinRT versions in the ZIP of the Clipboard sample source code that you downloaded.

OnClipboardChanged e OnWindowActivatedOnClipboardChanged and OnWindowActivated

Fino a questo punto abbiamo solo stub vuoti per questi due gestori di eventi,So far we have only empty stubs for these two event handlers. ma convertirli è semplice e non serve aggiungere nulla.But porting them is straightforward, and it doesn't raise anything new to discuss.

ScenarioControl_SelectionChangedScenarioControl_SelectionChanged

È un altro gestore di eventi privato che appartiene alla classe MainPage C#, definito in MainPage.xaml.cs.This is another private event handler belonging to the C# MainPage class, and defined in MainPage.xaml.cs. In C++/WinRT lo imposteremo come pubblico e lo implementeremo in MainPage.h e MainPage.cpp.In C++/WinRT, we'll make it public, and implement it in MainPage.h and MainPage.cpp.

Per questo metodo, saranno necessari MainPage::navigating, un campo booleano privato, inizializzato su false,For this method, we'll need MainPage::navigating, which is a private Boolean field, initialized to false. e anche un Frame in MainPage.xaml, denominato ScenarioFrame.And you'll need a Frame in MainPage.xaml, named ScenarioFrame. Tuttavia, oltre a questi dettagli, la conversione di questo metodo non rivela nuove tecniche.But, apart from those details, porting this method reveals no new techniques.

UpdateStatusUpdateStatus

Fino a questo punto abbiamo solo uno stub per MainPage.UpdateStatus.We have only a stub so far for MainPage.UpdateStatus. La conversione dell'implementazione ancora una volta non prevede nulla di nuovo,Porting its implementation, again, covers largely old ground. se non che, mentre in C# possiamo confrontare string con String.Empty, in C++/WinRT chiamiamo invece la funzione winrt::hstring::empty.One new point to note is that while in C# we can compare a string to String.Empty, In C++/WinRT we instead call the winrt::hstring::empty function. Un'altra differenza è che nullptr è l'equivalente C++ standard di null in C#.Another is that nullptr is the standard C++ equivalent of C#'s null.

Puoi eseguire il resto della conversione con le tecniche già analizzate.You can perform the rest of the port with techniques we've already covered. Ecco un elenco dei tipi di operazioni che è necessario eseguire prima che venga compilata la versione convertita di questo metodo.Here's a list of the kinds of things you'll need to do before the ported version of this method will compile.

  • In MainPage.xaml aggiungi un Border denominato StatusBorder.To MainPage.xaml, add a Border named StatusBorder.
  • In MainPage.xaml aggiungi un TextBlock denominato StatusBlock.To MainPage.xaml, add a TextBlock named StatusBlock.
  • In MainPage.xaml aggiungi uno StackPanel denominato StatusPanel.To MainPage.xaml, add a StackPanel named StatusPanel.
  • In pch.h aggiungi #include "winrt/Windows.UI.Xaml.Media.h".To pch.h, add #include "winrt/Windows.UI.Xaml.Media.h".
  • In pch.h aggiungi #include "winrt/Windows.UI.Xaml.Automation.Peers.h".To pch.h, add #include "winrt/Windows.UI.Xaml.Automation.Peers.h".
  • In MainPage.cpp aggiungi using namespace winrt::Windows::UI::Xaml::Media;.To MainPage.cpp add using namespace winrt::Windows::UI::Xaml::Media;.
  • In MainPage.cpp aggiungi using namespace winrt::Windows::UI::Xaml::Automation::Peers;.To MainPage.cpp add using namespace winrt::Windows::UI::Xaml::Automation::Peers;.

Copia il codice XAML e gli stili necessari per completare la conversione di MainPageCopy the XAML and styles necessary to finish up porting MainPage

Per XAML, l'ideale è poter usare lo stesso markup XAML in un progetto C# e in uno C++/WinRT.For XAML, the ideal case is that you can use the same XAML markup across a C# and a C++/WinRT project. L'esempio Clipboard è uno di questi casi.And the Clipboard sample is one of those cases.

Nel file Styles.xaml l'esempio Clipboard ha un ResourceDictionary XAML di stili, che vengono applicati ai pulsanti, ai menu e agli altri elementi dell'interfaccia utente dell'applicazione.In its Styles.xaml file, the Clipboard sample has a XAML ResourceDictionary of styles, which are applied to the buttons, menus, and other UI elements across the UI of the application. La pagina Styles.xaml viene unita ad App.xaml.The Styles.xaml page is merged into App.xaml. Si ottiene così il punto di partenza standard MainPage.xaml per l'interfaccia utente, di cui si è già parlato brevemente.And then there's the standard MainPage.xaml starting point for the UI, which we've already seen briefly. Ora possiamo riutilizzare questi tre file .xaml, senza modifiche, nella versione C++/WinRT del progetto.We can now re-use those three .xaml files, unchanged, in the C++/WinRT version of the project.

Come per i file di asset, puoi scegliere di fare riferimento agli stessi file XAML condivisi da più versioni dell'applicazione.As with asset files, you can choose to reference the same, shared XAML files from multiple versions of your application. In questa procedura dettagliata, solo per semplicità, copieremo i file nel progetto C++/WinRT e li aggiungeremo in questo modo.In this walkthrough, just for the sake of simplicity, we'll copy files into the C++/WinRT project and add them that way.

Passa alla cartella \Clipboard_sample\SharedContent\xaml, seleziona e copia App.xaml e MainPage.xaml e quindi incolla i due file nella cartella \Clipboard\Clipboard nel progetto C++/WinRT, scegliendo di sostituire i file quando ti viene chiesto.Navigate to the \Clipboard_sample\SharedContent\xaml folder, select and copy App.xaml and MainPage.xaml, and then paste those two files into the \Clipboard\Clipboard folder in your C++/WinRT project, choosing to replace files when prompted.

Aggiungi una nuova cartella al progetto C++/WinRT, immediatamente sotto il nodo del progetto, denominata Styles.Add a new folder to the C++/WinRT project, immediately under the project node, and named Styles. Passa alla cartella \Clipboard_sample\SharedContent\xaml, seleziona e copia Styles.xaml e incollalo nella cartella \Clipboard\Clipboard\Styles nel progetto C++/WinRT.Navigate to the \Clipboard_sample\SharedContent\xaml folder, select and copy Styles.xaml, and paste it into the \Clipboard\Clipboard\Styles folder in your C++/WinRT project. Fai clic con il pulsante destro del mouse sulla cartella Styles (in Esplora soluzioni nel progetto C++/WinRT) > Aggiungi > Elemento esistente e passa a \Clipboard\Clipboard\Styles.Right-click the Styles folder (in Solution Explorer in the C++/WinRT project) > Add > Existing item... and navigate to \Clipboard\Clipboard\Styles. In selezione file seleziona Styles e fai clic su Aggiungi.In the file picker, select Styles and click Add.

La conversione di MainPage a questo punto è terminata e, se hai seguito tutti i passaggi, il tuo progetto C++/WinRT ora verrà compilato ed eseguito.We've now finished porting MainPage, and if you've been following along with the steps then your C++/WinRT project will now build and run.

Consolidare i file .idlConsolidate your .idl files

Oltre al punto di partenza MainPage.xaml standard per l'interfaccia utente, l'esempio Clipboard presenta altre cinque pagine XAML specifiche dello scenario, insieme ai relativi file code-behind corrispondenti.In addition to the standard MainPage.xaml starting point for the UI, the Clipboard sample has five other scenario-specific XAML pages, together with their corresponding code-behind files. Verrà riutilizzato il markup XAML effettivo di tutte queste pagine, senza modifiche, nella versione C++/WinRT del progetto.We'll be re-using the actual XAML markup of all of these pages, unchanged, in the C++/WinRT version of the project. Nelle successive sezioni principali verrà inoltre esaminato come eseguire la conversione del code-behind.And we'll look at how to port the code-behind in the next few major sections. Ma prima è opportuno parlare di IDL.But before that, let's talk about IDL.

Può essere utile consolidare le classi di runtime in un singolo file IDL (vedi Factoring delle classi di runtime nei file Midl (.idl)).There's value in consolidating your runtime classes into a single IDL file (see Factoring runtime classes into Midl files (.idl)). Pertanto, di seguito il contenuto di CopyFiles.idl, CopyImage.idl, CopyText.idl, HistoryAndRoaming.idl, e OtherScenarios.idl verrà consolidato spostando tale codice IDL in un unico file denominato Project.idl (e quindi eliminando i file originari).So next we'll consolidate the contents of CopyFiles.idl, CopyImage.idl, CopyText.idl, HistoryAndRoaming.idl, and OtherScenarios.idl by moving that IDL into a single file named Project.idl (and then deleting the original files).

Contemporaneamente, sarà anche necessario rimuovere la proprietà fittizia generata automaticamente (Int32 MyProperty; e la relativa implementazione) da ognuno di questi cinque tipi di pagine XAML.While we're doing that, let's also remove the auto-generated dummy property (Int32 MyProperty;, and its implementation) from each of those five XAML page types.

Per prima cosa, aggiungi un nuovo elemento File Midl (.idl) al progetto C++/WinRT.First, add a new Midl File (.idl) item to the C++/WinRT project. Denomina tale elemento Project.idl.Name it Project.idl. Sostituisci l'intero contenuto di Project.idl con il codice seguente.Replace the entire contents of Project.idl with the following code.

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

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

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

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

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

Come puoi notare, si tratta semplicemente di una copia del contenuto dei singoli file .idl, tutti all'interno di un unico spazio dei nomi e con MyProperty rimossa da ogni classe di runtime.As you can see, this is just a copy of the contents of the individual .idl files, all inside one namespace, and with MyProperty removed from each runtime class.

In Esplora soluzioni di Visual Studio seleziona tutti i file IDL originari (CopyFiles.idl, CopyImage.idl, CopyText.idl, HistoryAndRoaming.idl e OtherScenarios.idl) e quindi fai clic su Modifica > Rimuovi per rimuoverli (scegli Elimina nella finestra di dialogo).In Solution Explorer in Visual Studio, multiple-select all of the original IDL files (CopyFiles.idl, CopyImage.idl, CopyText.idl, HistoryAndRoaming.idl, and OtherScenarios.idl) and Edit > Remove them (choose Delete in the dialog).

Infine — anche per completare la rimozione di MyProperty — nei file .h e .cpp di ciascuno dei cinque tipi di pagine XAML elimina le dichiarazioni e le definizioni delle funzioni di accesso int32_t MyProperty() e mutatore void MyProperty(int32_t).Finally—and to complete the removal of MyProperty—in the .h and .cpp files for each of the five XAML page types, delete the declarations and definitions of the int32_t MyProperty() accessor and void MyProperty(int32_t) mutator functions.

Per inciso, è sempre consigliabile che il nome dei file XAML corrisponda al nome della classe che tali file rappresentano.Incidentally, it's always a good idea to have the name of your XAML files match the name of the class that they represent. Se, ad esempio, un file di markup XAML contiene x:Class="MyNamespace.MyPage", il file deve essere denominato MyPage.xaml.For example, if you have x:Class="MyNamespace.MyPage" in a XAML markup file, then that file should be named MyPage.xaml. Benché non si tratti di un requisito tecnico, il fatto di non doversi destreggiare tra nomi diversi per lo stesso artefatto renderà il progetto più comprensibile e gestibile e più facile da usare.While this isn't a technical requirement, not having to juggle different names for the same artifact will make your project more understandable and maintainable, and easier to work with.

CopyFilesCopyFiles

Nel progetto C# il tipo di pagina XAML CopyFiles viene implementato nei file di codice sorgente CopyFiles.xaml e CopyFiles.xaml.cs.In the C# project, the CopyFiles XAML page type is implemented in the CopyFiles.xaml and CopyFiles.xaml.cs source code files. Ora è possibile esaminare i singoli membri di CopyFiles.Let's take a look at each of the members of CopyFiles in turn.

rootPagerootPage

Questo è un campo privato.This is a private field.

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

In C++/WinRT può essere definito e inizializzato come segue.In C++/WinRT, we can define and initialize it like this.

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

Anche in questo caso (come con MainPage::current), CopyFiles::rootPage viene dichiarato come se fosse di tipo SDKTemplate::MainPage, che è il tipo previsto, e non il tipo di implementazione.Again (just like with MainPage::current), CopyFiles::rootPage is declared as being of type SDKTemplate::MainPage, which is the projected type, and not the implementation type.

CopyFiles (il costruttore)CopyFiles (the constructor)

Nel progetto C++/WinRT il tipo CopyFiles dispone già di un costruttore contenente il codice desiderato (semplicemente chiama InitializeComponent).In the C++/WinRT project, the CopyFiles type already has a constructor containing the code we want (it just calls InitializeComponent).

CopyButton_ClickCopyButton_Click

Il metodo CopyButton_Click C# è un gestore eventi e dalla parola chiave async nella relativa firma è possibile comprendere che il metodo esegue operazioni asincrone.The C# CopyButton_Click method is an event handler, and from the async keyword in its signature we can tell that the method does asynchronous work. In C++/WinRT un metodo asincrono viene implementato come coroutine.In C++/WinRT, we implement an asynchronous method as a coroutine. Per un'introduzione alla concorrenza in C++/WinRT e per una descrizione di cosa sia una coroutine, vedi Concorrenza e operazioni asincrone con C++/WinRT.For an introduction to concurrency in C++/WinRT, together with a description of what a coroutine is, see Concurrency and asynchronous operations with C++/WinRT.

È comune voler pianificare altre operazioni da eseguire dopo il completamento di una coroutine e, per questi casi, la coroutine restituisce un tipo di oggetto asincrono che può essere atteso e che facoltativamente segnala lo stato di avanzamento.It's common to want to schedule further work after a coroutine completes, and for such cases the coroutine would return some asynchronous object type that can be awaited, and that optionally reports progress. Queste considerazioni, tuttavia, non si applicano in genere a un gestore eventi.But those considerations typically don't apply to an event handler. Pertanto, quando disponi di un gestore eventi che esegue operazioni asincrone, puoi implementarlo come una coroutine che restituisce winrt::fire_and_forget.So when you have an event handler that performs asynchronous operations, you can implement that as a coroutine that returns winrt::fire_and_forget. Per altre informazioni, vedi Attivare e poi dimenticare.For more info, see Fire and forget.

Anche se alla base di una coroutine di tipo fire-and-forget c'è il concetto che non è importante quando verrà completata, il lavoro prosegue comunque (o viene sospeso, in attesa della ripresa) in background.Although the idea of a fire-and-forget coroutine is that you don't care when it completes, work is still continuing (or is suspended, awaiting resumption) in the background. Puoi vedere dall'implementazione C# che CopyButton_Click dipende dal puntatore this (accede al membro dati di istanza rootPage).You can see from the C# implementation that CopyButton_Click depends on the this pointer (it accesses the instance data member rootPage). È pertanto necessario assicurarsi che il puntatore this (un puntatore a un oggetto CopyFiles) abbia una durata superiore a quella della coroutine CopyButton_Click.So we must be sure that the this pointer (a pointer to a CopyFiles object) outlives the CopyButton_Click coroutine. In una situazione analoga a questa applicazione di esempio, in cui l'utente si sposta tra le pagine dell'interfaccia utente, non è possibile controllare direttamente la durata di tali pagine.In a situation like this sample application, where the user navigates between UI pages, we can't directly control the lifetime of those pages. Se la pagina CopyFiles viene distrutta (spostandosi al di fuori di essa) mentre CopyButton_Click è ancora in fase di esecuzione in un thread in background, non sarà possibile accedere a rootPage in modo sicuro.Should the CopyFiles page be destroyed (by navigating away from it) while CopyButton_Click is still in flight on a background thread, it won't be safe to access rootPage. Per rendere corretta la coroutine, è necessario ottenere un riferimento sicuro al puntatore this e mantenerlo per la durata della coroutine.To make the coroutine correct, it needs to obtain a strong reference to the this pointer, and keep that reference for the duration of the coroutine. Per altre informazioni, vedi Riferimenti sicuri e deboli in C++/WinRT.For more info, see Strong and weak references in C++/WinRT.

Se osservi la versione C++/WinRT dell'esempio, in corrispondenza di CopyFiles::CopyButton_Click, noterai che ciò avviene con una semplice dichiarazione nello stack.If you look in the C++/WinRT version of the sample, at CopyFiles::CopyButton_Click, you'll see that it's done with a simple declaration on the stack.

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

Verranno ora esaminati gli altri aspetti del codice convertito che sono degni di nota.Let's look at the other aspects of the ported code that are noteworthy.

Nel codice viene creata un'istanza di un oggetto FileOpenPicker e due righe più avanti viene eseguito l'accesso alla proprietà FileTypeFilter di tale oggetto.In the code, we instantiate a FileOpenPicker object, and two lines later we access that object's FileTypeFilter property. Il tipo restituito della proprietà implementa un IVector di stringhe.The return type of that property implements an IVector of strings. In tale IVector viene chiamato il metodo IVector.ReplaceAll(T[]).And on that IVector, we call the IVector.ReplaceAll(T[]) method. L'aspetto interessante è il valore che viene passato al metodo, in cui è prevista una matrice.The interesting aspect is the value that we're passing to that method, where an array is expected. Di seguito è riportata la riga di codice.Here's the line of code.

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

Il valore che viene passato ({ L"*" }) è un elenco di inizializzatori C++ standard.The value that we're passing ({ L"*" }) is a standard C++ initializer list. In questo caso contiene un solo oggetto, ma un elenco di inizializzatori può contenere un numero qualsiasi di oggetti separati da virgole.It contains a single object, in this case, but an initializer list can contain any number of comma-separated objects. Le parti di C++/WinRT che ti consentono di passare facilmente un elenco di inizializzatori a un metodo come questo sono illustrate in Elenchi di inizializzatori standard.The pieces of C++/WinRT that allow you the convenience of passing an initializer list to a method such as this are explained in Standard initializer lists.

La parola chiave await C# viene convertita a co_await in C++/WinRT.We port the C# await keyword to co_await in C++/WinRT. Di seguito è riportato l'esempio tratto dal codice.Here's the example from the code.

auto storageItems{ co_await filePicker.PickMultipleFilesAsync() };

Considera quindi questa riga di codice C#.Next, consider this line of C# code.

dataPackage.SetStorageItems(storageItems);

C# è in grado di convertire in modo implicito l'elemento IReadOnlyList rappresentato da storageItems nell'elemento IEnumerable previsto da DataPackage.SetStorageItems.C# is able to implicitly convert the IReadOnlyList represented by storageItems into the IEnumerable expected by DataPackage.SetStorageItems. Tuttavia, in C++/WinRT è necessario eseguire in modo esplicito la conversione da IVectorView a IIterable .But in C++/WinRT we need to explicitly convert from IVectorView to IIterable. È quindi disponibile un altro esempio della funzione as in azione.And so we have another example of the as function in action.

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

Dove viene usata la parola chiave null in C# (ad esempio, Clipboard.SetContentWithOptions(dataPackage, null)), viene usata nullptr in C++/WinRT (ad esempio, Clipboard::SetContentWithOptions(dataPackage, nullptr)).Where we use the null keyword in C# (for example, Clipboard.SetContentWithOptions(dataPackage, null)), we use nullptr in C++/WinRT (for example, Clipboard::SetContentWithOptions(dataPackage, nullptr)).

PasteButton_ClickPasteButton_Click

Si tratta di un altro gestore eventi sotto forma di coroutine di tipo fire-and-forget.This is another event handler in the form of a fire-and-forget coroutine. Verranno ora esaminati gli aspetti del codice convertito che sono degni di nota.Let's look at the aspects of the ported code that are noteworthy.

Nella versione C# dell'esempio vengono intercettate le eccezioni con catch (Exception ex).In the C# version of the sample, we catch exceptions with catch (Exception ex). Nel codice C++/WinRT convertito vedrai l'espressione catch (winrt::hresult_error const& ex).In the ported C++/WinRT code, you'll see the expression catch (winrt::hresult_error const& ex). Per altre informazioni su winrt::hresult_error e su come gestirlo, vedi Gestione degli errori con C++/WinRT.For more info about winrt::hresult_error and how to work with it, see Error handling with C++/WinRT.

if (storageItems != null) è un esempio di test che indica se un oggetto C# è o non è null.An example of testing whether a C# object is null or not is if (storageItems != null). In C++/WinRT è possibile usare un operatore di conversione a bool, che esegue il test internamente a fronte di nullptr.In C++/WinRT, we can rely on a conversion operator to bool, which does the test against nullptr internally.

Di seguito è riportata una versione leggermente semplificata di un frammento di codice della versione C++/WinRT convertita dell'esempio.Here's a slightly simplified version of a fragment of code from the ported C++/WinRT version of the sample.

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

La costruzione di un elemento std::wstring_view da un elemento winrt::hstring come questo illustra un'alternativa alla chiamata della funzione hstring::c_str (per trasformare winrt::hstring in una stringa di tipo C).Constructing a std::wstring_view from a winrt::hstring like that illustrates an alternative to calling the hstring::c_str function (to turn the winrt::hstring into a C-style string). Questa alternativa funziona grazie all'operatore di conversione a std::wstring_view di hstring.This alternative works thanks to hstring's conversion operator to std::wstring_view.

Considera questo frammento di codice C#.Consider this fragment of C#.

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

Per convertire la parola chiave as C# a C++/WinRT, finora è stata usata un paio di volte la funzione as.To port the C# as keyword to C++/WinRT, so far we've seen the as function used a couple of times. Se la conversione dei tipi ha esito negativo, questa funzione genera un'eccezione.That function throws an exception if the type conversion fails. Se vuoi però che la conversione restituisca nullptr quando l'esito è negativo (in modo da poter gestire tale condizione nel codice), possiamo usare la funzione try_as.But if we want the conversion to return nullptr if it fails (so that we can handle that condition in the code), then we instead use the try_as function.

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

Copiare il codice XAML necessario per completare la conversione di CopyFilesCopy the XAML necessary to finish up porting CopyFiles

Ora puoi selezionare l'intero contenuto del file CopyFiles.xaml dal progetto C# e incollarlo nel file CopyFiles.xaml nel progetto C++/WinRT (sostituendo il contenuto esistente di tale file nel progetto C++/WinRT).You can now select the entire contents of the CopyFiles.xaml file from the C# project, and paste that into the CopyFiles.xaml file in the C++/WinRT project (replacing the existing contents of that file in the C++/WinRT project).

Modifica infine CopyFiles.h e .cpp ed elimina la funzione ClickHandler fittizia, in quanto il markup XAML corrispondente è stato appena sovrascritto.Finally, edit CopyFiles.h and .cpp and delete the dummy ClickHandler function, since we just overwrote the corresponding XAML markup.

La conversione di CopyFiles a questo punto è terminata e, se hai seguito tutti i passaggi, il tuo progetto C++/WinRT ora verrà compilato ed eseguito e lo scenario CopyFiles sarà funzionante.We've now finished porting CopyFiles, and if you've been following along with the steps then your C++/WinRT project will now build and run, and the CopyFiles scenario will be functional.

CopyImageCopyImage

Per convertire il tipo di pagina XAML CopyImage, segui lo stesso processo illustrato per CopyFiles.To port the CopyImage XAML page type, you follow the same process as for CopyFiles. Durante la conversione di CopyImage, vedrai l'uso dell'istruzione using C#, che assicura una corretta eliminazione degli oggetti che implementano l'interfaccia IDisposable.While porting CopyImage, you'll encounter the use of the C# using statement, which ensures that objects that implement the IDisposable interface are disposed correctly.

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

L'interfaccia equivalente in C++/WinRT è IClosable, con il suo unico metodo Close.The equivalent interface in C++/WinRT is IClosable, with its single Close method. Di seguito è riportato l'equivalente C++/WinRT del codice C# precedente.Here's the C++/WinRT equivalent of the C# code above.

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

Gli oggetti C++/WinRT implementano IClosable principalmente a vantaggio dei linguaggi privi di finalizzazione deterministica.C++/WinRT objects implement IClosable primarily for the benefit of languages that lack deterministic finalization. C++/WinRT prevede tale finalizzazione, pertanto spesso non è necessario chiamare IClosable::Close quando viene scritto codice C++/WinRT.C++/WinRT has deterministic finalization, and so we often don't need to call IClosable::Close when we're writing C++/WinRT. Tuttavia, in alcuni casi è opportuno chiamarlo e questa è una di tali circostanze.But there are times when it's good to call it, and this is one of those times. Qui l'identificatore imageStream è un wrapper con conteggio dei riferimenti intorno a un oggetto Windows Runtime sottostante (in questo caso, un oggetto che implementa IRandomAccessStreamWithContentType).Here, the imageStream identifier is a reference-counted wrapper around an underlying Windows Runtime object (in this case, an object that implements IRandomAccessStreamWithContentType). Benché sia possibile stabilire che il finalizzatore di imageStream (il relativo distruttore) venga eseguito alla fine dell'ambito che lo contiene (le parentesi graffe), non è certo che il finalizzatore chiami Close.Although we can determine that the finalizer of imageStream (its destructor) will run at the end of the enclosing scope (the curly brackets), we can't be certain that that finalizer will call Close. Ecco perché imageStream è stato passato ad altre API, che potrebbero contribuire comunque al conteggio dei riferimenti dell'oggetto Windows Runtime sottostante.That's because we passed imageStream to other APIs, and they might still be contributing to the reference count of the underlying Windows Runtime object. Questo è quindi un caso in cui è consigliabile chiamare Close in modo esplicito.So this is a case where it's a good idea to call Close explicitly. Per altre informazioni, vedi Devo chiamare IClosable::Close sulle classi di runtime utilizzate?.For more info, see Do I need to call IClosable::Close on runtime classes that I consume?.

Considera quindi l'espressione C# (uint)(imageDecoder.OrientedPixelWidth * 0.5), che troverai nel gestore eventi OnDeferredImageRequestedHandler.Next, consider the C# expression (uint)(imageDecoder.OrientedPixelWidth * 0.5), which you'll find in the OnDeferredImageRequestedHandler event handler. Tale espressione moltiplica un uint per un double, dando come risultato un double.That expression multiplies a uint by a double, resulting in a double. Ne esegue quindi il cast in un uint.It then casts that to a uint. In C++/WinRT si potrebbe usare un cast di tipo C simile ((uint32_t)(imageDecoder.OrientedPixelWidth() * 0.5)), ma è preferibile specificare in modo chiaro quale tipo di cast si intenda esattamente e in questo caso viene usato static_cast<uint32_t>(imageDecoder.OrientedPixelWidth() * 0.5).In C++/WinRT, we could use a similar-looking C-style cast ((uint32_t)(imageDecoder.OrientedPixelWidth() * 0.5)), but it's preferable to make it clear exactly what kind of cast we intend, and in this case we would do that with static_cast<uint32_t>(imageDecoder.OrientedPixelWidth() * 0.5).

La versione C# di CopyImage.OnDeferredImageRequestedHandler dispone di una clausola finally, ma non di una clausola catch.The C# version of CopyImage.OnDeferredImageRequestedHandler has a finally clause, but not a catch clause. Nella versione C++/WinRT è stato possibile procedere ulteriormente ed è stata implementata una clausola catch, in modo da poter segnalare se l'esito del rendering ritardato era positivo o negativo.We went just a little bit further in the C++/WinRT version, and implemented a catch clause so that we can report whether or not the delayed rendering was successful.

La conversione della parte restante di questa pagina XAML non fornisce nuovi spunti di cui discutere.Porting the remainder of this XAML page doesn't yield anything new to discuss. Ricorda di eliminare la funzione fittizia ClickHandler.Remember to delete the dummy ClickHandler function. Analogamente a quanto avviene con CopyFiles, l'ultimo passaggio della conversione consiste nel selezionare l'intero contenuto di CopyImage.xaml e incollarlo nello stesso file nel progetto C++/WinRT.And, just like with CopyFiles, the last step in the port is to select the entire contents of CopyImage.xaml, and paste it into the same file in the C++/WinRT project.

CopyTextCopyText

Puoi convertire CopyText.xaml e CopyText.xaml.cs usando le tecniche già descritte.You can port CopyText.xaml and CopyText.xaml.cs using techniques we've already covered.

HistoryAndRoamingHistoryAndRoaming

Durante la conversione del tipo di pagina XAML HistoryAndRoaming emergono alcuni spunti interessanti.There are some points of interest that arise while porting the HistoryAndRoaming XAML page type.

Prima di tutto, esamina il codice sorgente C# e segui il flusso di controllo da OnNavigatedTo al gestore eventi OnHistoryEnabledChanged e infine alla funzione asincrona CheckHistoryAndRoaming (che non è attesa, quindi essenzialmente è di tipo fire-and-forget).First, take a look at the C# source code, and follow the flow of control from OnNavigatedTo through the OnHistoryEnabledChanged event handler, and finally to the asynchronous function CheckHistoryAndRoaming (which is not awaited, so it's essentially fire and forget). Poiché CheckHistoryAndRoaming è asincrona, sarà necessario prestare attenzione in C++/WinRT riguardo alla durata del puntatore this.Because CheckHistoryAndRoaming is asynchronous, we'll need to be careful in C++/WinRT about the lifetime of the this pointer. Puoi vedere il risultato guardando l'implementazione nel file di codice sorgente HistoryAndRoaming.cpp.You can see the outcome if you look at the implementation in the HistoryAndRoaming.cpp source code file. In primo luogo, quando vengono associati delegati agli eventi Clipboard::HistoryEnabledChanged e Clipboard::RoamingEnabledChanged, viene usato solo un riferimento debole all'oggetto della pagina HistoryAndRoaming.First, when we attach delegates to the Clipboard::HistoryEnabledChanged and Clipboard::RoamingEnabledChanged events, we take only a weak reference to the HistoryAndRoaming page object. A tale scopo, viene creato il delegato con una dipendenza dal valore restituito da winrt::get_weak invece che con una dipendenza dal puntatore this.We do that by creating the delegate with a dependency on the value returned from winrt::get_weak, instead of a dependency on the this pointer. Ciò significa che il delegato stesso, che alla fine esegue chiamate nel codice asincrono, non mantiene attiva la pagina HistoryAndRoaming quando ci si sposta al di fuori di essa.Which means that the delegate itself, which eventually calls into asychronous code, doesn't keep the HistoryAndRoaming page alive, should we navigate away from it.

In secondo luogo, quando alla fine si giunge alla coroutine CheckHistoryAndRoaming di tipo fire-and-forget, la prima operazione da eseguire è usare un riferimento sicuro a this per garantire che la pagina HistoryAndRoaming resti attiva almeno fino al completamento della coroutine.And second, when we do finally reach our fire-and-forget CheckHistoryAndRoaming coroutine, the first thing we do is to take a strong reference to this to guarantee that the HistoryAndRoaming page lives at least until the coroutine finally completes. Per altre informazioni su entrambi gli aspetti appena illustrati, vedi Riferimenti sicuri e deboli in C++/WinRT.For more info about both of the aspects just described, see Strong and weak references in C++/WinRT.

Durante la conversione di CheckHistoryAndRoaming emerge un altro spunto interessante.We find another point of interest while porting CheckHistoryAndRoaming. Contiene il codice per aggiornare l'interfaccia utente, quindi si deve avere la certezza di eseguire questa operazione sul thread principale dell'interfaccia utente.It contains code to update the UI; so we need to be certain that we're doing that on the main UI thread. Il thread che inizialmente chiama un gestore eventi è il thread di UI principale.The thread that initially calls into an event handler is the main UI thread. In genere, tuttavia, un metodo asincrono può essere eseguito e/o ripreso su qualsiasi thread arbitrario.But typically, an asynchronous method can execute and/or resume on any arbitrary thread. In C# la soluzione consiste nel chiamare CoreDispatcher.RunAsync e nell'aggiornare l'interfaccia utente dall'interno della funzione lambda.In C#, the solution is to call CoreDispatcher.RunAsync, and update the UI from within the lambda function. In C++/WinRT è possibile usare la funzione winrt::resume_foreground insieme al Dispatcher del puntatore this per sospendere la coroutine e riprendere immediatamente sul thread principale dell'interfaccia utente.In C++/WinRT, we can use the winrt::resume_foreground function together with the this pointer's Dispatcher to suspend the coroutine and immediately resume on the main UI thread.

L'espressione pertinente è co_await winrt::resume_foreground(Dispatcher());.The relevant expression is co_await winrt::resume_foreground(Dispatcher());. In alternativa, sebbene con meno chiarezza, si potrebbe esprimere questa funzione semplicemente come co_await Dispatcher();.Alternatively, although with less clarity, we could express that simply as co_await Dispatcher();. La versione più breve viene ottenuta grazie a un operatore di conversione fornito da C++/WinRT.The shorter version is achieved courtesy of a conversion operator supplied by C++/WinRT.

La conversione della parte restante di questa pagina XAML non fornisce nuovi spunti di cui discutere.Porting the remainder of this XAML page doesn't yield anything new to discuss. Ricorda di eliminare la funzione fittizia ClickHandler e sovrascrivere il markup XAML.Remember to delete the dummy ClickHandler function, and to copy over the XAML markup.

OtherScenariosOtherScenarios

Puoi convertire OtherScenarios.xaml e OtherScenarios.xaml.cs usando le tecniche già descritte.You can port OtherScenarios.xaml and OtherScenarios.xaml.cs using techniques we've already covered.

ConclusioneConclusion

Avendo seguito le informazioni e le tecniche di conversione illustrate in questa procedura dettagliata, ora puoi provare a eseguire da solo la conversione delle tue applicazioni C# a C++/WinRT.Hopefully this walkthrough has armed you with sufficient porting info and techniques that you can now go ahead and port your own C# applications to C++/WinRT. Tramite un aggiornamento, puoi continuare a fare riferimento alla versione precedente (C#) e a quella successiva (C++/WinRT) del codice sorgente nell'esempio Clipboard e confrontarle affiancate per vedere la corrispondenza.By way of a refresher, you can continue to refer back to the before (C#) and after (C++/WinRT) versions of the source code in the Cliboard sample, and compare them side by side to see the correspondence.