Mover do C# para C++/WinRT
Dica
Se você já leu este tópico antes e está retornando a ele com uma tarefa específica em mente, vá para a seção Localizar um conteúdo com base na tarefa que você está executando deste tópico.
Este tópico descreve os detalhes técnicos envolvidos na portabilidade do código-fonte em um projeto C# para o equivalente no C++/WinRT.
Para obter um estudo de caso de portabilidade de um dos exemplos de aplicativo UWP (Plataforma Universal do Windows), confira o tópico complementar Como portar a amostra de área de transferência para o C++/WinRT do C#. Você pode adquirir prática e experiência em portabilidade seguindo o passo a passo e fazendo a portabilidade da amostra por conta própria durante as etapas.
Como se preparar e o que esperar
O estudo de caso Como portar a amostra de área de transferência para o C++/WinRT do C# ilustra exemplos dos tipos de decisões de design de software que você tomará ao portar um projeto para o C++/WinRT. Portanto, é uma boa ideia se preparar para a portabilidade adquirindo uma compreensão substancial de como o código existente funciona. Dessa forma, você obterá uma boa visão geral da funcionalidade do aplicativo e da estrutura do código, e as decisões que você tomará sempre o conduzirão para frente e na direção certa.
Em relação a quais tipos de portabilidade devem ser esperados, é possível agrupá-los em quatro categorias.
- Portar a projeção de linguagem. O WinRT (Windows Runtime) é projetado em várias linguagens de programação. Cada uma dessas projeções de linguagem foi criada para dar um aspecto idiomático à linguagem de programação em questão. Quanto ao C#, alguns tipos do Windows Runtime foram projetados como tipos .NET. Por exemplo, você converterá System.Collections.Generic.IReadOnlyList<T> novamente em Windows.Foundation.Collections.IVectorView<T>. Também em C#, algumas operações do Windows Runtime foram projetadas como recursos da linguagem C# convenientes. Um exemplo é que, em C#, você usa a sintaxe do operador
+=
para registrar um delegado de processamento de eventos. Portanto, você converterá recursos de linguagem como esse novamente na operação fundamental que está sendo executada (registro de evento, neste exemplo). - Portar a sintaxe da linguagem. Muitas dessas alterações são transformações mecânicas simples, com a substituição de um símbolo por outro. Por exemplo, alteração do ponto (
.
) para dois-pontos (::
). - Portar o procedimento da linguagem. Algumas dessas podem ser alterações simples e repetitivas (como
myObject.MyProperty
paramyObject.MyProperty()
). Outras precisam de alterações mais profundas (por exemplo, portar um procedimento que envolve o uso de System.Text.StringBuilder para um que envolve o uso de std::wostringstream). - Tarefas relacionadas à portabilidade que são específicas para o C++/WinRT. Alguns detalhes do Windows Runtime são resolvidos implicitamente pelo C#, nos bastidores. Esses detalhes são realizados explicitamente no C++/WinRT. Um exemplo disso é que você usa um arquivo
.idl
para definir as classes de runtime.
Após o índice baseado em tarefa a seguir, o restante das seções deste tópico é estruturado de acordo com a taxonomia acima.
Localizar um conteúdo com base na tarefa que você está executando
Tarefa | Conteúdo |
---|---|
Criar um WRC (componente do Windows Runtime) | Algumas funcionalidades podem ser obtidas (ou algumas APIs chamadas) somente com o C++. Você pode incluir essa funcionalidade em um WRC do C++/WinRT e consumir o WRC em um aplicativo C#, por exemplo. Confira Componentes do Windows Runtime com o C++/WinRT e Se você estiver criando uma classe de runtime em um componente do Windows Runtime. |
Portar um método assíncrono | É uma boa ideia que a primeira linha de um método assíncrono em uma classe de runtime C++/WinRT seja auto lifetime = get_strong(); (confira Como acessar com segurança o ponteiro this em uma corrotina de membro de classe).Portabilidade de Task : confira Ação assíncrona.Portabilidade de Task<T> : confira Operação assíncrona.Portabilidade de async void : confira Método fire-and-forget. |
Portar uma classe | Primeiro, determine se a classe precisa ser uma classe de runtime ou se pode ser uma classe comum. Para ajudar você a decidir isso, confira o início de Criar APIs com o C++/WinRT. Em seguida, confira as próximas três linhas abaixo. |
Portar uma classe de runtime | Uma classe que compartilha a funcionalidade fora do aplicativo C++ ou uma classe usada na vinculação de dados XAML. Confira Se você estiver criando uma classe de runtime em um componente do Windows Runtime ou Se você estiver criando uma classe de runtime para ser referenciada na interface do usuário XAML. Esses links descrevem isso mais detalhadamente, mas uma classe de runtime precisa ser declarada em IDL. Se o projeto já contiver um arquivo IDL (por exemplo, Project.idl ), recomendamos que você declare qualquer nova classe de runtime nesse arquivo. Em IDL, declare os métodos e os membros de dados que serão usados fora do seu aplicativo ou que serão usados em XAML. Depois de atualizar o arquivo IDL, recompile e examine os arquivos stub gerados (.h e .cpp ) na pasta Generated Files do projeto (no Gerenciador de Soluções, com o nó do projeto selecionado, verifique se a opção Mostrar Todos os Arquivos está ativada). Compare os arquivos stub com os arquivos já existentes no projeto e adicione arquivos ou adicione/atualize assinaturas de função, conforme necessário. A sintaxe do arquivo stub está sempre correta. Portanto, recomendamos que você a use para minimizar os erros de build. Depois que os stubs do projeto corresponderem aos dos arquivos stub, você poderá implementá-los portando o código C#. |
Portar uma classe comum | Confira Se você não estiver criando uma classe de runtime. |
Criar uma IDL | Introdução à linguagem IDL 3.0 da Microsoft Se você estiver criando uma classe de runtime para ser referenciada em sua interface de usuário XAML Como consumir objetos por meio da marcação XAML Definir as classes de runtime na IDL |
Portar uma coleção | Coleções com C++/WinRT Como disponibilizar uma fonte de dados para marcação XAML Contêiner associativo Acesso de membro de vetor |
Portar um evento | Delegado do manipulador de eventos como membro de classe Revogar delegado do manipulador de eventos |
Portar um método | Do C#: private async void SampleButton_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e) { ... } Para o arquivo .h C++/WinRT: fire_and_forget SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&); Para o arquivo .cpp C++/WinRT: fire_and_forget OcrFileImage::SampleButton_Tapped(IInspectable const&, RoutedEventArgs const&) {...} |
Portar cadeias de caracteres | Processamento da cadeia de caracteres em C++/WinRT ToString Construção da cadeia de caracteres Conversão boxing e unboxing de uma cadeia de caracteres |
Conversão de tipo | C#: o.ToString() C++/WinRT: to_hstring(static_cast<int>(o)) Confira também ToString. C#: (Value)o C++/WinRT: unbox_value<Value>(o) é gerado em caso de falha na conversão unboxing. Confira também Conversão boxing e unboxing. C#: o as Value? ?? fallback C++/WinRT: unbox_value_or<Value>(o, fallback) Retorna um fallback em caso de falha na conversão unboxing. Confira também Conversão boxing e unboxing. C#: (Class)o C++/WinRT: o.as<Class>() É gerado em caso de falha na conversão. C#: o as Class C++/WinRT: o.try_as<Class>() Retorna nulo em caso de falha na conversão. |
Alterações que envolvem a projeção de linguagem
Categoria | C# | C++/WinRT | Veja também |
---|---|---|---|
Objeto não tipado | object ou System.Object |
Windows::Foundation::IInspectable | Como portar o método EnableClipboardContentChangedNotifications |
Namespaces de projeção | using System; |
using namespace Windows::Foundation; |
|
using System.Collections.Generic; |
using namespace Windows::Foundation::Collections; |
||
Tamanho de uma coleção | collection.Count |
collection.Size() |
Como portar o método BuildClipboardFormatsOutputString |
Tipo de coleção usual | IList<T> e Adicionar para adicionar um elemento. | IVector<T> e Acrescentar para adicionar um elemento. Se você usar um std::vector em qualquer lugar, em seguida, use push_back para adicionar um elemento. | |
Tipo de coleção somente leitura | IReadOnlyList<T> | IVectorView<T> | Como portar o método BuildClipboardFormatsOutputString |
Delegado do manipulador de eventos como membro de classe | myObject.EventName += Handler; |
token = myObject.EventName({ get_weak(), &Class::Handler }); |
Como portar o método EnableClipboardContentChangedNotifications |
Revogar delegado do manipulador de eventos | myObject.EventName -= Handler; |
myObject.EventName(token); |
Como portar o método EnableClipboardContentChangedNotifications |
Contêiner associativo | IDictionary<K, V> | IMap<K, V> | |
Acesso de membro de vetor | x = v[i]; v[i] = x; |
x = v.GetAt(i); v.SetAt(i, x); |
Registrar/revogar um manipulador de eventos
No C++/WinRT, há várias opções sintáticas para registrar/revogar um delegado de manipulador de eventos, conforme descrito em Processar eventos usando delegados no C++/WinRT. Confira também Como portar o método EnableClipboardContentChangedNotifications.
Às vezes, por exemplo, quando um destinatário de evento (um objeto que processa um evento) está prestes a ser destruído, o ideal é revogar um manipulador de eventos para que a origem do evento (o objeto que gera o evento) não chame um objeto destruído. Veja Revogar um delegado registrado. Em casos como esse, crie uma variável de membro event_token para os manipuladores de eventos. Para obter um exemplo, confira Como portar o método EnableClipboardContentChangedNotifications.
Você também pode registrar um manipulador de eventos na marcação XAML.
<Button x:Name="OpenButton" Click="OpenButton_Click" />
No C#, o método OpenButton_Click pode ser privado e o XAML ainda poderá conectá-lo ao evento ButtonBase.Click gerado por OpenButton.
No C++/WinRT, o método OpenButton_Click precisará ser público no tipo de implementação se você desejar registrá-lo na marcação XAML. Se você registrar um manipulador de eventos somente no código imperativo, o manipulador de eventos não precisará ser público.
namespace winrt::MyProject::implementation
{
struct MyPage : MyPageT<MyPage>
{
void OpenButton_Click(
winrt::Windows:Foundation::IInspectable const& sender,
winrt::Windows::UI::Xaml::RoutedEventArgs const& args);
}
};
Como alternativa, você pode tornar o registro da página XAML um amigo do tipo de implementação e o OpenButton_Click privado.
namespace winrt::MyProject::implementation
{
struct MyPage : MyPageT<MyPage>
{
private:
friend MyPageT;
void OpenButton_Click(
winrt::Windows:Foundation::IInspectable const& sender,
winrt::Windows::UI::Xaml::RoutedEventArgs const& args);
}
};
Um último cenário é quando o projeto C# que você está portando se associa ao manipulador de eventos da marcação (para obter mais informações sobre esse cenário, confira Funções em x:Bind).
<Button x:Name="OpenButton" Click="{x:Bind OpenButton_Click}" />
Você pode alterar essa marcação para o Click="OpenButton_Click"
mais simples. Ou, caso prefira, você pode manter essa marcação como está. Tudo o que você precisa fazer para dar suporte a ela é declarar o manipulador de eventos em IDL.
void OpenButton_Click(Object sender, Windows.UI.Xaml.RoutedEventArgs e);
Observação
Declare a função como void
mesmo se você implementá-la como Disparar e esquecer.
Alterações que envolvem a sintaxe de linguagem
Categoria | C# | C++/WinRT | Veja também |
---|---|---|---|
Modificadores de acesso | public \<member\> |
public: \<member\> |
Como portar o método Button_Click |
Acessar um membro de dados | this.variable |
this->variable |
|
Ação assíncrona | async Task ... |
IAsyncAction ... |
Interface IAsyncAction, Simultaneidade e operações assíncronas com C++/WinRT |
Operação assíncrona | async Task<T> ... |
IAsyncOperation<T> ... |
Interface IAsyncOperation, Simultaneidade e operações assíncronas com C++/WinRT |
Método do tipo disparar e esquecer (implica uma operação assíncrona) | async void ... |
winrt::fire_and_forget ... |
Portabilidade do método CopyButton_Click, Disparar e esquecer |
Acessar uma constante enumerada | E.Value |
E::Value |
Como portar o método DisplayChangedFormats |
Espera cooperativa | await ... |
co_await ... |
Como portar o método CopyButton_Click |
Coleção de tipos projetados como um campo privado | private List<MyRuntimeClass> myRuntimeClasses = new List<MyRuntimeClass>(); |
std::vector <MyNamespace::MyRuntimeClass> m_myRuntimeClasses; |
|
Construção do GUID | private static readonly Guid myGuid = new Guid("C380465D-2271-428C-9B83-ECEA3B4A85C1"); |
winrt::guid myGuid{ 0xC380465D, 0x2271, 0x428C, { 0x9B, 0x83, 0xEC, 0xEA, 0x3B, 0x4A, 0x85, 0xC1} }; |
|
Separador de namespace | A.B.T |
A::B::T |
|
Null | null |
nullptr |
Como portar o método UpdateStatus |
Obter um objeto de tipo | typeof(MyType) |
winrt::xaml_typename<MyType>() |
Como portar a propriedade Scenarios |
Declaração de parâmetro para um método | MyType |
MyType const& |
Passagem de parâmetro |
Declaração de parâmetro para um método assíncrono | MyType |
MyType |
Passagem de parâmetro |
Chamar um método estático | T.Method() |
T::Method() |
|
Cadeias de caracteres | string ou System.String |
winrt::hstring | Processamento da cadeia de caracteres em C++/WinRT |
Cadeia de caracteres literal | "a string literal" |
L"a string literal" |
Como portar o construtor, Current e FEATURE_NAME |
Tipo inferido (ou deduzido) | var |
auto |
Como portar o método BuildClipboardFormatsOutputString |
Diretiva using | using A.B.C; |
using namespace A::B::C; |
Como portar o construtor, Current e FEATURE_NAME |
Literal de cadeia de caracteres textual/bruta | @"verbatim string literal" |
LR"(raw string literal)" |
Como portar o método DisplayToast |
Observação
Se um arquivo de cabeçalho não contiver uma diretiva using namespace
para determinado namespace, você precisará qualificar totalmente todos os nomes de tipos desse namespace ou, pelo menos, qualificá-los suficientemente para que o compilador os encontre. Para obter um exemplo, confira Como portar o método DisplayToast.
Como portar classes e membros
Você precisará decidir, para cada tipo C#, se desejará portá-lo para um tipo do Windows Runtime ou para uma classe/um struct/uma numeração C++ normal. Para obter mais informações e exemplos detalhados que ilustram como tomar essas decisões, confira Como portar a amostra de área de transferência para o C++/WinRT do C#.
Uma propriedade C# normalmente se torna uma função de acessador, uma função de modificador e um membro de dados de backup. Para obter mais informações e um exemplo, confira Como portar a propriedade IsClipboardContentChangedEnabled.
Para campos não estáticos, torne-os membros de dados do tipo de implementação.
Um campo estático C# torna-se um acessador estático e/ou uma função de modificador C++/WinRT. Para obter mais informações e um exemplo, confira Como portar o construtor, Current e FEATURE_NAME.
Para as funções de membro, novamente, você precisará decidir para cada uma delas se pertencem ou não à IDL ou se elas são uma função de membro pública ou privada do tipo de implementação. Para obter mais informações e exemplos de como tomar essa decisão, confira IDL para o tipo MainPage.
Como portar a marcação XAML e os arquivos de ativos
No caso de Como portar a amostra de área de transferência para o C++/WinRT do C#, pudemos usar a mesma marcação XAML (incluindo os recursos) e os arquivos de ativos em todo o projeto C# e C++/WinRT. Em alguns casos, as edições na marcação serão necessárias para conseguir isso. Confira Copiar o XAML e os estilos necessários para concluir a portabilidade de MainPage.
Alterações que envolvem procedimentos dentro da linguagem
Categoria | C# | C++/WinRT | Veja também |
---|---|---|---|
Gerenciamento do tempo de vida em um método assíncrono | N/D | auto lifetime{ get_strong() }; ouauto lifetime = get_strong(); |
Como portar o método CopyButton_Click |
Descarte | using (var t = v) |
auto t{ v }; t.Close(); // or let wrapper destructor do the work |
Como portar o método CopyImage |
Objeto de constructo | new MyType(args) |
MyType{ args } ouMyType(args) |
Como portar a propriedade Scenarios |
Criar uma referência não inicializada | MyType myObject; |
MyType myObject{ nullptr }; ouMyType myObject = nullptr; |
Como portar o construtor, Current e FEATURE_NAME |
Objeto de constructo em variável com argumentos | var myObject = new MyType(args); |
auto myObject{ MyType{ args } }; ou auto myObject{ MyType(args) }; ou auto myObject = MyType{ args }; ou auto myObject = MyType(args); ou MyType myObject{ args }; ou MyType myObject(args); |
Como portar o método Footer_Click |
Objeto de constructo em variável sem argumentos | var myObject = new T(); |
MyType myObject; |
Como portar o método BuildClipboardFormatsOutputString |
Inicialização abreviada de objeto | var p = new FileOpenPicker{ ViewMode = PickerViewMode.List }; |
FileOpenPicker p; p.ViewMode(PickerViewMode::List); |
|
Operação de vetor em massa | var p = new FileOpenPicker{ FileTypeFilter = { ".png", ".jpg", ".gif" } }; |
FileOpenPicker p; p.FileTypeFilter().ReplaceAll({ L".png", L".jpg", L".gif" }); |
Como portar o método CopyButton_Click |
Iterar na coleção | foreach (var v in c) |
for (auto&& v : c) |
Como portar o método BuildClipboardFormatsOutputString |
Capturar uma exceção | catch (Exception ex) |
catch (winrt::hresult_error const& ex) |
Como portar o método PasteButton_Click |
Detalhes da exceção | ex.Message |
ex.message() |
Como portar o método PasteButton_Click |
Obtém um valor da propriedade | myObject.MyProperty |
myObject.MyProperty() |
Como portar o método NotifyUser |
Definir um valor da propriedade | myObject.MyProperty = value; |
myObject.MyProperty(value); |
|
Incrementar um valor da propriedade | myObject.MyProperty += v; |
myObject.MyProperty(thing.Property() + v); Para cadeias de caracteres, alterne para um construtor |
|
ToString() | myObject.ToString() |
winrt::to_hstring(myObject) |
ToString() |
Cadeia de caracteres de linguagem para a cadeia de caracteres do Windows Runtime | N/D | winrt::hstring{ s } |
|
Construção da cadeia de caracteres | StringBuilder builder; builder.Append(...); |
std::wostringstream builder; builder << ...; |
Construção da cadeia de caracteres |
Interpolação de cadeia de caracteres | $"{i++}) {s.Title}" |
winrt::to_hstring e/ou winrt::hstring::operator+ | Como portar o método OnNavigatedTo |
Cadeia de caracteres vazia para comparação | System.String.Empty | winrt::hstring::empty | Como portar o método UpdateStatus |
Criar uma cadeia de caracteres vazia | var myEmptyString = String.Empty; |
winrt::hstring myEmptyString{ L"" }; |
|
Operações de dicionário | map[k] = v; // replaces any existing v = map[k]; // throws if not present map.ContainsKey(k) |
map.Insert(k, v); // replaces any existing v = map.Lookup(k); // throws if not present map.HasKey(k) |
|
Conversão de tipo (geração em caso de falha) | (MyType)v |
v.as<MyType>() |
Como portar o método Footer_Click |
Conversão de tipo (nula em caso de falha) | v as MyType |
v.try_as<MyType>() |
Como portar o método PasteButton_Click |
Elementos XAML com x:Name são propriedades | MyNamedElement |
MyNamedElement() |
Como portar o construtor, Current e FEATURE_NAME |
Alternar para o thread da IU | CoreDispatcher.RunAsync | CoreDispatcher.RunAsync ou winrt::resume_foreground | Como portar o método NotifyUser e Como portar o método HistoryAndRoaming |
Construção de elemento da interface do usuário no código imperativo em uma página XAML | Veja a construção de elemento da interface do usuário | Veja a construção de elemento da interface do usuário |
As seções a seguir fornecem mais detalhes em relação a alguns dos itens da tabela.
construção de instrução da interface do usuário
Estes exemplos de código mostram a construção de um elemento da interface do usuário no código imperativo de uma página XAML.
var myTextBlock = new TextBlock()
{
Text = "Text",
Style = (Windows.UI.Xaml.Style)this.Resources["MyTextBlockStyle"]
};
TextBlock myTextBlock;
myTextBlock.Text(L"Text");
myTextBlock.Style(
winrt::unbox_value<Windows::UI::Xaml::Style>(
Resources().Lookup(
winrt::box_value(L"MyTextBlockStyle")
)
)
);
ToString()
Os tipos C# fornecem o método Object.ToString.
int i = 2;
var s = i.ToString(); // s is a System.String with value "2".
O C++/ WinRT não fornece diretamente esse recurso, mas você poderá usar alternativas.
int i{ 2 };
auto s{ std::to_wstring(i) }; // s is a std::wstring with value L"2".
O C++/WinRT também dá suporte a winrt::to_hstring em um número limitado de tipos. Você precisará adicionar sobrecargas para os tipos adicionais que deseja converter em cadeia de caracteres.
Language | Converter int em cadeia de caracteres | Converter uma enumeração em cadeia de caracteres |
---|---|---|
C# | string result = "hello, " + intValue.ToString(); string result = $"hello, {intValue}"; |
string result = "status: " + status.ToString(); string result = $"status: {status}"; |
C++/WinRT | hstring result = L"hello, " + to_hstring(intValue); |
// must define overload (see below) hstring result = L"status: " + to_hstring(status); |
No caso da conversão de uma enumeração em cadeia de caracteres, você precisará fornecer a implementação de winrt::to_hstring.
namespace winrt
{
hstring to_hstring(StatusEnum status)
{
switch (status)
{
case StatusEnum::Success: return L"Success";
case StatusEnum::AccessDenied: return L"AccessDenied";
case StatusEnum::DisabledByPolicy: return L"DisabledByPolicy";
default: return to_hstring(static_cast<int>(status));
}
}
}
Em geral, essas conversões em cadeia de caracteres são consumidas implicitamente pela vinculação de dados.
<TextBlock>
You have <Run Text="{Binding FlowerCount}"/> flowers.
</TextBlock>
<TextBlock>
Most recent status is <Run Text="{x:Bind LatestOperation.Status}"/>.
</TextBlock>
Essas associações executarão winrt::to_hstring da propriedade associada. No caso do segundo exemplo (o StatusEnum), você precisará fornecer sua própria sobrecarga de winrt::to_hstring; caso contrário, obterá um erro do compilador.
Confira também Como portar o método Footer_Click.
Construção da cadeia de caracteres
Para a construção de cadeia de caracteres, o C# tem um tipo StringBuilder interno.
Categoria | C# | C++/WinRT |
---|---|---|
Construção da cadeia de caracteres | StringBuilder builder; builder.Append(...); |
std::wostringstream builder; builder << ...; |
Acrescentar uma cadeia de caracteres do Windows Runtime preservando os valores nulos | builder.Append(s); |
builder << std::wstring_view{ s }; |
Adicionar uma nova linha | builder.Append(Environment.NewLine); |
builder << std::endl; |
Acessar o resultado | s = builder.ToString(); |
ws = builder.str(); |
Confira também Como portar o método BuildClipboardFormatsOutputString e Como portar o método DisplayChangedFormats.
Executar código no thread da IU principal
Este exemplo é obtido do exemplo de scanner de código de barras.
Quando você deseja trabalhar no thread da IU principal em um projeto C#, normalmente você usa o método CoreDispatcher.RunAsync, assim.
private async void Watcher_Added(DeviceWatcher sender, DeviceInformation args)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
// Do work on the main UI thread here.
});
}
É muito mais simples expressar isso em C++/WinRT. Observe que estamos aceitando parâmetros por valor na pressuposição que desejaremos acessá-los após o primeiro ponto de suspensão (co_await
, nesse caso). Para obter mais informações, consulte Passagem de parâmetros.
winrt::fire_and_forget Watcher_Added(DeviceWatcher sender, winrt::DeviceInformation args)
{
co_await Dispatcher();
// Do work on the main UI thread here.
}
Se você precisar fazer o trabalho com uma prioridade diferente do padrão, confira a função winrt::resume_foreground, que tem uma sobrecarga que recebe uma prioridade. Para obter exemplos de código que mostram como aguardar uma chamada para winrt::resume_foreground, confira Programando com a afinidade de thread em mente.
Tarefas relacionadas à portabilidade que são específicas para o C++/WinRT
Definir as classes de runtime na IDL
Confira IDL para o tipo MainPage e Consolidar os arquivos .idl
.
Incluir os arquivos de cabeçalho do namespace C++/WinRT do Windows de que você precisa
No C++/WinRT, sempre que desejar usar um tipo de um namespace do Windows, inclua o arquivo de cabeçalho do namespace C++/WinRT do Windows correspondente. Para obter um exemplo, confira Como portar o método NotifyUser.
Conversão boxing e unboxing
O C# faz a conversão boxing automática de escalares em objetos. O C++/WinRT exige que você chame explicitamente a função winrt::box_value. Ambas as linguagens exigem que você faça a conversão unboxing explicitamente. Confira Conversão boxing e unboxing com o C++/WinRT.
Nas tabelas a seguir, usaremos estas definições.
C# | C++/WinRT |
---|---|
int i; |
int i; |
string s; |
winrt::hstring s; |
object o; |
IInspectable o; |
Operação | C# | C++/WinRT |
---|---|---|
Conversão boxing | o = 1; o = "string"; |
o = box_value(1); o = box_value(L"string"); |
Conversão unboxing | i = (int)o; s = (string)o; |
i = unbox_value<int>(o); s = unbox_value<winrt::hstring>(o); |
O C++/CX e o C# gerarão exceções se você tentar fazer a conversão unboxing de um ponteiro nulo em um tipo de valor. O C++/WinRT considera isso um erro de programação e falha. No C++/WinRT, use a função winrt::unbox_value_or se desejar lidar com o caso em que o objeto não é do tipo que você pensou que fosse.
Cenário | C# | C++/WinRT |
---|---|---|
Fazer a conversão unboxing de um inteiro conhecido | i = (int)o; |
i = unbox_value<int>(o); |
Se o for nulo | System.NullReferenceException |
Falha |
Se o não for um int convertido | System.InvalidCastException |
Falha |
Fazer a conversão unboxing de int, se for nulo, usar fallback; falhar, em qualquer outra situação | i = o != null ? (int)o : fallback; |
i = o ? unbox_value<int>(o) : fallback; |
Fazer a conversão unboxing de int, se possível; usar fallback para qualquer outra situação | i = as int? ?? fallback; |
i = unbox_value_or<int>(o, fallback); |
Para obter um exemplo, confira Como portar o método OnNavigatedTo e Como portar o método Footer_Click.
Conversão boxing e unboxing de uma cadeia de caracteres
Uma cadeia de caracteres é, de algumas maneiras, um tipo de valor e, de outras, um tipo de referência. O C# e o C++/WinRT tratam as cadeias de caracteres de maneira diferente.
O tipo do ABI HSTRING é um ponteiro para uma cadeia de caracteres de contagem de referências. Mas ele não é derivado de IInspectable, portanto, não é tecnicamente um objeto. Além disso, um HSTRING nulo representa a cadeia de caracteres vazia. A conversão boxing de itens não derivados de IInspectable é feita encapsulando-os dentro de um IReference<T> e o Windows Runtime fornece uma implementação padrão na forma do objeto PropertyValue (os tipos personalizados são relatados como PropertyType::OtherType).
O C# representa uma cadeia de caracteres do Windows Runtime como um tipo de referência, enquanto o C++/WinRT projeta uma cadeia de caracteres como um tipo de valor. Isso significa que uma cadeia de caracteres nula convertida pode ter diferentes representações, dependendo do procedimento adotado.
Comportamento | C# | C++/WinRT |
---|---|---|
Declarações | object o; string s; |
IInspectable o; hstring s; |
Categoria de tipo de cadeia de caracteres | Tipo de referência | Tipo de valor |
projetos HSTRING nulos como | "" |
hstring{} |
São nulos e idênticos "" ? |
Não | Sim |
Validade de nulo | s = null; s.Length gera NullReferenceException |
s = hstring{}; s.size() == 0 (válido) |
Se você atribuir uma cadeia de caracteres nula ao objeto | o = (string)null; o == null |
o = box_value(hstring{}); o != nullptr |
Se você atribuir "" ao objeto |
o = ""; o != null |
o = box_value(hstring{L""}); o != nullptr |
Conversões boxing e unboxing básicas.
Operação | C# | C++/WinRT |
---|---|---|
Fazer a conversão boxing de uma cadeia de caracteres | o = s; A cadeia de caracteres vazia se torna um objeto não nulo. |
o = box_value(s); A cadeia de caracteres vazia se torna um objeto não nulo. |
Fazer a conversão unboxing de uma cadeia de caracteres conhecida | s = (string)o; O objeto nulo torna-se uma cadeia de caracteres nula. InvalidCastException se não for uma cadeia de caracteres. |
s = unbox_value<hstring>(o); O objeto nulo falha. Falha se não for uma cadeia de caracteres. |
Fazer a conversão unboxing de uma possível cadeia de caracteres | s = o as string; Objeto nulo ou não cadeia de caracteres se torna uma cadeia de caracteres nula. OU s = o as string ?? fallback; Nulo ou não cadeia de caracteres se torna fallback. Cadeia de caracteres vazia preservada. |
s = unbox_value_or<hstring>(o, fallback); Nulo ou não cadeia de caracteres se torna fallback. Cadeia de caracteres vazia preservada. |
Como disponibilizar uma classe para a extensão de marcação {Binding}
Se você pretende usar a extensão de marcação {Binding} para associar dados ao tipo de dados, confira Objeto de associação declarado usando {Binding}.
Como consumir objetos por meio da marcação XAML
Em um projeto C#, você pode consumir membros privados e elementos nomeados por meio da marcação XAML. Mas no C++/WinRT, todas as entidades consumidas com o uso da extensão de marcação XAML {x:Bind} precisam ser expostas publicamente em IDL.
Além disso, a associação a um booliano exibe true
ou false
no C#, mas mostra Windows.Foundation.IReference`1<Boolean> no C++/WinRT.
Para obter mais informações e exemplos de código, confira Como consumir objetos para marcação.
Como disponibilizar uma fonte de dados para marcação XAML
No C++/WinRT 2.0.190530.8 ou superior, winrt::single_threaded_observable_vector cria um vetor observável que dá suporte a IObservableVector<T> e a IObservableVector<IInspectable>. Para obter um exemplo, confira Como portar a propriedade Scenarios.
Você pode criar o arquivo MIDL (.idl) desta forma (confira também Como fatorar classes de runtime em arquivos MIDL (.idl)).
namespace Bookstore
{
runtimeclass BookSku { ... }
runtimeclass BookstoreViewModel
{
Windows.Foundation.Collections.IObservableVector<BookSku> BookSkus{ get; };
}
runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
{
MainPage();
BookstoreViewModel MainViewModel{ get; };
}
}
Depois, implemente-o desta forma.
// BookstoreViewModel.h
...
struct BookstoreViewModel : BookstoreViewModelT<BookstoreViewModel>
{
BookstoreViewModel()
{
m_bookSkus = winrt::single_threaded_observable_vector<Bookstore::BookSku>();
m_bookSkus.Append(winrt::make<Bookstore::implementation::BookSku>(L"To Kill A Mockingbird"));
}
Windows::Foundation::Collections::IObservableVector<Bookstore::BookSku> BookSkus();
{
return m_bookSkus;
}
private:
Windows::Foundation::Collections::IObservableVector<Bookstore::BookSku> m_bookSkus;
};
...
Para obter informações, confira Controles de itens XAML; associação a uma coleção do C++/WinRT e Coleções com C++/WinRT.
Como disponibilizar uma fonte de dados para marcação XAML (antes do C++/WinRT 2.0.190530.8)
A vinculação de dados XAML exige que uma origem de itens implemente IIterable<IInspectable>, bem como uma das combinações de interfaces a seguir.
- IObservableVector<IInspectable>
- IBindableVector e INotifyCollectionChanged
- IBindableVector e IBindableObservableVector
- IBindableVector por si só (não responderá a alterações)
- IVector<IInspectable>
- IBindableIterable (iterará e salvará elementos em uma coleção particular)
Uma interface genérica, como IVector<T>, não pode ser detectada em runtime. Cada IVector<T> tem um IID (identificador de interface) distinto, que é uma função de T. Qualquer desenvolvedor pode expandir o conjunto de T de maneira arbitrária e, portanto, é evidente que o código de associação XAML nunca pode saber o conjunto completo a ser consultado. Essa restrição não é um problema para o C#, porque cada objeto CLR que implementa IEnumerable<T> implementa IEnumerable automaticamente. No nível do ABI, isso significa que cada objeto que implementa IObservableVector<T> implementa IObservableVector<IInspectable> automaticamente.
O C++/WinRT não oferece essa garantia. Se uma classe de runtime do C++/WinRT implementar IObservableVector<T>, não poderemos pressupor que uma implementação de IObservableVector<IInspectable> também seja fornecida de alguma forma.
Consequentemente, veja abaixo como o exemplo anterior precisará ser examinado.
...
runtimeclass BookstoreViewModel
{
// This is really an observable vector of BookSku.
Windows.Foundation.Collections.IObservableVector<Object> BookSkus{ get; };
}
Veja também a implementação.
// BookstoreViewModel.h
...
struct BookstoreViewModel : BookstoreViewModelT<BookstoreViewModel>
{
BookstoreViewModel()
{
m_bookSkus = winrt::single_threaded_observable_vector<Windows::Foundation::IInspectable>();
m_bookSkus.Append(winrt::make<Bookstore::implementation::BookSku>(L"To Kill A Mockingbird"));
}
// This is really an observable vector of BookSku.
Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> BookSkus();
{
return m_bookSkus;
}
private:
Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> m_bookSkus;
};
...
Caso você precise acessar objetos em m_bookSkus, precisará executar o QI novamente para Bookstore::BookSku.
Widget MyPage::BookstoreViewModel(winrt::hstring title)
{
for (auto&& obj : m_bookSkus)
{
auto bookSku = obj.as<Bookstore::BookSku>();
if (bookSku.Title() == title) return bookSku;
}
return nullptr;
}
Classes derivadas
Para derivação de uma classe de runtime, a classe base precisa ser combinável. O C# não exige que você execute nenhuma etapa especial para tornar suas classes combináveis, ao contrário do C++/WinRT. Use a palavra-chave não selada para indicar que deseja que a classe seja utilizável como uma classe base.
unsealed runtimeclass BasePage : Windows.UI.Xaml.Controls.Page
{
...
}
runtimeclass DerivedPage : BasePage
{
...
}
Na classe do cabeçalho do tipo de implementação, inclua o arquivo de cabeçalho da classe base antes de incluir o cabeçalho gerado automaticamente da classe derivada. Caso contrário, você receberá erros como "uso ilícito deste tipo como uma expressão".
// DerivedPage.h
#include "BasePage.h" // This comes first.
#include "DerivedPage.g.h" // Otherwise this header file will produce an error.
namespace winrt::MyNamespace::implementation
{
struct DerivedPage : DerivedPageT<DerivedPage>
{
...
}
}