Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Uma propriedade que pode ser efetivamente associada a um controle XAML é conhecida como uma propriedade observável . Essa ideia baseia-se no padrão de design de software conhecido como o padrão de observador. Este tópico mostra como implementar propriedades observáveis no C++/WinRT e como associar controles XAML a eles (para obter informações em segundo plano, consulte Associação de dados).
Important
Para obter conceitos e termos essenciais que dão suporte à compreensão de como consumir e criar classes de runtime com C++/WinRT, consulte Consumir APIs com APIs C++/WinRT e Criar com C++/WinRT.
O que significa observável para uma propriedade?
Digamos que uma classe de runtime chamada BookSku tenha uma propriedade chamada Title. Se BookSku gerar o evento INotifyPropertyChanged::P ropertyChanged sempre que o valor do Título for alterado, isso significa que Title é uma propriedade observável. É o comportamento de BookSku (gerando ou não o evento) que determina quais, se houver, de suas propriedades são observáveis.
Um elemento de texto XAML, ou controle, pode associar e manipular esses eventos. Esse elemento ou controle manipula o evento recuperando os valores atualizados e atualizando-se para mostrar o novo valor.
Note
Para obter informações sobre como instalar e usar o VSIX (Extensão de Visual Studio) do C++/WinRT e o pacote NuGet (que juntos fornecem suporte ao modelo de projeto e ao build), consulte Visual Studio suporte para C++/WinRT.
Criar um aplicativo vazio (Livraria)
Comece criando um novo projeto no Microsoft Visual Studio. Crie um Blank App, Packaged (WinUI 3 in Desktop) para um projeto em C++ e nomeie-o Bookstore. Verifique se Colocar a solução e o projeto no mesmo diretório está desmarcada. Direcione a versão mais recente disponível (ou seja, não versão prévia) do SDK do Windows.
Vamos criar uma nova classe para representar um livro que tenha uma propriedade de título observável. Estamos criando e consumindo a classe na mesma unidade de compilação. Mas queremos conseguir nos vincular a essa classe a partir do XAML e, por esse motivo, ela será uma classe de tempo de execução. E vamos usar C++/WinRT tanto para desenvolvê-lo quanto para consumi-lo.
A primeira etapa na criação de uma nova classe de runtime é adicionar um novo item midl file (.idl) ao projeto. Dê ao novo item o nome BookSku.idl. Exclua o conteúdo padrão de BookSku.idl e cole esta declaração de classe de runtime.
// BookSku.idl
namespace Bookstore
{
runtimeclass BookSku : Microsoft.UI.Xaml.Data.INotifyPropertyChanged
{
BookSku(String title);
String Title;
}
}
Note
Suas classes de modelo de exibição, na verdade, qualquer classe de runtime que você declarar em seu aplicativo não precisam derivar de uma classe base. A classe BookSku declarada acima é um exemplo disso. Ele implementa uma interface, mas não deriva de nenhuma classe base.
Qualquer classe de tempo de execução que você declarar no aplicativo e que de fato derive de uma classe base é conhecida como uma classe componível. E há restrições em torno de classes composáveis. Para que um aplicativo passe nos testes do Kit de Certificação de Aplicativos do Windows usados pelo Visual Studio e pela Microsoft Store para validar envios (e, portanto, para que o aplicativo seja ingerido com êxito na Microsoft Store), uma classe composável deve, em última análise, derivar de uma classe base do Windows. Isso significa que a classe na própria raiz da hierarquia de herança deve ser um tipo proveniente de um namespace Windows.* ou Microsoft.*. Se você precisar derivar uma classe de runtime de uma classe base, por exemplo, para implementar uma classe BindableBase para todos os modelos de exibição de onde derivar, você poderá derivar de Microsoft. UI. Xaml.DependencyObject.
Um modelo de exibição é uma abstração de um modo de exibição e, portanto, é associado diretamente à exibição (a marcação XAML). Um modelo de dados é uma abstração dos dados e é usado apenas pelos seus modelos de exibição, não sendo vinculado diretamente ao XAML. Portanto, você pode declarar seus modelos de dados não como classes de runtime, mas como structs ou classes C++. Eles não precisam ser declarados em MIDL e você está livre para usar qualquer hierarquia de herança desejada.
Salve o arquivo e compile o projeto. A compilação ainda não vai ser totalmente bem-sucedida, mas vai fazer algumas coisas necessárias para nós. Especificamente, durante o processo de build, a midl.exe ferramenta é executada para criar um arquivo de metadados Windows Runtime que descreve a classe de runtime (o arquivo é colocado no disco em \Bookstore\Debug\Bookstore\Unmerged\BookSku.winmd). Em seguida, a cppwinrt.exe ferramenta é executada para gerar arquivos de código-fonte para dar suporte a você na criação e no consumo da classe de runtime. Esses arquivos incluem stubs para que você comece a implementar a classe de runtime bookSku que você declarou em sua IDL. Vamos encontrá-los no disco daqui a pouco, mas esses stubs são \Bookstore\Bookstore\Generated Files\sources\BookSku.h e BookSku.cpp.
Agora, clique com o botão direito do mouse no nó do projeto no Visual Studio e clique em Abrir Pasta no Explorador de Arquivos. Isso abre a pasta do projeto no Explorador de Arquivos. Agora você deve examinar o conteúdo da \Bookstore\Bookstore\ pasta. A partir daí, navegue até a pasta \Generated Files\sources\ e copie os arquivos stub BookSku.h e BookSku.cpp para a área de transferência. Navegue novamente até a pasta do projeto (\Bookstore\Bookstore\) e cole os dois arquivos que você acabou de copiar. Por fim, no Gerenciador de Soluções, com o nó do projeto selecionado, certifique-se de que a opção Mostrar todos os arquivos esteja ativada. Clique com o botão direito do mouse nos arquivos stub copiados e clique em Include In Project.
Implementar BookSku
Agora vamos abrir \Bookstore\Bookstore\BookSku.h e BookSku.cpp implementar nossa classe de runtime. Primeiro, você verá um static_assert na parte superior do BookSku.h e BookSku.cpp, que você precisará remover.
Em seguida, em BookSku.h, faça essas alterações.
- No construtor padrão, altere
= defaultpara= delete. Isso ocorre porque não queremos um construtor padrão. - Adicione um membro privado para armazenar a cadeia de caracteres de título. Observe que temos um construtor que usa um valor winrt::hstring . Esse valor é a cadeia de caracteres de título.
- Adicione outro membro privado para o evento que geraremos quando o título for alterado.
Depois de fazer essas alterações, você BookSku.h terá esta aparência.
// BookSku.h
#pragma once
#include "BookSku.g.h"
namespace winrt::Bookstore::implementation
{
struct BookSku : BookSkuT<BookSku>
{
BookSku() = delete;
BookSku(winrt::hstring const& title);
winrt::hstring Title();
void Title(winrt::hstring const& value);
winrt::event_token PropertyChanged(Microsoft::UI::Xaml::Data::PropertyChangedEventHandler const& value);
void PropertyChanged(winrt::event_token const& token);
private:
winrt::hstring m_title;
winrt::event<Microsoft::UI::Xaml::Data::PropertyChangedEventHandler> m_propertyChanged;
};
}
namespace winrt::Bookstore::factory_implementation
{
struct BookSku : BookSkuT<BookSku, implementation::BookSku>
{
};
}
Em BookSku.cpp, implemente as funções assim.
// BookSku.cpp
#include "pch.h"
#include "BookSku.h"
#include "BookSku.g.cpp"
namespace winrt::Bookstore::implementation
{
BookSku::BookSku(winrt::hstring const& title) : m_title{ title }
{
}
winrt::hstring BookSku::Title()
{
return m_title;
}
void BookSku::Title(winrt::hstring const& value)
{
if (m_title != value)
{
m_title = value;
m_propertyChanged(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{ L"Title" });
}
}
winrt::event_token BookSku::PropertyChanged(Microsoft::UI::Xaml::Data::PropertyChangedEventHandler const& handler)
{
return m_propertyChanged.add(handler);
}
void BookSku::PropertyChanged(winrt::event_token const& token)
{
m_propertyChanged.remove(token);
}
}
Na função mutador título , verificamos se um valor está sendo definido diferente do valor atual. E, nesse caso, atualizamos o título e também acionamos o evento INotifyPropertyChanged::P ropertyChanged com um argumento igual ao nome da propriedade que foi alterada. Isso é para que a interface do usuário (UI) saiba o valor de qual propriedade consultar novamente.
O projeto será compilado novamente agora, se você quiser verificar isso.
Declarar e implementar BookstoreViewModel
Nossa página XAML principal será associada a um modelo de exibição principal. E esse modelo de exibição terá várias propriedades, incluindo uma do tipo BookSku. Nesta etapa, declararemos e implementaremos nossa classe de runtime do modelo de exibição principal.
Adicione um novo item midl file (.idl) chamado BookstoreViewModel.idl. Consulte também Separando classes de tempo de execução em arquivos MIDL (.idl).
// BookstoreViewModel.idl
import "BookSku.idl";
namespace Bookstore
{
runtimeclass BookstoreViewModel
{
BookstoreViewModel();
BookSku BookSku{ get; };
}
}
Salve e faça a compilação (a compilação ainda não será totalmente bem-sucedida, mas o motivo de estarmos fazendo a compilação é gerar os arquivos stub novamente).
Copie BookstoreViewModel.h e BookstoreViewModel.cpp da Generated Files\sources pasta para a pasta do projeto e inclua-as no projeto. Abra esses arquivos (removendo novamente static_assert ) e implemente a classe de runtime, conforme mostrado abaixo. Observe como, em BookstoreViewModel.h, estamos incluindo BookSku.h, que declara o tipo de implementação para BookSku (que é winrt::Bookstore::implementation::BookSku). E estamos removendo = default do construtor padrão.
Note
Nas listagens abaixo de BookstoreViewModel.h e BookstoreViewModel.cpp, o código ilustra a forma padrão de construir o membro de dados m_bookSku. Esse é o método que remonta à primeira versão do C++/WinRT e é uma boa ideia estar pelo menos familiarizado com o padrão. Com o C++/WinRT versão 2.0 e posterior, há uma forma otimizada de construção disponível para você conhecida como construção uniforme (consulte Notícias e alterações, em C++/WinRT 2.0). Posteriormente neste tópico, mostraremos um exemplo de construção uniforme.
// BookstoreViewModel.h
#pragma once
#include "BookstoreViewModel.g.h"
#include "BookSku.h"
namespace winrt::Bookstore::implementation
{
struct BookstoreViewModel : BookstoreViewModelT<BookstoreViewModel>
{
BookstoreViewModel();
Bookstore::BookSku BookSku();
private:
Bookstore::BookSku m_bookSku{ nullptr };
};
}
namespace winrt::Bookstore::factory_implementation
{
struct BookstoreViewModel : BookstoreViewModelT<BookstoreViewModel, implementation::BookstoreViewModel>
{
};
}
// BookstoreViewModel.cpp
#include "pch.h"
#include "BookstoreViewModel.h"
#include "BookstoreViewModel.g.cpp"
namespace winrt::Bookstore::implementation
{
BookstoreViewModel::BookstoreViewModel()
{
m_bookSku = winrt::make<Bookstore::implementation::BookSku>(L"Atticus");
}
Bookstore::BookSku BookstoreViewModel::BookSku()
{
return m_bookSku;
}
}
Note
O tipo de m_bookSku é o tipo projetado (winrt::Bookstore::BookSku), e o parâmetro de template que você usa com winrt::make é o tipo de implementação (winrt::Bookstore::implementation::BookSku). Mesmo assim, make retorna uma instância do tipo projetado.
Agora o projeto será compilado novamente.
Adicionar uma propriedade do tipo BookstoreViewModel ao MainPage
Abra MainPage.idl, que declara a classe de runtime que representa nossa página principal da interface do usuário.
- Adicionar uma
importdiretiva para importarBookstoreViewModel.idl. - Adicione uma propriedade somente leitura chamada MainViewModel, do tipo BookstoreViewModel.
- Remova a propriedade MyProperty .
// MainPage.idl
import "BookstoreViewModel.idl";
namespace Bookstore
{
runtimeclass MainPage : Microsoft.UI.Xaml.Controls.Page
{
MainPage();
BookstoreViewModel MainViewModel{ get; };
}
}
Salve o arquivo. O projeto ainda não terá êxito totalmente na criação, mas a criação agora é uma coisa útil a se fazer porque regenera os arquivos de código-fonte nos quais a classe de runtime MainPage é implementada (\Bookstore\Bookstore\Generated Files\sources\MainPage.h e MainPage.cpp). Então vá em frente e construa agora. O erro de build que você pode esperar ver neste estágio é 'MainViewModel': não é membro de 'winrt::Bookstore::implementation::MainPage'.
Se você omitir a inclusão de BookstoreViewModel.idl (veja a listagem de MainPage.idl acima), verá o erro esperando < perto de "MainViewModel". Outra dica é garantir que você deixe todos os tipos no mesmo namespace: o namespace mostrado nas listagens de código.
Para resolver o erro que esperamos encontrar, agora você precisará copiar os stubs dos acessores da propriedade MainViewModel dos arquivos gerados (\Bookstore\Bookstore\Generated Files\sources\MainPage.h e MainPage.cpp) para \Bookstore\Bookstore\MainPage.h e MainPage.cpp. As etapas para fazer isso são descritas a seguir.
Em \Bookstore\Bookstore\MainPage.h, execute estas etapas.
- Inclua
BookstoreViewModel.h, que declara o tipo de implementação para BookstoreViewModel (que é winrt::Bookstore::implementation::BookstoreViewModel). - Adicione um membro privado para armazenar o modelo de exibição. Observe que a função acessador de propriedade (e o membro m_mainViewModel) é implementada em termos do tipo projetado para BookstoreViewModel (que é Bookstore::BookstoreViewModel).
- O tipo de implementação está no mesmo projeto (unidade de compilação) que o aplicativo, portanto, criamos m_mainViewModel por meio da sobrecarga do construtor que leva std::nullptr_t.
- Remova a propriedade MyProperty .
Note
No par de listagens abaixo para MainPage.h e MainPage.cpp, o código ilustra a maneira padrão de construir o membro de dados m_mainViewModel . Na seção a seguir, mostraremos uma versão que usa uma estrutura uniforme.
// MainPage.h
...
#include "BookstoreViewModel.h"
...
namespace winrt::Bookstore::implementation
{
struct MainPage : MainPageT<MainPage>
{
MainPage();
Bookstore::BookstoreViewModel MainViewModel();
void ClickHandler(Windows::Foundation::IInspectable const&, Microsoft::UI::Xaml::RoutedEventArgs const&);
private:
Bookstore::BookstoreViewModel m_mainViewModel{ nullptr };
};
}
...
Em \Bookstore\Bookstore\MainPage.cpp, conforme mostrado na listagem abaixo, faça as seguintes alterações.
- Chame winrt::make (com o tipo de implementação BookstoreViewModel ) para atribuir uma nova instância do tipo BookstoreViewModel projetado a m_mainViewModel. Como vimos acima, o construtor BookstoreViewModel cria um novo objeto BookSku como um membro de dados privado, definindo seu título inicialmente como
L"Atticus". - No manipulador de eventos do botão (ClickHandler), atualize o título do livro para seu título publicado.
- Implemente o acessador para a propriedade MainViewModel .
- Remova a propriedade MyProperty .
// MainPage.cpp
#include "pch.h"
#include "MainPage.h"
#include "MainPage.g.cpp"
using namespace winrt;
using namespace Microsoft::UI::Xaml;
namespace winrt::Bookstore::implementation
{
MainPage::MainPage()
{
m_mainViewModel = winrt::make<Bookstore::implementation::BookstoreViewModel>();
InitializeComponent();
}
void MainPage::ClickHandler(Windows::Foundation::IInspectable const& /* sender */, Microsoft::UI::Xaml::RoutedEventArgs const& /* args */)
{
MainViewModel().BookSku().Title(L"To Kill a Mockingbird");
}
Bookstore::BookstoreViewModel MainPage::MainViewModel()
{
return m_mainViewModel;
}
}
Construção uniforme
Para usar a construção uniforme em vez de winrt::make, declare MainPage.h e inicialize m_mainViewModel em apenas uma etapa, conforme mostrado abaixo.
// MainPage.h
...
#include "BookstoreViewModel.h"
...
struct MainPage : MainPageT<MainPage>
{
...
private:
Bookstore::BookstoreViewModel m_mainViewModel;
};
...
E então, no construtor MainPage em MainPage.cpp, não há necessidade de usar o código m_mainViewModel = winrt::make<Bookstore::implementation::BookstoreViewModel>();.
Para obter mais informações sobre construção uniforme e exemplos de código, consulte Aceitar a construção uniforme e acesso direto à implementação.
Associar o botão à propriedade Title
Abra MainPage.xaml, que contém as marcações XAML para nossa página principal de UI. Conforme mostrado na listagem abaixo, remova o nome do botão e altere o valor da propriedade Content de um literal para uma expressão de associação. Observe a propriedade Mode=OneWay na expressão de associação (unidirecional, do modelo de visualização para a interface do usuário). Sem essa propriedade, a interface do usuário não responderá a eventos de alteração de propriedade.
<Button Click="ClickHandler" Content="{x:Bind MainViewModel.BookSku.Title, Mode=OneWay}"/>
Agora, construa e execute o projeto. Clique no botão para executar o manipulador de eventos Clique . Esse manipulador chama a função modificadora do título do livro; essa função gera um evento para que a interface do usuário saiba que a propriedade Title foi alterada; e o botão consulta novamente o valor dessa propriedade para atualizar seu próprio valor de Content.
Usando a extensão de marcação {Binding} com C++/WinRT
Para a versão atualmente lançada do C++/WinRT, para poder usar a extensão de marcação {Binding}, você precisará implementar as interfaces ICustomPropertyProvider e ICustomProperty .
Vinculação elemento a elemento
Você pode associar a propriedade de um elemento XAML à propriedade de outro elemento XAML. Aqui está um exemplo de como isso fica no código de marcação.
<TextBox x:Name="myTextBox" />
<TextBlock Text="{x:Bind myTextBox.Text, Mode=OneWay}" />
Você precisará declarar a entidade XAML nomeada myTextBox como uma propriedade somente para leitura em seu arquivo Midl (.idl).
// MainPage.idl
runtimeclass MainPage : Microsoft.UI.Xaml.Controls.Page
{
MainPage();
Microsoft.UI.Xaml.Controls.TextBox myTextBox{ get; };
}
Esta é a razão para essa necessidade. Todos os tipos que o compilador XAML precisa validar (incluindo os usados em {x:Bind}) são lidos de Windows Metadados (WinMD). Tudo o que você precisa fazer é adicionar a propriedade somente leitura ao arquivo Midl. Não o implemente, pois o code-behind XAML gerado automaticamente já fornece a implementação.
Consumindo objetos da marcação XAML
Todas as entidades consumidas usando a extensão de marcação XAML {x:Bind} devem ser expostas publicamente no IDL. Além disso, se a marcação XAML contiver uma referência a outro elemento que também está na marcação, o getter dessa marcação deverá estar presente na IDL.
<Page x:Name="MyPage">
<StackPanel>
<CheckBox x:Name="UseCustomColorCheckBox" Content="Use custom color"
Click="UseCustomColorCheckBox_Click" />
<Button x:Name="ChangeColorButton" Content="Change color"
Click="{x:Bind ChangeColorButton_OnClick}"
IsEnabled="{x:Bind UseCustomColorCheckBox.IsChecked.Value, Mode=OneWay}"/>
</StackPanel>
</Page>
O elemento ChangeColorButton refere-se ao elemento UseCustomColorCheckBox por meio da associação. Portanto, a IDL desta página deve declarar uma propriedade somente para leitura chamada UseCustomColorCheckBox para que ela seja acessível à vinculação.
O delegado do manipulador de evento de clique para UseCustomColorCheckBox usa a sintaxe clássica de delegado do XAML, portanto não precisa de uma entrada no IDL; basta que ele seja público na sua classe de implementação. Por outro lado, ChangeColorButton também possui um manipulador de evento de {x:Bind} clique, que também deve ser incluído na IDL.
runtimeclass MyPage : Microsoft.UI.Xaml.Controls.Page
{
MyPage();
// These members are consumed by binding.
void ChangeColorButton_OnClick();
Microsoft.UI.Xaml.Controls.CheckBox UseCustomColorCheckBox{ get; };
}
Você não precisa fornecer uma implementação para a propriedade UseCustomColorCheckBox . O gerador de código XAML faz isso para você.
Vinculação a booleano
Você pode fazer isso em um modo de diagnóstico:
<TextBlock Text="{Binding CanPair}"/>
Isso é exibido como true ou false em C++/CX; mas em C++/WinRT exibe Windows.Foundation.IReference`1<Boolean>.
Em vez disso, use x:Bind ao associar a um booliano.
<TextBlock Text="{x:Bind CanPair}"/>
Usando as bibliotecas de implementação de Windows (WIL)
As Windows Implementation Libraries (WIL) fornece recursos auxiliares para facilitar a implementação de propriedades vinculáveis. Consulte Notifying Properties na documentação do WIL.
APIs importantes
Tópicos relacionados:
Windows developer