Olá pessoal!
Por várias razões, a maioria de nós usa aplicativos de área de trabalho, pelo menos um navegador :) E alguns de nós precisam escrever nossos próprios. Neste artigo, desejo examinar o processo de desenvolvimento de um aplicativo de desktop simples usando a tecnologia Windows Presentation Foundation (WPF) e aplicando o padrão MVVM. Aqueles que desejam continuar lendo, por favor, abaixo do gato.
Acho que não é necessário dizer que o WPF é o desenvolvimento da Microsoft :) Essa tecnologia foi projetada para desenvolver aplicativos de desktop para Windows, começando com o Windows XP. Porque assim? Isso ocorre pelo fato de o WPF ser executado na plataforma .NET, cujos requisitos mínimos são o Windows XP e posterior. Infelizmente, o WPF não funciona em outras plataformas, embora haja chances de que isso mude no futuro próximo: a estrutura Avalonia baseada no WPF está em desenvolvimento .
O que há de especial no WPF?
Duas principais diferenças entre o WPF e outras ferramentas de criação de desktops:
- A linguagem de marcação XAML para marcar a própria interface da janela.
- Renderização através do DirectX, aceleração gráfica de hardware.
Eu não vou entrar em detalhes, porque este não é exatamente o tópico do artigo. Se estiver interessado, pesquise no Google XAML, renderização WPF, milcore.dll e DirectX :)
Sobre o que é este artigo?
Este artigo contém um aplicativo de exemplo criado na tecnologia WPF:
Tentarei orientar o material do artigo em uma direção prática, no estilo de "repita comigo", com explicações.
O que precisamos para repetir o artigo?
Pouca experiência em desenvolvimento em C # :) No mínimo, você precisa entender bem a sintaxe da linguagem. Você também precisará de uma máquina Windows (Win 10 nos exemplos) com o Visual Studio instalado (nos exemplos em 2017, há uma versão gratuita da Comunidade ). Ao instalar o VS, você precisará ativar o suporte ao desenvolvimento de área de trabalho para a plataforma .NET

Também nesta seção, descreverei a criação de um projeto.
Lançamos o VS, criamos um novo projeto, selecionamos o tipo de aplicativo WPF App (.NET Framework) (você pode inseri-lo na barra de pesquisa no canto superior direito) e nomeie o que for.

Depois de criar um novo projeto, a janela do editor de interface é aberta, fica assim para mim

Na parte inferior, há um editor de layout; na parte superior, há uma visualização da interface da janela, mas você pode alterar a localização relativa do editor de código e a visualização da interface para que eles fiquem na ordem horizontal usando esses botões (à direita da borda das duas áreas):

Antes de começar
Os elementos da janela (também chamados de controles da palavra Controle ) devem ser colocados dentro do contêiner ou dentro de outro elemento do tipo ContentControl. Um contêiner é um controle especial que permite colocar vários controles filho dentro e organizar seu arranjo mútuo. Exemplos de recipientes:
- Grade - permite organizar elementos por colunas e linhas, a largura de cada coluna ou linha é configurada individualmente.
- StackPanel - permite organizar filhos em uma única linha ou coluna.
Existem outros recipientes. Como o contêiner também é um controle, dentro dele, pode haver contêineres aninhados contendo contêineres aninhados e assim por diante. Isso permite que você organize de maneira flexível os controles entre si. Além disso, com a ajuda de contêineres, não podemos controlar com menos flexibilidade o comportamento de controles aninhados ao redimensionar uma janela.
Interface MVVM e INotifyPropertyChanged. Cópia do texto.
O resultado deste exemplo será um aplicativo com dois controles, em um dos quais você pode editar o texto e na outra apenas visualização. Alterações de uma para outra serão transferidas de forma síncrona sem cópia explícita do texto usando a ligação .
Portanto, temos um projeto recém-criado ( chamei -o de Ex1 ), vá para o editor de layout e primeiro substitua o contêiner padrão ( <Grid> </Grid> ) por <StackPanel> </StackPanel> . Este contêiner será suficiente, porque precisaremos colocar apenas dois controles um acima do outro. Especificamos explicitamente como os componentes serão organizados adicionando a propriedade Orientation = "Vertical" . Adicione alguns elementos dentro da pilha do painel: um campo para inserir texto e um campo para exibir texto. Como esses controles não contêm código incorporado, você pode descrevê-los com uma tag de fechamento automático (consulte o código abaixo). Após todos os procedimentos acima, o código de descrição do contêiner e os controles aninhados devem assumir o seguinte formato:
<StackPanel Orientation="Vertical"> <TextBox /> <TextBlock /> </StackPanel>
Agora vamos nos concentrar no objetivo deste exemplo. Queremos que, ao digitar na caixa de texto, o mesmo texto seja exibido de forma síncrona no bloco de texto, evitando a operação de cópia explícita. Precisamos de algum tipo de entidade conectora, e aqui chegamos a algo vinculativo , que foi mencionado acima. A ligação na terminologia WPF é um mecanismo que permite associar algumas propriedades dos controles a algumas propriedades de um objeto da classe C # e atualizar mutuamente essas propriedades quando uma das partes do pacote é alterada (isso pode funcionar em uma, na outra ou nas duas direções ao mesmo tempo). Para aqueles que estão familiarizados com o Qt, você pode desenhar uma analogia de slots e sinais. Para não esticar o tempo, vamos para o código.
Portanto, para organizar a ligação, você precisa das propriedades dos controles e de alguma propriedade de uma determinada classe C #. Primeiro, vamos descobrir o código XAML. O texto dos dois controles é armazenado na propriedade Text, portanto, adicione uma ligação para essas propriedades. É feito assim:
<TextBox Text="{Binding}"/> <TextBlock Text="{Binding}"/>
Criamos uma ligação, mas, por enquanto, não está claro o porquê :) Precisamos de um objeto de alguma classe e alguma propriedade nesse objeto à qual a ligação será feita (como eles dizem, à qual você deve se ligar).
Então, o que é essa aula? Essa classe é chamada de modelo de visualização e serve como um link entre a visualização (a interface ou suas partes) e o modelo (modelo, ou seja, as partes do código responsáveis pela lógica do aplicativo. Isso permite separar (até certo ponto) ) a lógica do aplicativo a partir da interface (exibição, exibição) é denominada padrão MVVM (Model-View-ViewModel) .No WPF, essa classe também é chamada DataContext .
No entanto, apenas escrever uma classe de modelo de exibição não é suficiente. É necessário informar de alguma forma o mecanismo de ligação que a propriedade do modelo de exibição ou a propriedade de exibição foi alterada. Para fazer isso, existe uma interface especial INotifyPropertyChanged , que contém o evento PropertyChanged . Implementamos essa interface na estrutura da classe base BaseViewModel . No futuro, herdaremos todos os nossos modelos de exibição dessa classe base para não duplicar a implementação da interface. Portanto, adicione o diretório ViewModels ao projeto e adicione o arquivo BaseViewModel.cs a esse diretório. Temos a seguinte estrutura de projeto:

Código de implementação para o modelo de vista base:
using System.ComponentModel; namespace Ex1.ViewModels { public class BaseViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } }
Vamos criar nosso viewmodel para nossa classe MainWindow , herdando da classe base. Para fazer isso, no mesmo diretório ViewModels , crie o arquivo MainWindowViewModel.cs , dentro do qual haverá esse código:
namespace Ex1.ViewModels { public class MainWindowViewModel : BaseViewModel { } }
Ótimo! Agora precisamos adicionar uma propriedade a esse modelo de exibição, na qual vincularemos o texto de nossos controles. Como este é um texto, o tipo dessa propriedade deve ser string :
public string SynchronizedText { get; set; }
Como resultado, obtemos esse código
namespace Ex1.ViewModels { public class MainWindowViewModel : BaseViewModel { public string SynchronizedText { get; set; } } }
Então, ao que parece, eles fizeram isso. Resta vincular essa propriedade a partir da visualização e está pronto. Vamos fazer agora:
<TextBox Text="{Binding Path=SynchronizedText}"/> <TextBlock Text="{Binding Path=SynchronizedText}"/>
Nishtyak, começamos o projeto, digitamos na caixa de texto iiiii ... nada acontece))) Bem, tudo bem, na verdade, estamos indo no caminho certo, apenas não chegamos ao ponto certo.
Proponho parar por um momento e pensar no que estamos perdendo. Nós temos uma visão. Viewmodel também. Propriedades como zabindili. A interface desejada é implementada. Fizemos muito trabalho para copiar uma linha de texto patética, por que precisamos disso ???!?! 111
Ok, brincadeiras à parte. Esquecemos de criar um objeto de modelo de exibição e outra coisa (mais sobre isso mais tarde). Descrevemos a própria classe, mas isso não significa nada, porque não temos objetos dessa classe. Ok, onde você precisa armazenar um link para esse objeto? Mais perto do início do exemplo, mencionei um certo DataContext usado no WPF. Portanto, qualquer visualização tem uma propriedade DataContext à qual podemos atribuir um link ao nosso modelo de visualização. Vamos fazer isso. Para fazer isso, abra o arquivo MainWindow.xaml e pressione F7 para abrir o código para esta exibição. Está quase vazio, apenas possui um construtor de classe de janela. Adicione a criação do nosso modelo de exibição a ele e coloque-o no DataContext da janela (não se esqueça de adicionar usando o espaço de nome desejado):
public MainWindow() { InitializeComponent(); this.DataContext = new MainWindowViewModel(); }
Era simples, mas ainda não o suficiente. Ainda assim, quando o aplicativo inicia, nenhuma sincronização de texto ocorre. O que mais precisa ser feito?
Você precisa aumentar o evento PropertyChanged quando a propriedade SynchronizedText for alterada e informar à exibição que ele deve monitorar esse evento. Portanto, para acionar o evento, modifique o código do modelo de exibição:
public class MainWindowViewModel : BaseViewModel { private string _synchronizedText; public string SynchronizedText { get => _synchronizedText; set { _synchronizedText = value; OnPropertyChanged(nameof(SynchronizedText)); } } }
O que fizemos aqui? Adicionamos um campo oculto para armazenar texto, agrupamos-o em uma propriedade existente e, ao alterar essa propriedade, não apenas alteramos o campo oculto, mas também chamamos o método OnPropertyChanged definido no modelo de vista base e aumentamos o evento PropertyChanged declarado na interface INotifyPropertyChanged , também implementada na base ver modelos. Acontece que, toda vez que o texto é alterado, ocorre o evento PropertyChanged , para o qual o nome da propriedade do modelo de exibição que foi alterado é passado.
Bem, quase tudo, a linha de chegada! Resta especificar a exibição que deve ouvir o evento PropertyChanged :
<TextBox Text="{Binding Path=SynchronizedText, UpdateSourceTrigger=PropertyChanged, Mode=OneWayToSource}"/> <TextBlock Text="{Binding Path=SynchronizedText, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"/>
Além do fato de indicarmos por qual acionador a atualização deve ocorrer, também indicamos em que direção essa atualização está sendo rastreada: de vista para ver modelo ou vice-versa. Como inserimos texto na caixa de texto, estamos interessados apenas nas alterações na exibição; portanto, selecionamos o modo OneWayToSource . No caso do bloco de texto, tudo é exatamente o oposto: estamos interessados em alterações no modelo de exibição para exibi-las na exibição, portanto, selecionamos o modo OneWay . Se quiséssemos rastrear as alterações nas duas direções, não poderíamos especificar o Modo , nem especificar o TwoWay explicitamente.
Então, execute o programa, digite o texto e voi-la! O texto muda de forma síncrona e não copiamos nada em lugar nenhum!

Obrigado por sua atenção, para continuar. Lidaremos com o DataTemplate e o padrão de comando.