Usando o padrão MVVM (Model-View-ViewModel) no Hilo (aplicativos da Windows Store em C++ e XAML)

Applies to Windows only

De: Desenvolvendo um aplicativo da Windows Store de ponta a ponta em C++ e XAML: Hilo

Logotipo padrões & práticas

Página anterior | Próxima página

No início do projeto, decidimos adotar o padrão MVVM para a arquitetura do Hilo. O que nos atraiu foi o fato de que o padrão MVVM facilita o processo de manter e testar um aplicativo da Windows Store em C++ e XAML, especialmente à medida que ele cresce. O MVVM é um padrão relativamente novo para aplicativos em C++.

Baixar

Baixar o exemplo do Hilo
Baixar o manual (PDF)

Depois de baixar o código, veja as instruções em Aprenda a usar o Hilo.

Você aprenderá

  • Como o MVVM pode beneficiar os aplicativos da Windows Store.
  • Técnicas recomendadas para aplicar o padrão MVVM a aplicativos da Windows Store.
  • Como mapear exibições para elementos da interface do usuário.
  • Como compartilhar modelos de exibição entre exibições.
  • Como executar comandos em um modelo de exibição.

Aplica-se a

  • Tempo de Execução do Windows para Windows 8
  • Extensões de componente do Visual C++ (C++/CX)
  • XAML

O que é o MVVM?

O MVVM é um padrão de arquitetura. Ele é uma especialização do padrão de modelo de apresentação que foi introduzido por Martin Fowler. Ele também está relacionado aos padrões MVC (Model-View-Controller) e MVP (Model-View-Presenter) que você talvez já conheça.

Um aplicativo que usa o MVVM separa comportamento de apresentação, lógica de negócios e interface do usuário.

  • Os modelos representam o estado e as operações dos objetos de negócios que seu aplicativo manipula. Por exemplo, o Hilo lê e modifica arquivos de imagem. Portanto, faz sentido que os tipos de dados de arquivos de imagem e as operações em arquivos de imagem façam parte do modelo do Hilo.
  • As exibições contêm elementos da interface do usuário e incluem código que implementa a experiência do usuário do aplicativo. Uma exibição define a estrutura, o layout e a aparência do que o usuário vê na tela. Grades, páginas, botões e caixas de texto são exemplos dos elementos que os objetos de exibição gerenciam.
  • Os modelos de exibição encapsulam o estado, as ações e as operações do aplicativo. Um modelo de exibição serve como uma camada de separação entre o modelo e a exibição. Ele fornece os dados em um formato que a exibição pode consumir. Além disso, atualiza o modelo para que não haja necessidade de interação entre ele e a exibição. Os modelos de exibição respondem a comandos e eventos de gatilho. Eles também funcionam como fontes de dados para os dados mostrados pelas exibições. Os modelos de exibição são criados especificamente para oferecer suporte a uma exibição. É possível interpretar um modelo de exibição como o aplicativo sem a interface do usuário. Nos aplicativos da Windows Store, é possível vincular exibições a seus modelos de exibição correspondentes por meio de declarações.

Veja a seguir as relações entre uma exibição, um modelo de exibição e um modelo.

Relações entre exibição, modelo de exibição e modelo

[Início]

MVVM no Hilo

No Hilo, há uma classe de exibição por página da interface do usuário. Uma página é uma instância da classe Windows::UI::Xaml::Controls::Page. Cada exibição tem uma classe de modelo de exibição correspondente. Todos os modelos de exibição do Hilo compartilham o modelo de domínio do aplicativo, que geralmente é chamado simplesmente de modelo. O modelo consiste em classes que os modelos de exibição usam para implementar a funcionalidade do aplicativo.

Observação  Para acessar diretamente um guia passo a passo do código da exibição e do modelo de exibição do Hilo, veja Criando e navegando pelas páginas do Hilo.

Na solução Hilo.sln do Visual Studio, há pastas de solução nomeadas de acordo com cada uma das camadas do MVVM.

Pastas de solução do Visual Studio
  • A pasta Models contém os arquivos .cpp (C++) e .h (cabeçalho C++) que compõem o modelo do Hilo.
  • A pasta Views contém os arquivos XAML e as classes da interface do usuário.
  • A pasta ViewModels contém os arquivos .cpp e .h das classes de modelo de exibição do aplicativo.

Com o padrão MVVM, a vinculação de dados XAML permite que um modelo de exibição funcione como o contexto de dados de uma página. Um contexto de dados é responsável por disponibilizar propriedades que fornecem a exibição com dados dos elementos da interface do usuário na página.

Observação  Você não precisa usar a vinculação de dados para conectar as exibições e os modelos de exibição. Também é possível usar um arquivo code-behind com código em C++ associado a classes de página. Você pode reconhecer os arquivos code-behind porque eles usam o sufixo .xaml.cpp. Por exemplo, no Hilo, o arquivo MainHubView.xaml.cpp é o arquivo code-behind da página que o arquivo MainHubView.xaml define. Muitas ferramentas de design visual, como o Microsoft Expression, são otimizadas para uso com vinculação de dados.

Os modelos de exibição conectam-se ao modelo subjacente do aplicativo chamando métodos de instância. Não é necessária uma vinculação especial para fazer essas chamadas. Se você desejar que haja uma forte separação entre o modelo e os modelos de exibição do aplicativo, pode empacotar as classes de modelo em uma biblioteca separada. O Hilo não usa uma biblioteca separada para seu modelo. Em vez disso, ele simplesmente mantém os arquivos que definem as classes de modelo em uma pasta separada do projeto do Hilo no Visual Studio.

[Início]

Por que usar o MVVM no Hilo?

Existem duas abordagens principais para a implementação de uma interface do usuário: usar um arquivo code-behind para a lógica de apresentação ou separar a estrutura da interface do usuário e a lógica de apresentação com um padrão como o MVVM. Após uma análise das necessidades, optamos pela abordagem do MVVM para o Hilo porque:

  • Queríamos testar nossa lógica de apresentação. O MVVM facilita a separação clara entre a lógica de exibição e os controles da interface do usuário, o que é importante para a automação de testes.
  • Queríamos garantir que a exibição e a lógica de apresentação pudessem evoluir de maneira independente e reduzir dependências entre desenvolvedores e designers da interface do usuário. O MVVM, usado com a vinculação de dados do XAML, possibilita isso.

[Início]

Para saber mais

Saiba mais sobre o MVVM online. Veja a seguir alguns exemplos no código gerenciado, mas os conceitos também são aplicáveis ao C++:

[Início]

Variações do padrão MVVM

É possível personalizar o padrão MVVM de diversas maneiras. Analisemos algumas delas.

Mapeando exibições para elementos de interface do usuário que não são páginas

No Hilo, cada classe de página é um objeto de exibição MVVM e todas as exibições MVVM são páginas. Porém, você não precisa fazer o mesmo. Por exemplo, uma exibição poderia ser um DataTemplate de um objeto em um ItemsControl.

Compartilhando modelos de exibição entre várias exibições

Uma exibição pode ter seu próprio modelo de exibição ou pode compartilhar o modelo de exibição de outra exibição. A escolha depende do fato de as exibições compartilharem ou não muitas das mesmas funcionalidades. No Hilo, cada exibição é associada a um modelo de exibição exclusivo para fins de simplicidade.

Executando comandos em um modelo de exibição

Você pode usar a vinculação de dados para botões e outros controles da interface do usuário que fazem o aplicativo executar operações. Se o controle for uma fonte de comando, a propriedade Command do controle será vinculada por dados a uma propriedade ICommand no modelo de exibição. Quando o comando do controle for invocado, o código no modelo de exibição será executado. Recomendamos que você use a vinculação de dados para comandos quando usar o MVVM.

Veja a seguir um exemplo da execução de um comando do Hilo. A página de rotação de imagem contém um elemento de interface do usuário para o botão Salvar Arquivo. Esse código XAML é proveniente do arquivo RotateImageView.xaml.


<Button x:Name="SaveButton"
        x:Uid="AcceptAppBarButton"
        Command="{Binding SaveCommand}" 
        Style="{StaticResource AcceptAppBarButtonStyle}"
        Tag="Save" />


A expressão "Command={Binding SaveCommand}" cria uma associação entre a propriedade Command do botão e a propriedade SaveCommand da classe RotateImageViewModel. A propriedade SaveCommand contém um identificado para um objeto ICommand. O próximo código é proveniente do arquivo RotateImageViewModel.cpp.


ICommand^ RotateImageViewModel::SaveCommand::get()
{
    return m_saveCommand;
}


A variável membro m_saveCommand é inicializada no construtor da classe RotateImageViewModel. Veja a seguir o código do arquivo RotateImageViewModel.cpp.


m_saveCommand = ref new DelegateCommand(ref new ExecuteDelegate(this, &RotateImageViewModel::SaveImage), nullptr);


A classe DelegateCommand do Hilo é uma implementação da interface ICommand. A classe define o tipo delegado ExecuteDelegate. Os delegados permitem usar um ponteiro para uma função membro em C++ como um objeto do Tempo de Execução do Windows que pode ser chamado. O Hilo invoca o ExecuteDelegate quando a interface do usuário aciona o comando.

Observação  Para saber mais sobre a extensão de linguagem de delegados em C++/CX, veja Delegados (C++/CX).

Como usamos a vinculação de dados, alterar a ação do comando de salvamento é uma questão de atribuir um objeto DelegateCommand diferente à variável membro m_saveCommand. Não há necessidade de alterar o arquivo XAML da exibição.

Neste exemplo, a função membro subjacente é proveniente do arquivo RotateImageViewModel.cpp.


void RotateImageViewModel::SaveImage(Object^ parameter)
{
   // Asynchronously save image file
}	  

Usando um objeto localizador de modelo de exibição para vincular exibições a modelos de exibição

No Hilo, cada exibição (página) tem um modelo de exibição correspondente.

Se você usar o MVVM, o aplicativo precisará conectar suas exibições a seus modelos de exibição. Isso significa que cada exibição deve ter um modelo de exibição atribuído a sua propriedade DataContext. No Hilo, usamos uma única classe ViewModelLocator, pois precisávamos configurar código para ser executado antes que os elementos da interface do usuário fossem vinculados ao modelo de exibição. A classe ViewModelLocator tem propriedades que recuperam um objeto de modelo de exibição para cada página do aplicativo. Veja Criando e navegando entre páginas para obter uma descrição de como a classe ViewModelLocator vincula exibições e modelos de exibição no Hilo.

Você não precisa usar uma classe localizadora de modelo de exibição. Na realidade, há várias maneiras de vincular uma exibição a seu objeto de modelo de exibição correspondente. Se você não usar uma classe localizadora de modelo de exibição, poderá conectar a criação e a destruição de instâncias do modelo de exibição à vida útil do objeto de exibição correspondente. Por exemplo, você pode criar uma nova instância de modelo de exibição sempre que a página for carregada.

Também pode conectar exibições a modelos de exibição em um arquivo code-behind. O código em um arquivo code-behind pode criar uma nova instância do modelo de exibição e atribuí-la à propriedade DataContext da exibição. É possível criar uma instância do modelo de exibição no método Initialize da página ou em seu método OnNavigatedTo.

[Início]

Dicas para desenvolver aplicativos da Windows Store usando o MVVM

Veja a seguir algumas dicas para aplicar o padrão MVVM a aplicativos da Windows Store em C++.

Mantenha dependências de exibição fora do modelo de exibição

Ao desenvolver um aplicativo da Windows Store com o padrão MVVM, você precisa decidir o que será colocado no modelo, nas exibições e nos modelos de exibição. A divisão geralmente é uma questão de preferência, mas alguns princípios gerais são aplicáveis. Idealmente, defina a exibição com XAML somente com code-behind limitado sem lógica de negócios. Também recomendamos que você mantenha o modelo de exibição sem dependências dos tipos de dados para exibições ou elementos da interface do usuário. Não inclua arquivos de cabeçalho de exibição nos arquivos de origem do modelo de exibição.

Centralize as conversões de dados no modelo de exibição ou em uma camada de conversão

O modelo de exibição fornece dados do modelo em um formato que pode ser facilmente usado pela exibição. Para isso, o modelo de exibição às vezes precisa executar a conversão de dados. Colocar essa conversão de dados no modelo de exibição é uma boa ideia, pois as propriedades são fornecidas em um formato ao qual a interface do usuário pode ser vinculada.

Também é possível ter uma camada de conversão de dados separada que fica entre o modelo de exibição e a exibição. Isso pode ocorrer, por exemplo, quando tipos de dados precisam de formatação especial que o modelo de exibição não oferece.

Exponha modos operacionais no modelo de exibição

O modelo de exibição também pode ser responsável por definir alterações de estado lógico que afetam alguns aspectos da apresentação na exibição, como uma indicação de que uma operação está pendente ou se determinado comando está disponível. Você não precisa de code-behind para habilitar e desabilitar elementos da interface do usuário. Isso pode ser feito através da vinculação a uma propriedade do modelo de exibição.

Veja um exemplo a seguir.


<Grid Background="{Binding HasPhotos, Converter={StaticResource BrushConverter}}"
      Height="150"
      IsTapEnabled="{Binding HasPhotos}"
      PointerEntered="OnZoomedOutGridPointerEntered"
      Margin="0"
      Width="150">


No exemplo, o atributo IsTapEnabled é vinculado à propriedade HasPhoto do modelo de exibição.

Garanta que os modelos de exibição tenham o atributo Bindable

Para que o modelo de exibição participe da vinculação de dados com a exibição, as classes do modelo de exibição devem ter o atributo Windows::UI::Xaml::Data::Bindable para garantir que o tipo seja incluído no arquivo gerado do XAML.

Além disso, também é necessário incluir o cabeçalho do seu modelo de exibição no arquivo de cabeçalho App.xaml.h, direta ou indiretamente. No Hilo, todos os arquivos de cabeçalho dos modelos de exibição são incluídos no arquivo ViewModelLocator.h, que é incluído no arquivo App.xaml.h. Isso garante que os tipos necessários para funcionar com XAML sejam devidamente gerados no momento da compilação.

Observação  Para saber mais sobre a extensão de linguagem de atributos do C++/CX, veja Atributos definidos pelo usuário (C++/CX).

Veja a seguir um exemplo do atributo Bindable.


[Windows::UI::Xaml::Data::Bindable] 
[Windows::Foundation::Metadata::WebHostHiddenAttribute]
public ref class MainHubViewModel sealed : public ViewModelBase 
{ 
  // ...  
} 	  

Garanta que os modelos de exibição implementem a interface INotifyProperyChanged para que a vinculação de dados funcione

Os modelos de exibição que precisam notificar os clientes que um valor de propriedade foi alterado devem acionar o evento PropertyChanged. Para fazer isso, as classes do modelo de exibição precisam implementar a interface Windows::UI::Xaml::Data::INotifyPropertyChanged. O Tempo de Execução do Windows registra um manipulador para esse evento quando o aplicativo é executado. O Visual Studio fornece uma implementação da interface INotifyPropertyChanged na classe de modelo BindableBase que você pode usar como uma classe base para qualquer fonte de dados XAML. Veja a seguir o arquivo de cabeçalho gerado de BindableBase.h:



[Windows::Foundation::Metadata::WebHostHidden]
public ref class BindableBase : Windows::UI::Xaml::DependencyObject, Windows::UI::Xaml::Data::INotifyPropertyChanged, Windows::UI::Xaml::Data::ICustomPropertyProvider
{
  public:
    virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged;

   // ...

  protected:
    virtual void OnPropertyChanged(Platform::String^ propertyName);
};	  

A implementação gerada invoca o manipulador quando o evento é acionado.


void BindableBase::OnPropertyChanged(String^ propertyName)
{
    PropertyChanged(this, ref new PropertyChangedEventArgs(propertyName));
}	  

Observação  O C++/CX tem eventos e propriedades como parte da linguagem de programação. Ele inclui as palavras-chave de propriedade e evento. Os tipos do Tempo de Execução do Windows declaram eventos em suas interfaces públicas que o seu aplicativo pode assinar. O assinante executa ações personalizadas quando o publicador aciona o evento. Para saber mais sobre os recursos do C++/CX que oferecem suporte ao Tempo de Execução do Windows, veja Criando componentes do Tempo de Execução do Windows em C++. Para sabre mais sobre a extensão de linguagem de eventos do C++/CX que é usada neste código de exemplo, veja Eventos (C++/CX).

As classes do modelo de exibição podem herdar a implementação INotifyPropertyChanged através da derivação da classe BindableBase. Por exemplo, veja a seguir a declaração da classe ViewModelBase no Hilo. O código é proveniente do arquivo ViewModelBase.h.


public ref class ViewModelBase : public Common::BindableBase
{
  // ...
}	  

Sempre que os modelos de exibição precisarem informar à interface do usuário que uma propriedade vinculada foi alterada, eles chamarão o método OnPropertyChanged que herdaram da classe BindableBase. Por exemplo, veja a seguir um método definido por propriedade especificado na classe RotateImageViewModel.


void RotateImageViewModel::RotationAngle::set(float64 value)
{
    m_rotationAngle = value;

    // Derive margin so that rotated image is always fully shown on screen.
    Thickness margin(0.0);
    switch (safe_cast<unsigned int>(m_rotationAngle))
    {
    case 90:
    case 270:
        margin.Top = 110.0;
        margin.Bottom = 110.0;
        break;
    }
    m_imageMargin = margin;
    OnPropertyChanged("ImageMargin");
    OnPropertyChanged("RotationAngle");
}


Observação  As notificações de propriedade para o XAML devem ocorrer no thread da interface do usuário. Isso significa que o método OnPropertyChanged e qualquer um de seus chamadores também devem ocorrer no thread da interface do usuário do aplicativo. Em geral, uma convenção útil é que todos os métodos e propriedades do modelo de exibição devem ser chamados no thread da interface do usuário do aplicativo.

Mantenha as exibições e os modelos de exibição independentes

Se você seguir os princípios descritos neste artigo, poderá reimplementar um modelo de exibição sem alterar a exibição. A vinculação de exibições a determinada propriedade em sua fonte de dados deve ser a principal dependência de uma exibição de seu modelo de exibição correspondente. Se você renomear a propriedade vinculada no modelo de exibição, também precisará renomeá-la na expressão de vinculação de dados XAML.

Use técnicas de programação assíncrona para manter a interface do usuário rápida

Os aplicativos da Windows Store devem oferecer uma experiência do usuário rápida e direta. Por esse motivo, o Hilo mantém o thread da interface do usuário desbloqueado. O Hilo usa métodos de biblioteca assíncronos para operações de entrada/saída e tarefas paralelas quando as operações executam uma quantidade significativa de processamento. O Hilo aciona eventos para notificar de maneira assíncrona a exibição sobre uma alteração de propriedade.

Para saber mais, veja Programação assíncrona para aplicativos da Windows Store em C++ e XAML.

Sempre respeite as regras de threading dos objetos do Tempo de Execução do Windows

Os objetos que são criados por chamadas feitas para o Tempo de Execução do Windows às vezes são de um único thread. Isso significa que você deve invocar os métodos, propriedades e manipuladores de eventos do mesmo contexto de thread que foi usado para criar o objeto. Na maioria dos casos, o contexto é o thread da interface do usuário do aplicativo.

Para evitar erros, o Hilo foi projetado de modo que chamadas feitas para seus modelos de exibição ocorram no thread da interface do usuário do aplicativo. As classes do modelo executam operações demoradas, como processamento de imagens, em threads de trabalho.

Para saber mais, veja Padrões de programação para interface do usuário assíncrona. Veja Controlando o thread de execução para saber mais sobre o modelo de threading usado pelos aplicativos da Windows Store.

Para obter um guia passo a passo sobre como o Hilo usa a programação assíncrona, veja Programação assíncrona para aplicativos da Windows Store em C++ e XAML.

[Início]

 

 

Mostrar:
© 2015 Microsoft