Partilhar via


Criar associações de dados

Se você criou uma interface do usuário com imagens de espaço reservado e texto clichê, este tutorial mostra como conectá-la a dados reais usando associações de dados. Aprenda a formatar dados, manter a interface do usuário e os dados sincronizados e melhorar a capacidade de manutenção do código.

Neste tutorial, você aprenderá como substituir seu clichê por associações de dados e criar outros links diretos entre sua interface do usuário e seus dados. Você também aprende a formatar ou converter seus dados para exibição e manter sua interface do usuário e dados sincronizados. Ao concluir este tutorial, você pode melhorar a simplicidade e a organização do código XAML e C#, facilitando a manutenção e a extensão.

Você começa com uma versão simplificada do exemplo PhotoLab. Esta versão inicial inclui a camada de dados completa mais os layouts de página XAML básicos e deixa de fora muitos recursos para facilitar a navegação no código. Este tutorial não se baseia no aplicativo completo, portanto, certifique-se de verificar 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 exibe uma exibição de galeria de fotos, juntamente com algumas informações sobre cada arquivo de imagem.

Captura de tela da página principal do aplicativo PhotoLab mostrando uma exibição de galeria de fotos com metadados de imagem.

A página de detalhes exibe uma única foto depois de selecioná-la. Um menu de edição expansível permite alterar, renomear e guardar a foto.

Captura de tela da página de detalhes do aplicativo PhotoLab mostrando uma única foto com opções de edição.

Pré-requisitos

Parte 0: Obter o código inicial do GitHub

Para este tutorial, você começa com uma versão simplificada do exemplo PhotoLab.

  1. Vá para a página do GitHub para obter o exemplo: https://github.com/Microsoft/Windows-appsample-photo-lab.

  2. Em seguida, você precisa clonar ou baixar o exemplo. Selecione o Clone ou baixe botão. É apresentado um submenu. O menu Clone ou download na página GitHub do exemplo do PhotoLab

    Se você não estiver familiarizado com o GitHub:

    a) Selecione Baixar ZIP e salve o arquivo localmente. Esta ação baixa um arquivo de .zip que contém todos os arquivos de projeto que você precisa.

    b) Extraia o ficheiro. Use o Explorador de Arquivos para navegar até o arquivo de .zip que você acabou de baixar, clique com o botão direito do mouse nele e selecione Extrair tudo....

    c. Navegue até a cópia local do exemplo e vá para o diretório Windows-appsample-photo-lab-master\xaml-basics-starting-points\data-binding.

    Se você está familiarizado com o GitHub:

    a) Clonar o ramo principal do repositório localmente.

    b) Navegue até ao diretório Windows-appsample-photo-lab\xaml-basics-starting-points\data-binding.

  3. Clique duas vezes Photolab.sln para abrir a solução no Visual Studio.

Parte 1: Substitua os espaços reservados

Nesta seção, você cria associações únicas em XAML de modelo de dados para exibir imagens reais e metadados de imagem em vez de conteúdo de espaço reservado.

Ligações de utilização única são para dados de leitura única e imutáveis. Eles são de alto desempenho e fáceis de criar, para que possa exibir grandes conjuntos de dados nos GridView e ListView controles.

Substitua os marcadores de posição por associações únicas

  1. Abra a xaml-basics-starting-points\data-binding pasta e inicie o PhotoLab.sln arquivo no Visual Studio.

  2. Verifique se a Plataforma de Solução está definida como x86 ou x64, não Arm, e execute o aplicativo. Esta etapa mostra o estado do aplicativo com espaços reservados para a interface do usuário, antes que as associações sejam adicionadas.

    executar aplicação com imagens e texto de espaço reservado

  3. Abra MainPage.xaml e procure por um DataTemplate com o nome ImageGridView_DefaultItemTemplate. Você atualizará este modelo para usar associações de dados.

    Antes:

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate">
    

    O x:Key valor é usado pelo ImageGridView para selecionar este modelo para exibir objetos de dados.

  4. Adicione um x:DataType valor ao modelo.

    Depois:

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
    

    x:DataType Indica a que tipo este modelo se destina. Nesse caso, é um modelo para a ImageFileInfo classe (onde local: indica o namespace local, conforme definido em uma declaração xmlns perto da parte superior do arquivo).

    Você precisa de x:DataType ao utilizar expressões x:Bind numa definição de dados, conforme descrito a seguir.

  5. No DataTemplate, localize o elemento Image nomeado ItemImage e substitua seu valor Source 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.

    expressões fornecem um valor para uma propriedade da interface de utilizador, obtendo o valor de uma propriedade do objeto de dados . Em modelos, a propriedade indicada é uma propriedade do que o x:DataType estiver configurado para. Portanto, neste caso, a fonte de dados é a ImageFileInfo.ImageSource propriedade.

    Observação

    O x:Bind valor também permite que o editor saiba sobre o tipo de dados, para que você possa usar o IntelliSense em vez de digitar o nome da propriedade em uma x:Bind expressão. Experimente no código que acabou de colar: coloque o cursor logo a seguir x:Bind e prima a Barra de espaço para ver uma lista de propriedades às quais pode ligar.

  6. 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}" ... />
    

Corra a aplicação para ver como está até agora. Não há mais espaços reservados! Você está em um bom começo.

Executando o aplicativo com imagens e texto reais em vez de espaços reservados

Observação

Se quiser experimentar mais, tente adicionar um novo TextBlock ao modelo de dados e use o truque x:Bind IntelliSense para encontrar uma propriedade a ser exibida.

Nesta seção, você cria ligações únicas em XAML de página para conectar a vista de galeria à coleção de imagens. Essas ligações substituem o código processual existente no code-behind. Você também cria um botão Excluir para ver como a exibição da galeria muda quando você remove imagens da coleção. Ao mesmo tempo, você aprende a vincular eventos a manipuladores de eventos para obter mais flexibilidade do que os manipuladores de eventos tradicionais.

Todas as associações cobertas até agora estão dentro de modelos de dados e referem-se a propriedades da classe indicada pelo x:DataType valor. E quanto ao resto do XAML na sua página?

x:Bind expressões fora dos modelos de dados sempre se ligam à própria página. Isso significa que é possível fazer referência a qualquer componente colocado no code-behind ou declarado em XAML, incluindo propriedades personalizadas e propriedades de outros controlos de interface do utilizador na página (desde que estes tenham um valor x:Name).

No exemplo PhotoLab, você usa uma ligação como esta para conectar o controle principal GridView diretamente à coleção de imagens, em vez de fazê-lo em code-behind. Mais tarde, você verá outros exemplos.

Vincular o controle GridView principal à coleção Images

  1. No MainPage.xaml.cs, localize o GetItemsAsync método e remova o código que define ItemsSource.

    Antes:

    ImageGridView.ItemsSource = Images;
    

    Depois:

    // Replaced with XAML binding:
    // ImageGridView.ItemsSource = Images;
    
  2. Em MainPage.xaml, encontre o GridView chamado ImageGridView e adicione um atributo ItemsSource. Para o valor, use uma x:Bind expressão que se refira à propriedade implementada Images no code-behind.

    Antes:

    <GridView x:Name="ImageGridView"
    

    Depois:

    <GridView x:Name="ImageGridView"
              ItemsSource="{x:Bind Images}"
    

    A Images propriedade é do tipo ObservableCollection<ImageFileInfo>, portanto, os itens individuais exibidos no GridView são do tipo ImageFileInfo. Este tipo corresponde ao x:DataType valor descrito na Parte 1.

Todas as associações que observaste anteriormente são associações únicas e somente leitura, sendo o comportamento padrão para expressões simples x:Bind. Os dados são carregados apenas na inicialização, o que torna as ligações de alto desempenho, sendo perfeitas para suportar visualizações complexas múltiplas de grandes conjuntos de dados.

Mesmo a ligação ItemsSource que acabou de adicionar é uma ligação pontual, de somente leitura, a um valor de propriedade imutável, mas há uma distinção importante a ser feita aqui. O valor imutável da Images propriedade é 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 Images valor da propriedade nunca muda, mas como a propriedade é do tipo ObservableCollection<T>, o conteúdo da coleção pode ser alterado e a associação percebe automaticamente as alterações e atualiza a interface do usuário.

Para testar esse comportamento, adicione temporariamente um botão que exclui a imagem selecionada no momento. Este botão não está na versão final porque a seleção de uma imagem leva você 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 de página (por meio da chamada do método InitializeComponent), mas a coleção Images é preenchida posteriormente no método GetItemsAsync.

Adicionar um botão de exclusão

  1. Em 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ê vai conectá-los na próxima parte do tutorial.)

    <AppBarButton Icon="Delete"
                  Label="Delete selected image"
                  Click="{x:Bind DeleteSelectedImage}" />
    

    Se você já estiver familiarizado com XAML, esse Click valor pode parecer incomum. Em versões anteriores do XAML, você tinha que definir isso como um método com uma assinatura específica do manipulador de eventos, geralmente incluindo parâmetros para o remetente do evento e um objeto de argumentos específicos do evento. Você ainda pode usar essa técnica quando precisar dos argumentos de evento, mas com x:Bind, você também pode se conectar a outros métodos. Por exemplo, se você não precisar dos dados do evento, poderá se conectar a métodos que não têm parâmetros, como fazemos aqui.

  2. Em MainPage.xaml.cs, adicione o DeleteSelectedImage método.

    private void DeleteSelectedImage() =>
        Images.Remove(ImageGridView.SelectedItem as ImageFileInfo);
    

    Este método simplesmente exclui a imagem selecionada da Images coleção.

Agora execute o aplicativo e use o botão para excluir algumas imagens. Como se pode ver, a interface do utilizador é atualizada automaticamente, graças à associação de dados e ao tipo ObservableCollection<T>.

Observação

Esse código exclui apenas a ImageFileInfo instância da Images coleção 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á ligaçõ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 muitas propriedades de controle, não apenas as mais óbvias, como TextBlock.Text e Image.Source.

Vincular o modelo de dados de imagem ao controle deslizante de zoom

  • Encontre o DataTemplate chamado ImageGridView_DefaultItemTemplate e substitua os valores **Height** e Width do controlo no topo 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}">
    

Reparou que estas são Binding expressões, e não x:Bind expressões? Esta é a maneira antiga de fazer vinculações de dados, e é principalmente obsoleta. x:Bind faz quase tudo o que Binding faz, e muito mais. No entanto, quando você usa x:Bind em um modelo de dados, ele se liga ao tipo declarado no x:DataType valor. Então, como vincular algo no modelo a algo na página XAML ou no code-behind? Você deve usar uma expressão estilo antigo Binding.

Binding As expressões não reconhecem o x:DataType valor, mas essas Binding expressões têm ElementName valores que funcionam quase da mesma maneira. Eles indicam ao mecanismo de vinculação que o Valor de Vinculação é uma ligação à propriedade Value do elemento especificado na página (ou seja, o elemento que possui o valor x:Name). Se quiser associar a uma propriedade no code-behind, isso seria parecido com {Binding MyCodeBehindProperty, ElementName=page}, onde page se refere ao valor x:Name definido no elemento Page em XAML.

Observação

Por padrão, as expressões Binding são unidirecionais, o que significa que atualizarão automaticamente a interface de utilizadorquando o valor da propriedade vinculada for alterado.

Por outro lado, o padrão para x:Bind é umvez, o que significa que todas as alterações na propriedade vinculada são ignoradas. Esse é o padrão porque é a opção de maior desempenho e a maioria das ligações são para dados estáticos e somente leitura.

A lição aqui é que, se você usar x:Bind com propriedades que podem alterar seus valores, certifique-se 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 poderoso sem precisar de muito código.

aplicação em execução com slider de zoom mostrando

Observação

Para um desafio, experimente ligar outras propriedades da interface do utilizador à propriedade do controlo deslizante de zoom Value, ou a outros controlos deslizantes que acrescentar após o controlo deslizante de zoom. Por exemplo, você pode vincular a propriedade FontSize do TitleTextBlock a um novo controle deslizante com um valor padrão de 24. Certifique-se 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á associações unidirecionais do modelo de imagem para a nova propriedade. O valor ItemSize será atualizado pelo controle deslizante de zoom e por outros fatores como o alternador Ajustar à tela e o tamanho da janela, tornando a experiência mais refinada.

Ao contrário das propriedades de controlo internas, as suas propriedades personalizadas não atualizam automaticamente a UI, mesmo com vínculos unidirecionais e bidirecionais. Eles funcionam bem com ligações de devez, mas se você quiser que as alterações de propriedade realmente apareçam na interface do usuário, será necessário trabalhar.

Crie a propriedade ItemSize para que ela atualize a interface do usuário

  1. Em MainPage.xaml.cs, altere a MainPage assinatura da classe para que ela implemente a INotifyPropertyChanged interface.

    Antes:

    public sealed partial class MainPage : Page
    

    Depois:

    public sealed partial class MainPage : Page, INotifyPropertyChanged
    

    Isso informa ao sistema de vinculação que MainPage tem um evento PropertyChanged (adicionado em seguida) que as associações podem escutar para atualizar a interface do usuário.

  2. Adicione um PropertyChanged evento à MainPage classe.

    public event PropertyChangedEventHandler PropertyChanged;
    

    Este evento fornece a implementação completa exigida pela interface INotifyPropertyChanged. No entanto, para que ele tenha algum efeito, você deve gerar explicitamente o evento em suas propriedades personalizadas.

  3. Adicione uma propriedade ItemSize e aumente o evento PropertyChanged em seu 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 _itemSize privado. O uso de um campo de suporte como este permite que a propriedade verifique se um novo valor é igual ao valor antigo antes de gerar um evento potencialmente desnecessário PropertyChanged .

    O evento em si é gerado pelo método Invoke. O ponto de interrogação verifica se o PropertyChanged evento é nulo - ou seja, se algum manipulador de eventos já foi adicionado. Cada ligação unidirecional ou bidirecional adiciona um manipulador de eventos nos bastidores, mas se ninguém estiver ouvindo, nada mais aconteceria aqui. Se, no entanto, PropertyChanged não for 'null', Invoke será chamado com uma referência à fonte do evento (a própria página, representada pela palavra-chave this) e um objeto de evento-args que indica o nome da propriedade. Com essas informações, todas as ligações unidirecionais ou bidirecionais à propriedade ItemSize serão informadas de quaisquer alterações para que possam atualizar a interface de utilizador vinculada.

  4. Em MainPage.xaml, localize o DataTemplate nome ImageGridView_DefaultItemTemplate e substitua os valores de Height e Width do controlo na parte superior do Grid template. (Se você fez a ligação de controle para controle na parte anterior deste tutorial, as únicas alterações são substituir Value por ItemSize e ZoomSlider por page. Certifique-se de fazer isso para ambos Height e Width!)

    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 de utilizador pode responder a alterações de ItemSize, é necessário realmente fazer algumas alterações. Como mencionado anteriormente, o ItemSize valor é calculado a partir do estado atual de vários controles da interface do usuário, mas o cálculo deve ser executado sempre que esses controles mudarem de estado. Para fazer isso, irá usar a associação de eventos para que determinadas alterações na interface do utilizador chamem um método auxiliar que atualize ItemSize.

Atualizar o valor da propriedade ItemSize

  1. Adicione o DetermineItemSize método 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;
        }
    }
    
  2. Em MainPage.xaml, navegue até a parte superior do arquivo e adicione uma SizeChanged associação de evento ao Page elemento.

    Antes:

    <Page x:Name="page"
    

    Depois:

    <Page x:Name="page"
          SizeChanged="{x:Bind DetermineItemSize}"
    
  3. Localize o Slider denominado ZoomSlider (na seção Page.Resources) e adicione uma ligação de evento ValueChanged.

    Antes:

    <Slider x:Name="ZoomSlider"
    

    Depois:

    <Slider x:Name="ZoomSlider"
            ValueChanged="{x:Bind DetermineItemSize}"
    
  4. Localize o ToggleSwitch chamado FitScreenToggle e adicione uma associação de evento Toggled.

    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 Ajustar à tela alternar para alterar as dimensões do modelo de imagem. Como você pode ver, as alterações mais recentes permitem uma experiência de zoom/redimensionamento mais refinada, mantendo o código bem organizado.

Aplicação a correr com ajuste ao ecrã habilitado

Observação

Para um desafio, tente adicionar um TextBlock após o ZoomSlider e vincular a Text propriedade à ItemSize propriedade. Como ele não está em um modelo de dados, você pode usar x:Bind em vez de Binding como nas associações anteriores ItemSize .

Parte 5: Habilitar edições do usuário

Aqui, você criará ligações bidirecionais para permitir que os usuários atualizem valores, incluindo o título da imagem, a classificação e vários efeitos visuais.

Para conseguir isso, você atualizará o existente DetailPage, que fornece um visualizador de imagem única, controle de zoom e interface do usuário de edição.

Primeiro, no entanto, você precisa anexar o DetailPage para que o aplicativo navegue até ele quando o usuário clica em uma imagem na exibição de galeria.

Anexe a página de detalhes

  1. Em MainPage.xaml, localize o GridView chamado ImageGridView. Para tornar os itens clicáveis, defina IsItemClickEnabled como True e adicione um manipulador de ItemClick eventos.

    Sugestão

    Se você digitar a alteração abaixo em vez de copiar/colar, verá um pop-up do IntelliSense que diz "<New Event Handler>". Se você pressionar a tecla Tab, ela preencherá o valor com um nome de manipulador de método padrão e excluirá automaticamente o método mostrado na próxima etapa. Em seguida, podes pressionar F12 para navegar até o método no código de fundo.

    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 ocorre porque precisamos ver os dados do evento, como mostrado a seguir.

  2. No MainPage.xaml.cs, adicione o manipulador de eventos (ou preencha-o, se você usou 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 ImageFileInfo objeto usado por DetailPage.OnNavigatedTo para inicializar a página. Você não terá que implementar esse método neste tutorial, mas você pode dar uma olhada para ver o que ele faz.

  3. (Opcional) Exclua ou comente todos os controles adicionados em pontos de reprodução anteriores que funcionam com a imagem selecionada no momento. Mantê-los por perto não fará mal a nada, 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 ao redor. 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 as alterações. Você precisa mudar 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 usando as expressões simples x:Bind que abordamos na Parte 1. Se você se lembrar, isso significa que todos eles são ligações únicas, o que explica por que as alterações nos valores não são registradas. Para corrigir isto, tudo o que temos de fazer é transformá-las em ligações bidirecionais.

Tornar os controles de edição interativos

  1. Em DetailPage.xaml, localize o chamado TitleTextBlock e o controle RatingControl depois dele e atualize suas expressões para incluir 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}"
                            ... >
    
  2. Faça a mesma coisa para todos os controles deslizantes de efeito que vêm após 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 seria de esperar, significa que os dados se movem em ambas as direções sempre que há alterações em ambos os lados.

Como as ligações unidirecionais abordadas anteriormente, essas ligações bidirecionais agora atualizarão a interface do usuário sempre que as propriedades vinculadas forem alteradas, graças à INotifyPropertyChanged implementação na ImageFileInfo classe. Com a associação bidirecional, no entanto, os valores também serão movidos da interface do usuário para as propriedades associadas sempre que o usuário interagir com o controle. No lado XAML, nada mais é necessário.

Execute o aplicativo e tente os controles de edição. Como você pode ver, quando você faz uma alteração, ela agora afeta os valores da imagem, e essas alterações persistem quando você navega de volta para a página principal.

Parte 6: Formatar valores através da vinculação de funções

Subsiste um último problema. Quando você move os controles deslizantes de efeito, os rótulos ao lado deles ainda não mudam.

controles deslizantes de efeito com valores de rótulo padrão

A parte final deste tutorial é adicionar associações que formatam os valores do controle deslizante para exibição.

Vincular os rótulos do controle deslizante de efeitos e formatar os valores para exibição

  1. Encontre o TextBlock após o controlo deslizante Exposure e substitua o valor de Text pela expressão de ligaçã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á vinculando ao valor de retorno de um método. O método deve ser 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, o método é o conhecido método .NET ToString, que é acessado através da propriedade item da página e, em seguida, através da propriedade Exposure do item. (Isso ilustra como se pode ligar a métodos e propriedades que estão profundamente aninhados numa cadeia de conexões.)

    A vinculação de função é uma 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 escutará as alterações nesses valores conforme esperado com o modo unidirecional. Neste exemplo, o argumento cultura é uma referência a um campo imutável implementado em code-behind, mas poderia facilmente ter sido uma propriedade que gera eventos . Nesse caso, quaisquer alterações no valor da propriedade fariam com que a x:Bind expressão chamasse ToString com o novo valor e, em seguida, atualizasse a interface do usuário com o resultado.

  2. Faça a mesma coisa para os TextBlockque 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, incluindo os rótulos do controle deslizante.

controles deslizantes de efeito com etiquetas de trabalho

Diferenças entre Binding e x:Bind

Ao criar associações de dados em XAML em seus aplicativos UWP, você pode escolher entre Binding e x:Bind. Aqui estão as principais diferenças:

  • x:Bind: Fornece validação em tempo de compilação, melhor desempenho e é fortemente tipificado. É mais adequado para cenários em que a estrutura de dados é conhecida em tempo de compilação.
  • Binding: Oferece avaliação de tempo de execução e maior flexibilidade para cenários dinâmicos, como quando a estrutura de dados é determinada em tempo de execução.

Cenários não suportados por x:Bind

Embora x:Bind seja altamente eficiente, tem limitações em determinados cenários:

  • Estruturas de dados dinâmicas: x:Bind não podem ser usadas quando a estrutura de dados é determinada em tempo de execução.
  • Vinculação de elemento a elemento: a vinculação direta entre dois elementos da interface do usuário não é suportada pelo x:Bind.
  • Herança DataContext: Ao contrário de Binding, x:Bind não herda automaticamente o DataContext de um elemento pai.
  • Ligações bidirecionais: x:Bind suporta ligações bidirecionais, permitindo que as alterações fluam da interface do usuário de volta para a propriedade de origem. Para que a interface de utilizador seja atualizada quando a propriedade fonte for alterada (em ligações unidirecionais ou bidirecionais), é necessário implementar INotifyPropertyChanged nos seus objetos de dados.

Para obter mais detalhes e exemplos, consulte os seguintes recursos:

Conclusão

Este tutorial deu-lhe uma amostra da vinculação de dados e mostrou-lhe algumas das funcionalidades disponíveis. Uma palavra de cautela antes de terminarmos: nem tudo é vinculável e, às vezes, os valores aos quais você tenta se conectar são incompatíveis com as propriedades que você está tentando vincular. Há muita flexibilidade na vinculação, mas ela não funcionará em todas as situações.

Um exemplo de um problema não resolvido pela associação é quando um controle não tem propriedades adequadas para vincular, como acontece com o recurso de zoom de página de detalhes. Este controle deslizante de zoom precisa interagir com o ScrollViewer que exibe a imagem, mas ScrollViewer só pode ser atualizado através de seu ChangeView método. Nesse caso, usamos manipuladores de eventos convencionais para manter o ScrollViewer e o controle deslizante de zoom sincronizados; consulte os métodos ZoomSlider_ValueChanged e MainImageScroll_ViewChanged em DetailPage para obter mais detalhes.

No entanto, a vinculação é uma maneira poderosa e flexível de simplificar seu código e manter a lógica da interface do usuário separada da lógica de dados. Isso tornará muito mais fácil para você ajustar ambos os lados dessa divisão, reduzindo o risco de introduzir bugs do outro lado.

Um exemplo de separação de dados e interface de utilizador é com a propriedade ImageFileInfo.ImageTitle. Essa propriedade (e a ImageRating propriedade) é ligeiramente diferente da ItemSize propriedade criada na Parte 4 porque o valor é armazenado nos metadados do arquivo (expostos através do ImageProperties tipo) em vez de em um campo. Além disso, ImageTitle retorna o ImageName valor (definido como o nome do arquivo) se não houver nenhum 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 ImageProperties.Title propriedade e, em seguida, chama SavePropertiesAsync para gravar o novo valor no arquivo. (Este é um método assíncrono, mas não podemos usar a palavra-chave await em uma propriedade - e você não gostaria porque os getters e setters de propriedade devem concluir imediatamente. Então, em vez disso, você chamaria o método e ignoraria o objeto Task que ele retorna.)

Ir mais longe

Agora que você concluiu este laboratório, você tem conhecimento suficiente para resolver um problema por conta própria.

Como você deve ter notado, se você alterar o nível de zoom na página de detalhes, ele será redefinido automaticamente quando você navegar para trás e, em seguida, selecionar a mesma imagem novamente. Você consegue descobrir como preservar e restaurar o nível de zoom para cada imagem individualmente? Boa sorte!

Você deve ter 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. Começar aqui: