Tutorial: Criar vinculações de dados
Suponhamos que você tenha projetado e implementado uma interface do usuário de boa aparência, repleta de imagens de espaço reservado, texto clichê "lorem ipsum" e controles que ainda não fazem nada. Em seguida, você a conectará a dados reais, e ela deixará de ser um protótipo de design para se transformar em um aplicativo dinâmico.
Neste tutorial, você aprenderá a substituir seu clichê por vinculações de dados e criar outros links diretos entre a interface do usuário e os dados. Você também verá como formatar ou converter seus dados para exibição, e como manter a interface do usuário e os dados sincronizados. Ao concluir este tutorial, você saberá como melhorar a simplicidade e a organização do XAML e do código C#, tornando-os mais fáceis de manter e estender.
Você iniciará com uma versão simplificada do exemplo do PhotoLab. Esta versão de iniciante inclui a camada de dados completa, além dos layouts de página XAML básicos, e deixa de fora muitos recursos para facilitar a navegação do código. Este tutorial não abrange o aplicativo completo; portanto, confira a versão final para ver recursos como animações personalizadas e layouts adaptáveis. Você pode encontrar a versão final na pasta raiz do repositório Windows-appsample-photo-lab.
O aplicativo de exemplo PhotoLab tem duas páginas. A página principal mostra uma exibição de galeria de fotos, juntamente com algumas informações sobre cada arquivo de imagem.
A página de detalhes exibe uma única foto após ela ter sido selecionada. Um menu de edição de submenu permite que a foto seja alterada, renomeada e salva.
Pré-requisitos
- Visual Studio 2019 ou mais recente: Baixar o Visual Studio (a edição Community é gratuita).
- SDK do Windows (10.0.17763.0 ou mais recente): Baixar o SDK do Windows mais recente (gratuito)
- Windows 10, versão 1809 ou posterior
Parte 0: Obter o código inicial do GitHub
Para este tutorial, você iniciará com uma versão simplificada da amostra do PhotoLab.
Acesse a página do GitHub para ver a amostra: https://github.com/Microsoft/Windows-appsample-photo-lab.
Em seguida, você precisará clonar ou baixar a amostra. Clique no botão Clonar ou baixar. Um submenu será exibido.
Se você não estiver familiarizado com o GitHub:
a. Clique em Baixar ZIP e salve o arquivo localmente. Isso baixa um arquivo .zip que contém todos os arquivos de projeto de que você precisa.
b. Extraia o arquivo. Use o Explorador de Arquivos para procurar o arquivo .zip recém-baixado, clique com o botão direito do mouse nele e selecione Extrair Tudo... .
c. Navegue até a cópia local da amostra e acesse o diretório
Windows-appsample-photo-lab-master\xaml-basics-starting-points\data-binding
.Se você estiver familiarizado com o GitHub:
a. Clone o branch principal do repositório localmente.
b. Navegue até o diretório
Windows-appsample-photo-lab\xaml-basics-starting-points\data-binding
.Clique duas vezes em
Photolab.sln
para abrir a solução no Visual Studio.
Parte 1: Substituir os espaços reservados
Aqui, você criará associações únicas no XAML do modelo de dados para exibir imagens reais e metadados das imagens, em vez do conteúdo de espaço reservado.
As vinculações únicas destinam-se a dados somente leitura e inalteráveis, o que significa que eles são de alto desempenho e fáceis de criar, permitindo que você exiba grandes conjuntos de dados grandes nos controles GridView
e ListView
.
Substitua os espaços reservados por vinculações únicas
Abra a pasta
xaml-basics-starting-points\data-binding
e inicie o arquivoPhotoLab.sln
no Visual Studio.Verifique se a Plataforma de Solução está definida como x86 ou x64, não como ARM, e execute o aplicativo. Isso mostra o estado do aplicativo com espaços reservados de interface do usuário, antes que as vinculações tenham sido adicionadas.
Abra o MainPage.xaml e procure um
DataTemplate
chamado ImageGridView_DefaultItemTemplate. Você atualizará esse modelo para usar vinculações de dados.Antes:
<DataTemplate x:Key="ImageGridView_DefaultItemTemplate">
O valor
x:Key
é usado peloImageGridView
para selecionar esse modelo para exibição de objetos de dados.Adicione um valor
x:DataType
ao modelo.Depois:
<DataTemplate x:Key="ImageGridView_DefaultItemTemplate" x:DataType="local:ImageFileInfo">
x:DataType
indica a qual tipo este um modelo se destina. Nesse caso, trata-se de um modelo para a classeImageFileInfo
(em quelocal:
indica o namespace local, conforme definido em uma declaração xmlns próximo à parte superior do arquivo).x:DataType
é necessário ao usar expressõesx:Bind
em um modelo de dados, conforme descrito a seguir.No
DataTemplate
, localize o elementoImage
chamadoItemImage
e substitua seu valorSource
conforme mostrado.Antes:
<Image x:Name="ItemImage" Source="/Assets/StoreLogo.png" Stretch="Uniform" />
Depois:
<Image x:Name="ItemImage" Source="{x:Bind ImageSource}" Stretch="Uniform" />
x:Name
identifica um elemento XAML para que você possa consultá-lo em outro lugar no XAML e no code-behind.A expressões
x:Bind
fornecem um valor a uma propriedade de interface do usuário, obtendo o valor de uma propriedade data-object. Nos modelos, a propriedade indicada destina-se a qualquer valor para o qualx:DataType
tenha sido definido. Portanto, nesse caso, a fonte de dados é a propriedadeImageFileInfo.ImageSource
.Observação
O valor
x:Bind
também permite ao editor saber sobre o tipo de dados, para que você possa usar o IntelliSense em vez de digitar o nome da propriedade em uma expressãox:Bind
. Experimente-o no código que você acabou de colar: coloque o cursor logo apósx:Bind
e pressione a barra de espaço para ver uma lista de propriedades às quais é possível se vincular.Substitua os valores dos outros controles de interface do usuário da mesma maneira. (Tente fazer isso com o IntelliSense em vez de copiar/colar!)
Antes:
<TextBlock Text="Placeholder" ... /> <StackPanel ... > <TextBlock Text="PNG file" ... /> <TextBlock Text="50 x 50" ... /> </StackPanel> <muxc:RatingControl Value="3" ... />
Depois:
<TextBlock Text="{x:Bind ImageTitle}" ... /> <StackPanel ... > <TextBlock Text="{x:Bind ImageFileType}" ... /> <TextBlock Text="{x:Bind ImageDimensions}" ... /> </StackPanel> <muxc:RatingControl Value="{x:Bind ImageRating}" ... />
Execute o aplicativo para ver sua aparência até agora. Não há mais espaços reservados! Começamos bem!
Observação
Se você quiser fazer mais testes, tente adicionar um novo TextBlock ao modelo de dados e use o truque x:Bind IntelliSense para localizar uma propriedade a ser exibida.
Parte 2: usar a vinculação para conectar a interface do usuário da galeria às imagens
Aqui, você criará vinculações únicas na página XAML para conectar a exibição da galeria à coleção de imagens, substituindo o código de procedimento existente que faz isso no code-behind. Você também criará um botão Excluir para ver como a exibição da galeria muda quando as imagens são removidas da coleção. Ao mesmo tempo, você aprenderá a vincular eventos a manipuladores de eventos para obter mais flexibilidade do que a fornecida pelos manipuladores de eventos tradicionais.
Todas as vinculações abordadas até agora estão dentro de modelos de dados e referem-se a propriedades da classe indicada pelo valor x:DataType
. E o restante do XAML na página?
As expressões x:Bind
fora dos modelos de dados sempre estão vinculadas à página. Isso significa que você pode fazer referência a qualquer item colocado no code-behind ou declarado no XAML, incluindo propriedades personalizadas e propriedades de outros controles de interface do usuário na página (contanto que elas tenham um valor x:Name
).
No exemplo do PhotoLab, uma das funções de uma vinculação como esta é conectar o controle GridView
principal diretamente à coleção de imagens, em vez de fazê-lo no code-behind. Mais tarde, você verá outros exemplos.
Vincular o controle GridView principal à coleção de imagens
No MainPage.xaml.cs, localize o método
GetItemsAsync
e remova o código que defineItemsSource
.Antes:
ImageGridView.ItemsSource = Images;
Depois:
// Replaced with XAML binding: // ImageGridView.ItemsSource = Images;
No MainPage.xaml, localize o
GridView
chamadoImageGridView
e adicione um atributoItemsSource
. Como valor, use uma expressãox:Bind
que faça referência à propriedadeImages
implementada no code-behind.Antes:
<GridView x:Name="ImageGridView"
Depois:
<GridView x:Name="ImageGridView" ItemsSource="{x:Bind Images}"
A propriedade
Images
é do tipoObservableCollection<ImageFileInfo>
e, portanto, os itens individuais exibidos emGridView
são do tipoImageFileInfo
. Isso corresponde ao valorx:DataType
descrito na Parte 1.
Todas as vinculações que examinamos até agora são únicas e somente leitura, que é o comportamento padrão das expressões x:Bind
sem formatação. Os dados são carregados apenas na inicialização, o que torna as vinculações de alto desempenho perfeito para suporte a várias exibições complexas de grandes conjuntos de dados.
Até mesmo a vinculação ItemsSource
que você acabou de adicionar é única e somente leitura para um valor de propriedade inalterável, mas há uma distinção importante a fazer aqui. O valor inalterável da propriedade Images
é uma instância única e específica de uma coleção, inicializada uma vez, conforme mostrado aqui.
private ObservableCollection<ImageFileInfo> Images { get; }
= new ObservableCollection<ImageFileInfo>();
O valor da propriedade Images
nunca muda, mas como a propriedade é do tipo ObservableCollection<T>
, o conteúdo da coleção pode ser alterado, e a associação perceberá automaticamente as alterações e atualizará a interface do usuário.
Para testar isso, vamos adicionar temporariamente um botão que exclua a imagem atualmente selecionada. Esse botão não está na versão final porque a seleção de uma imagem o levará a uma página de detalhes. No entanto, o comportamento de ObservableCollection<T>
ainda é importante no exemplo final do PhotoLab, porque o XAML é inicializado no construtor da página (por meio da chamada de método InitializeComponent
), mas a coleção Images
é preenchida posteriormente no método GetItemsAsync
.
Adicionar um botão Excluir
No MainPage.xaml, localize o
CommandBar
chamado MainCommandBar e adicione um novo botão antes do botão de zoom. (Os controles de zoom ainda não funcionam. Você os conectará na próxima parte do tutorial).<AppBarButton Icon="Delete" Label="Delete selected image" Click="{x:Bind DeleteSelectedImage}" />
Se você já está familiarizado com o XAML, esse valor
Click
pode parecer incomum. Nas versões anteriores do XAML, você tinha que configurar isso para um método com uma assinatura específica de manipulador de eventos, geralmente incluindo parâmetros para o remetente do evento e um objeto de argumento específico de evento. Você ainda poderá usar essa técnica quando precisar de argumentos de evento, mas comx:Bind
, você também poderá se conectar a outros métodos. Por exemplo, se você não precisar dos dados de evento, poderá se conectar a métodos que não tenham parâmetros, como fazemos aqui.No MainPage.xaml.cs, adicione o método
DeleteSelectedImage
.private void DeleteSelectedImage() => Images.Remove(ImageGridView.SelectedItem as ImageFileInfo);
Esse método simplesmente exclui a imagem selecionada da coleção
Images
.
Agora execute o aplicativo e use o botão para apagar algumas imagens. Como você pode ver, a interface do usuário é atualizada automaticamente, graças à associação de dados e ao tipo ObservableCollection<T>
.
Observação
Esse código só exclui a instância de ImageFileInfo
da coleção Images
no aplicativo em execução. Ele não exclui o arquivo de imagem do computador.
Parte 3: Configurar o controle deslizante de zoom
Nesta parte, você criará vinculações unidirecionais de um controle no modelo de dados para o controle deslizante de zoom, que está fora do modelo. Você também aprenderá que pode usar a vinculação de dados com várias propriedades de controle, e não apenas com as mais óbvias, como TextBlock.Text
e Image.Source
.
Vincular o modelo de dados da imagem ao controle deslizante de zoom
Localize o
DataTemplate
chamadoImageGridView_DefaultItemTemplate
e substitua os valores**Height**
eWidth
do controleGrid
na parte superior do modelo.Antes
<DataTemplate x:Key="ImageGridView_DefaultItemTemplate" x:DataType="local:ImageFileInfo"> <Grid Height="200" Width="200" Margin="{StaticResource LargeItemMargin}">
Depois
<DataTemplate x:Key="ImageGridView_DefaultItemTemplate" x:DataType="local:ImageFileInfo"> <Grid Height="{Binding Value, ElementName=ZoomSlider}" Width="{Binding Value, ElementName=ZoomSlider}" Margin="{StaticResource LargeItemMargin}">
Você notou que essas são expressões Binding
, e não expressões x:Bind
? Essa é a maneira antiga de fazer vinculações de dados e, na maioria das vezes, é considerada obsoleta. x:Bind
faz quase tudo que Binding
faz, e muito mais. No entanto, quando você usa x:Bind
em um modelo de dados, ele se vincula ao tipo declarado no valor x:DataType
. Então, como vincular algo no modelo a algo na página XAML ou no code-behind? Você deve usar a antiga expressão Binding
.
As expressões Binding
não reconhecem o valor x:DataType
, mas essas expressões Binding
têm valores ElementName
que funcionam quase da mesma forma. Eles informam ao mecanismo de vinculação que Binding Value é uma vinculação à propriedade Value
do elemento especificado na página (ou seja, o elemento com esse valor x:Name
). Se você deseja vincular-se a uma propriedade no code-behind, a aparência seria algo semelhante a {Binding MyCodeBehindProperty, ElementName=page}
, em que page
representa o valor x:Name
definido no elemento Page
no XAML.
Observação
Por padrão, as expressões Binding
são one-way, o que significa que elas atualizarão automaticamente a interface do usuário quando o valor de propriedade vinculado for alterado.
Em contrapartida, o padrão de x:Bind
é one-time, o que significa que todas as alterações à propriedade vinculada serão ignoradas. Esse é o padrão porque é a opção de mais alto desempenho, e a maioria das vinculações são dados estáticos e somente leitura.
A lição aqui é que, se você usar x:Bind
com propriedades que podem alterar seus valores, não deixe de adicionar Mode=OneWay
ou Mode=TwoWay
. Você verá exemplos disso na próxima seção.
Execute o aplicativo e use o controle deslizante para alterar as dimensões do modelo de imagem. Como você pode ver, o efeito é muito eficaz, sem a necessidade de muito código.
Observação
Se quiser um desafio ainda maior, tente vincular outras propriedades de interface do usuário à propriedade Value
ou a outros controles deslizantes adicionados após o controle deslizante de zoom. Por exemplo, você pode vincular a propriedade FontSize
do TitleTextBlock
a um novo controle deslizante com o valor padrão 24. Não deixe de definir valores mínimos e máximos razoáveis.
Parte 4: Melhorar a experiência de zoom
Nesta parte, você adicionará uma propriedade personalizada ItemSize
ao code-behind e criará vinculações unidirecionais do modelo de imagem à nova propriedade. O valor ItemSize
será atualizado pelo controle deslizante de zoom e outros fatores, como alternância Fit to screen e tamanho de janela, proporcionando uma experiência mais refinada.
Diferente das propriedades de controle internas, as propriedades personalizadas não atualizam automaticamente a interface do usuário, mesmo com vinculações unidirecionais e bidirecionais. Elas funcionam bem com vinculações únicas, mas se você quiser que as alterações de propriedade apareçam na interface do usuário, você precisará executar outras etapas.
Criar a propriedade ItemSize de modo que ela atualize a interface do usuário
No MainPage.xaml.cs, altere a assinatura da classe
MainPage
para que ela implemente a interfaceINotifyPropertyChanged
.Antes:
public sealed partial class MainPage : Page
Depois:
public sealed partial class MainPage : Page, INotifyPropertyChanged
Isso informa o sistema de associação de que
MainPage
tem um eventoPropertyChanged
(adicionado em seguida) que as associações podem escutar para atualizar a interface do usuário.Adicione um evento
PropertyChanged
à classeMainPage
.public event PropertyChangedEventHandler PropertyChanged;
Esse evento fornece a implementação completa exigida pela interface
INotifyPropertyChanged
. No entanto, para que ela surta algum efeito, você deve gerar explicitamente o evento nas propriedades personalizadas.Adicione uma propriedade
ItemSize
e gere o eventoPropertyChanged
no setter.public double ItemSize { get => _itemSize; set { if (_itemSize != value) { _itemSize = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ItemSize))); } } } private double _itemSize;
A propriedade
ItemSize
expõe o valor de um campo privado_itemSize
. O uso de um campo de apoio como esse permite que a propriedade verifique se um novo valor é igual ao valor antigo antes de gerar um eventoPropertyChanged
potencialmente desnecessário.O próprio evento é gerado pelo método
Invoke
. O ponto de interrogação verifica se o eventoPropertyChanged
é nulo, ou seja, se algum manipulador de eventos já foi adicionado. Cada vinculação unidirecional ou bidirecional adiciona um manipulador de eventos nos bastidores, mas se nenhuma vinculação estiver realizando a escuta, nada mais acontecerá aqui. No entanto, sePropertyChanged
não for nulo,Invoke
será chamado com uma referência à origem do evento (a própria página, representada pela palavra-chavethis
) e a um objeto event-args que indica o nome da propriedade. Com essas informações, todas as vinculações unidirecionais ou bidirecionais à propriedadeItemSize
serão informadas sobre quaisquer alterações para que possam atualizar a interface do usuário vinculada.No MainPage.xaml, localize o
DataTemplate
chamadoImageGridView_DefaultItemTemplate
e substitua os valoresHeight
eWidth
do controleGrid
na parte superior do modelo. (Se você fez a associação entre controles na parte anterior deste tutorial, as únicas alterações serão substituirValue
porItemSize
eZoomSlider
porpage
. Não se esqueça de fazer isso paraHeight
eWidth
).Antes
<DataTemplate x:Key="ImageGridView_DefaultItemTemplate" x:DataType="local:ImageFileInfo"> <Grid Height="{Binding Value, ElementName=ZoomSlider}" Width="{Binding Value, ElementName=ZoomSlider}" Margin="{StaticResource LargeItemMargin}">
Depois
<DataTemplate x:Key="ImageGridView_DefaultItemTemplate" x:DataType="local:ImageFileInfo"> <Grid Height="{Binding ItemSize, ElementName=page}" Width="{Binding ItemSize, ElementName=page}" Margin="{StaticResource LargeItemMargin}">
Agora que a interface do usuário pode responder às alterações de ItemSize
, você precisa fazer algumas alterações. Como mencionado anteriormente, o valor ItemSize
é calculado a partir do estado atual de vários controles de interface do usuário, mas o cálculo deve ser executado sempre que esses controles mudarem de estado. Para fazer isso, você usará a vinculação de eventos para que determinadas alterações de interface do usuário chamem um método auxiliar que atualize ItemSize
.
Atualizar o valor da propriedade ItemSize
Adicione o método
DetermineItemSize
a MainPage.xaml.cs.private void DetermineItemSize() { if (FitScreenToggle != null && FitScreenToggle.IsOn == true && ImageGridView != null && ZoomSlider != null) { // The 'margins' value represents the total of the margins around the // image in the grid item. 8 from the ItemTemplate root grid + 8 from // the ItemContainerStyle * (Right + Left). If those values change, // this value needs to be updated to match. int margins = (int)this.Resources["LargeItemMarginValue"] * 4; double gridWidth = ImageGridView.ActualWidth - (int)this.Resources["DefaultWindowSidePaddingValue"]; double ItemWidth = ZoomSlider.Value + margins; // We need at least 1 column. int columns = (int)Math.Max(gridWidth / ItemWidth, 1); // Adjust the available grid width to account for margins around each item. double adjustedGridWidth = gridWidth - (columns * margins); ItemSize = (adjustedGridWidth / columns); } else { ItemSize = ZoomSlider.Value; } }
No MainPage.xaml, navegue até a parte superior do arquivo e adicione um evento
SizeChanged
vinculado ao elementoPage
.Antes:
<Page x:Name="page"
Depois:
<Page x:Name="page" SizeChanged="{x:Bind DetermineItemSize}"
Localize o
Slider
chamadoZoomSlider
(na seçãoPage.Resources
) e adicione uma associação de eventoValueChanged
.Antes:
<Slider x:Name="ZoomSlider"
Depois:
<Slider x:Name="ZoomSlider" ValueChanged="{x:Bind DetermineItemSize}"
Localize o
ToggleSwitch
chamadoFitScreenToggle
e adicione uma vinculação de eventosToggled
.Antes:
<ToggleSwitch x:Name="FitScreenToggle"
Depois:
<ToggleSwitch x:Name="FitScreenToggle" Toggled="{x:Bind DetermineItemSize}"
Execute o aplicativo e use o controle deslizante de zoom e a alternância Ajustar à Tela para alterar as dimensões de modelo de imagem. Como você pode ver, as últimas alterações permitem uma experiência de zoom/redimensionamento mais refinada, mantendo o código bem organizado.
Observação
Se quiser um desafio ainda maior, tente adicionar um TextBlock
após o ZoomSlider
e vincular a propriedade Text
à propriedade ItemSize
. Como não se trata de um modelo de dados, você pode usar x:Bind
, em vez de Binding
, como nas vinculações ItemSize
anteriores.
Parte 5: Habilitar edições do usuário
Aqui, você criará vinculações bidirecionais para permitir aos usuários atualizar valores, incluindo o título da imagem, a classificação e vários efeitos visuais.
Para conseguir isso, você atualizará o DetailPage
existente, que fornece um visualizador de imagem única, controle de zoom e interface do usuário para edição.
No entanto, primeiro você precisa anexar o DetailPage
para que o aplicativo navegue até ele quando o usuário clicar em uma imagem na exibição da galeria.
Anexar o DetailPage
Em MainPage.xaml, localize o
GridView
chamadoImageGridView
. Para tornar os itens clicáveis, definaIsItemClickEnabled
comoTrue
e adicione um manipulador de eventosItemClick
.Dica
Se você digitar a alteração abaixo, em vez de copiá-la ou colá-la, verá um pop-up IntelliSense informando "<New Event Handler>". Se você pressionar a tecla Tab, ela preencherá o valor com um nome de manipulador de método padrão e desligará automaticamente o método mostrado na próxima etapa. Pressione F12 para navegar até o método no code-behind.
Antes:
<GridView x:Name="ImageGridView">
Depois:
<GridView x:Name="ImageGridView" IsItemClickEnabled="True" ItemClick="ImageGridView_ItemClick">
Observação
Estamos usando um manipulador de eventos convencional aqui, em vez de uma expressão x:Bind. Isso porque precisamos ver os dados de evento, como mostrado a seguir.
No MainPage.xaml.cs, adicione o manipulador de eventos (ou preencha-o, se você tiver seguido a dica na última etapa).
private void ImageGridView_ItemClick(object sender, ItemClickEventArgs e) { this.Frame.Navigate(typeof(DetailPage), e.ClickedItem); }
Esse método simplesmente navega até a página de detalhes, passando o item clicado, que é um objeto
ImageFileInfo
usado por DetailPage.OnNavigatedTo para inicializar a página. Você não precisará implementar esse método neste tutorial, mas pode dar uma olhada para ver o que ele faz.(Opcional) Exclua ou comente todos os controles adicionados nos pontos de reprodução anteriores que funcionam com a imagem selecionada. Mantê-los não fará mal nenhum, mas agora é muito mais difícil selecionar uma imagem sem navegar até a página de detalhes.
Agora que você conectou as duas páginas, execute o aplicativo e dê uma olhada. Tudo funciona, exceto os controles no painel de edição, que não respondem quando você tenta alterar os valores.
Como você pode ver, a caixa de texto do título exibe o título e permite que você digite alterações. Você precisará alterar o foco para outro controle para confirmar as alterações, mas o título no canto superior esquerdo da tela ainda não foi atualizado.
Todos os controles já estão vinculados por meio da expressão x:Bind
sem formatação que abordamos na Parte 1. Se você se lembra, isso significará que todas as vinculações são únicas,explicando por que as alterações nos valores não são registradas. Para corrigir isso, tudo o que temos a fazer é transformá-las em vinculações bidirecionais.
Tornar os controles de edição interativos
Em DetailPage.xaml, localize o
TextBlock
chamado TitleTextBlock e o controle RatingControl após ele e atualize as expressõesx:Bind
para que incluam Mode=TwoWay.Antes:
<TextBlock x:Name="TitleTextBlock" Text="{x:Bind item.ImageTitle}" ... > <muxc:RatingControl Value="{x:Bind item.ImageRating}" ... >
Depois:
<TextBlock x:Name="TitleTextBlock" Text="{x:Bind item.ImageTitle, Mode=TwoWay}" ... > <muxc:RatingControl Value="{x:Bind item.ImageRating, Mode=TwoWay}" ... >
Faça o mesmo para todos os controles deslizantes de efeito que acompanham o controle de classificação.
<Slider Header="Exposure" ... Value="{x:Bind item.Exposure, Mode=TwoWay}" ... <Slider Header="Temperature" ... Value="{x:Bind item.Temperature, Mode=TwoWay}" ... <Slider Header="Tint" ... Value="{x:Bind item.Tint, Mode=TwoWay}" ... <Slider Header="Contrast" ... Value="{x:Bind item.Contrast, Mode=TwoWay}" ... <Slider Header="Saturation" ... Value="{x:Bind item.Saturation, Mode=TwoWay}" ... <Slider Header="Blur" ... Value="{x:Bind item.Blur, Mode=TwoWay}" ...
O modo bidirecional, como era de se esperar, significa que os dados são movidos em ambas as direções sempre que há alterações em ambos os lados.
Assim como as vinculações unidirecionais abordadas anteriormente, agora essas vinculações bidirecionais atualizarão a interface do usuário sempre que as propriedades vinculadas forem alteradas, graças à implementação INotifyPropertyChanged
na classe ImageFileInfo
. No entanto, com a vinculação bidirecional, os valores também serão movidos da interface do usuário para as propriedades vinculadas sempre que o usuário interagir com o controle. Nada mais é necessário no lado do XAML.
Execute o aplicativo e teste os controles de edição. Como você pode ver, quando você fizer uma alteração, ela agora afetará os valores de imagem, e essas alterações persistirão quando você retornar à página principal.
Parte 6: Formatar valores por meio da vinculação de função
Um último problema permanece. Quando você move os controles deslizantes de efeito, as etiquetas ao lado deles ainda não são alteradas.
A parte final deste tutorial é adicionar vinculações que formatam os valores do controle deslizante para exibição.
Vincular as etiquetas do controle deslizante de efeito e formatar os valores para exibição
Localize o
TextBlock
após o controle deslizanteExposure
e substitua o valorText
pela expressão de vinculação mostrada aqui.Antes:
<Slider Header="Exposure" ... /> <TextBlock ... Text="0.00" />
Depois:
<Slider Header="Exposure" ... /> <TextBlock ... Text="{x:Bind item.Exposure.ToString('N', culture), Mode=OneWay}" />
Isso é chamado de vinculação de função porque você está se vinculando ao valor de retorno de um método. O método estará acessível através do code-behind da página ou do tipo
x:DataType
se você estiver em um modelo de dados. Nesse caso, ele é o método familiarToString
do .NET, que é acessado através da propriedade de item da página e, depois, através da propriedadeExposure
do item. (Isso ilustra como você pode se vincular a métodos e propriedades que estão profundamente aninhados em uma cadeia de conexões.)A vinculação de função é a maneira ideal de formatar valores para exibição porque você pode passar outras fontes de vinculação como argumentos de método, e a expressão de vinculação fará a escuta das alterações desses valores conforme esperado com o modo unidirecional. Neste exemplo, o argumento culture é uma referência a um campo inalterável implementado no code-behind, mas ele poderia tranquilamente ter sido uma propriedade que gera eventos
PropertyChanged
. Nesse caso, qualquer alteração no valor da propriedade faria com que a expressãox:Bind
chamasseToString
com o novo valor e depois atualizasse a interface do usuário com o resultado.Faça o mesmo para os
TextBlock
s que rotulam os outros controles deslizantes de efeito.<Slider Header="Temperature" ... /> <TextBlock ... Text="{x:Bind item.Temperature.ToString('N', culture), Mode=OneWay}" /> <Slider Header="Tint" ... /> <TextBlock ... Text="{x:Bind item.Tint.ToString('N', culture), Mode=OneWay}" /> <Slider Header="Contrast" ... /> <TextBlock ... Text="{x:Bind item.Contrast.ToString('N', culture), Mode=OneWay}" /> <Slider Header="Saturation" ... /> <TextBlock ... Text="{x:Bind item.Saturation.ToString('N', culture), Mode=OneWay}" /> <Slider Header="Blur" ... /> <TextBlock ... Text="{x:Bind item.Blur.ToString('N', culture), Mode=OneWay}" />
Agora, quando você executa o aplicativo, tudo funciona, inclusive as etiquetas do controle deslizante.
Conclusão
Este tutorial fez uma demonstração da vinculação de dados e mostrou algumas das funcionalidades disponíveis. Um aviso antes de concluir: nem tudo pode ser vinculado e, às vezes, os valores que você tenta conectar são incompatíveis com as propriedades que você está tentando vincular. O processo de vinculação é muito flexível, mas não funcionará em todas as situações.
Um exemplo de um problema não abordado pela vinculação é quando um controle não tem propriedades adequadas às quais vincular, como o recurso de zoom da página de detalhes. Esse controle deslizante de zoom precisará interagir com o ScrollViewer
que exibe a imagem, mas ScrollViewer
só pode ser atualizado por meio do método ChangeView
. Nesse caso, usamos manipuladores de eventos convencionais para manter o ScrollViewer
e o controle deslizante de zoom em sincronia; confira os métodos ZoomSlider_ValueChanged
e MainImageScroll_ViewChanged
em DetailPage
para obter detalhes.
No entanto, a vinculação é uma maneira eficaz e flexível de simplificar o código e manter a lógica de interface do usuário separada da lógica de dados. Isso facilitará muito o ajuste de ambos os lados, reduzindo o risco de inserção de bugs no outro lado.
Um exemplo de interface do usuário e separação de dados ocorre na propriedade ImageFileInfo.ImageTitle
. Essa propriedade (e a propriedade ImageRating
) é um pouco diferente da propriedade ItemSize
que você criou na Parte 4 porque o valor é armazenado nos metadados do arquivo (exposto através do tipo ImageProperties
), e não em um campo. Além disso, ImageTitle
retorna o valor ImageName
(definido para o nome do arquivo) se não houver título nos metadados do arquivo.
public string ImageTitle
{
get => String.IsNullOrEmpty(ImageProperties.Title) ? ImageName : ImageProperties.Title;
set
{
if (ImageProperties.Title != value)
{
ImageProperties.Title = value;
var ignoreResult = ImageProperties.SavePropertiesAsync();
OnPropertyChanged();
}
}
}
Como você pode ver, o setter atualiza a propriedade ImageProperties.Title
e, em seguida, chama SavePropertiesAsync
para gravar o novo valor no arquivo. (Esse é um método assíncrono, mas não podemos usar a palavra-chave await
em uma propriedade – e você não desejaria isso porque getters e setters de propriedade devem ser concluídos imediatamente. Em vez disso, você deve chamar o método e ignorar o objeto Task
que ele retorna).
Aprofundamento
Agora que você concluiu este laboratório, tem conhecimentos de vinculação suficientes para enfrentar um problema por conta própria.
Como você deve ter notado, se alterar o nível de zoom na página de detalhes, ele será redefinido automaticamente quando você navegar para trás e selecionar a mesma imagem novamente. Você consegue descobrir como preservar e restaurar o nível de zoom em cada imagem? Boa sorte!
Você tem todas as informações necessárias neste tutorial, mas, se precisar de mais orientação, os documentos de vinculação de dados estão a apenas um clique de distância. Inicie aqui: