Partilhar via


Visão geral da vinculação de dados do Windows

A vinculação de dados em aplicativos WinUI permite conectar controles a fontes de dados de forma eficiente. Saiba como vincular um controle a um único item ou a uma coleção de itens, controlar a renderização de itens, implementar exibições de detalhes e formatar dados para exibição. Para obter mais detalhes, consulte Vinculação de dados em profundidade.

Pré-requisitos

Este tópico pressupõe que você saiba como criar um aplicativo WinUI básico com o SDK de Aplicativo do Windows. Para obter instruções sobre como criar seu primeiro aplicativo WinUI, consulte Criar um aplicativo WinUI.

Criar o projeto

Crie um novo aplicativo em branco WinUI, projeto C# empacotado. Nomeie-o "Quickstart".

Vincular a um único item

Cada ligação consiste em um destino de vinculação e uma fonte de vinculação. Normalmente, o destino é uma propriedade de um controle ou outro elemento da interface do usuário, e a origem é uma propriedade de uma instância de classe (um modelo de dados ou um modelo de exibição). Este exemplo mostra como vincular um controle a um único item. O alvo é a propriedade Text de um TextBlock. A origem é uma instância de uma classe simples chamada Recording que representa uma gravação de áudio. Vejamos primeiro a aula.

Adicione uma nova classe ao seu projeto e nomeie a classe Recording.

namespace Quickstart
{
    public class Recording
    {
        public string ArtistName { get; set; }
        public string CompositionName { get; set; }
        public DateTime ReleaseDateTime { get; set; }
        public Recording()
        {
            ArtistName = "Wolfgang Amadeus Mozart";
            CompositionName = "Andante in C for Piano";
            ReleaseDateTime = new DateTime(1761, 1, 1);
        }
        public string OneLineSummary
        {
            get
            {
                return $"{CompositionName} by {ArtistName}, released: "
                    + ReleaseDateTime.ToString("d");
            }
        }
    }
    public class RecordingViewModel
    {
        private Recording defaultRecording = new();
        public Recording DefaultRecording { get { return defaultRecording; } }
    }
}

Em seguida, exponha a classe de origem de vinculação da classe que representa sua janela de marcação. Adicione uma propriedade do tipo RecordingViewModel a MainWindow.xaml.cs.

namespace Quickstart
{
    public sealed partial class MainWindow : Window
    {
        public MainWindow()
        {
            this.InitializeComponent();
        }
        public RecordingViewModel ViewModel{ get; } = new RecordingViewModel();
    }
}

A última peça é ligar um TextBlock à propriedade ViewModel.DefaultRecording.OneLineSummary.

<Window x:Class="Quickstart.MainWindow" ... >
    <Grid>
        <TextBlock Text="{x:Bind ViewModel.DefaultRecording.OneLineSummary}"
                   HorizontalAlignment="Center"
                   VerticalAlignment="Center"/>
    </Grid>
</Window>

Aqui está o resultado.

Captura de tela de um aplicativo WinUI mostrando um TextBlock vinculado a um único item.

Vincular a uma coleção de itens

Um cenário comum é vincular a uma coleção de objetos de negócios. Em C#, use a classe genérica ObservableCollection<T> para vinculação de dados. Ele implementa a interface INotifyCollectionChanged , que fornece notificação de alteração para associações quando itens são adicionados ou removidos. No entanto, devido a um bug conhecido do modo de lançamento do WinUI com o .NET 8 e posterior, talvez seja necessário usar uma Lista<T> em alguns cenários, especialmente se sua coleção for estática e não for alterada após a inicialização. Se a interface do usuário precisar ser atualizada quando a coleção for alterada no tempo de execução, use ObservableCollection<T>. Se você só precisa exibir um conjunto fixo de itens, List<T> é suficiente. Além disso, se você quiser que seus controles acoplados sejam atualizados com alterações nas propriedades de objetos na coleção, esses objetos devem implementar INotifyPropertyChanged. Para obter mais informações, consulte Vinculação de dados em profundidade.

Observação

Ao usar List<T>, pode não receber notificações sobre mudanças em coleções. Se precisar de responder a alterações, considere utilizar ObservableCollection<T>o . Neste exemplo, você não precisa responder a alterações de coleção, portanto List<T> , é suficiente.

O exemplo a seguir vincula um ListView a uma coleção de Recording objetos. Primeiro, adicione a coleção ao seu modelo de exibição. Adicione esses novos membros à RecordingViewModel classe.

public class RecordingViewModel
{
    ...
    private List<Recording> recordings = new();
    public List<Recording> Recordings{ get{ return recordings; } }
    public RecordingViewModel()
    {
        recordings.Add(new Recording(){ ArtistName = "Johann Sebastian Bach",
            CompositionName = "Mass in B minor", ReleaseDateTime = new DateTime(1748, 7, 8) });
        recordings.Add(new Recording(){ ArtistName = "Ludwig van Beethoven",
            CompositionName = "Third Symphony", ReleaseDateTime = new DateTime(1805, 2, 11) });
        recordings.Add(new Recording(){ ArtistName = "George Frideric Handel",
            CompositionName = "Serse", ReleaseDateTime = new DateTime(1737, 12, 3) });
    }
}

Em seguida, vincule um ListView à ViewModel.Recordings propriedade.

<Window x:Class="Quickstart.MainWindow" ... >
    <Grid>
        <ListView ItemsSource="{x:Bind ViewModel.Recordings}"
                  HorizontalAlignment="Center"
                  VerticalAlignment="Center"/>
    </Grid>
</Window>

Você ainda não forneceu um modelo de dados para a Recording classe, portanto, o melhor que a estrutura da interface do usuário pode fazer é chamar ToString para cada item no ListView. A implementação padrão de ToString retorna o nome do tipo.

Vinculando um modo de exibição de lista 1

Para corrigir este problema, pode sobrescrever ToString para retornar o valor de OneLineSummary, ou pode fornecer um modelo de dados. A opção de modelo de dados é uma solução mais comum e flexível. Você especifica um modelo de dados usando a propriedade ContentTemplate de um controle de conteúdo ou a propriedade ItemTemplate de um controle de itens. Aqui estão duas maneiras de criar um modelo de dados para Recording juntamente com uma ilustração do resultado.

<ListView ItemsSource="{x:Bind ViewModel.Recordings}"
HorizontalAlignment="Center" VerticalAlignment="Center">
    <ListView.ItemTemplate>
        <DataTemplate x:DataType="local:Recording">
            <TextBlock Text="{x:Bind OneLineSummary}"/>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

Vinculando um modo de exibição de lista 2

<ListView ItemsSource="{x:Bind ViewModel.Recordings}"
HorizontalAlignment="Center" VerticalAlignment="Center">
    <ListView.ItemTemplate>
        <DataTemplate x:DataType="local:Recording">
            <StackPanel Orientation="Horizontal" Margin="6">
                <SymbolIcon Symbol="Audio" Margin="0,0,12,0"/>
                <StackPanel>
                    <TextBlock Text="{x:Bind ArtistName}" FontWeight="Bold"/>
                    <TextBlock Text="{x:Bind CompositionName}"/>
                </StackPanel>
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

Vinculando um modo de exibição de lista 3

Para obter mais informações sobre sintaxe XAML, consulte Criar uma interface do usuário com XAML. Para obter mais informações sobre layout de controle, consulte Definir layouts com XAML.

Adicionar uma vista de detalhes

Pode escolher exibir todos os detalhes dos objetos nos itens do ListView . Mas essa abordagem ocupa muito espaço. Em vez disso, você pode mostrar apenas dados suficientes no item para identificá-lo. Quando o usuário faz uma seleção, você pode exibir todos os detalhes do item selecionado em uma parte separada da interface do usuário, conhecida como exibição de detalhes. Esta disposição também é conhecida como uma vista mestre/detalhes ou uma vista de lista/detalhes.

Você pode implementar esse arranjo de duas maneiras. Você pode vincular o modo de exibição de detalhes à propriedade SelectedItem do ListView. Ou você pode usar um CollectionViewSource. Nesse caso, você vincula tanto o ListView quanto a vista de detalhes ao CollectionViewSource. Essa abordagem cuida do item selecionado atualmente para você. Ambas as técnicas são mostradas nas seções a seguir, e ambas dão os mesmos resultados (mostrados na ilustração).

Observação

Até agora, neste tópico, você usou apenas a extensão de marcação {x:Bind}. Mas ambas as técnicas mostradas nas seções a seguir exigem a extensão de marcação {Binding} mais flexível (mas com menor desempenho).

Primeiro, aqui está a técnica do SelectedItem. Para um aplicativo C#, a única alteração necessária é na marcação.

<Window x:Class="Quickstart.MainWindow" ... >
    <Grid>
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <ListView x:Name="recordingsListView" ItemsSource="{x:Bind ViewModel.Recordings}">
                <ListView.ItemTemplate>
                    <DataTemplate x:DataType="local:Recording">
                        <StackPanel Orientation="Horizontal" Margin="6">
                            <SymbolIcon Symbol="Audio" Margin="0,0,12,0"/>
                            <StackPanel>
                                <TextBlock Text="{x:Bind CompositionName}"/>
                            </StackPanel>
                        </StackPanel>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
            <StackPanel DataContext="{Binding SelectedItem, ElementName=recordingsListView}"
            Margin="0,24,0,0">
                <TextBlock Text="{Binding ArtistName}"/>
                <TextBlock Text="{Binding CompositionName}"/>
                <TextBlock Text="{Binding ReleaseDateTime}"/>
            </StackPanel>
        </StackPanel>
    </Grid>
</Window>

Para a técnica de CollectionViewSource , primeiro adicione um como recurso no nível superior do .

<Grid.Resources>
    <CollectionViewSource x:Name="RecordingsCollection" Source="{x:Bind ViewModel.Recordings}"/>
</Grid.Resources>

Observação

A classe Window no WinUI não tem uma propriedade Resources. Em vez disso, você pode adicionar o CollectionViewSource ao elemento Grid de nível superior (ou outro elemento pai da interface do usuário, como StackPanel). Se estiver a trabalhar num Page, pode adicionar o CollectionViewSource ao Page.Resources.

Em seguida, ajuste as associações no ListView (que não precisa mais ser nomeado) e no modo de exibição de detalhes para usar o CollectionViewSource. Ao vincular a exibição de detalhes diretamente ao CollectionViewSource, você implica que deseja vincular ao item atual em associações onde o caminho não pode ser encontrado na própria coleção. Não há necessidade de especificar a propriedade CurrentItem como o caminho para a vinculação, embora você possa fazer isso se houver alguma ambiguidade.

...
<ListView ItemsSource="{Binding Source={StaticResource RecordingsCollection}}">
...
<StackPanel DataContext="{Binding Source={StaticResource RecordingsCollection}}" ...>
...

E aqui está o resultado idêntico em cada caso.

Vinculando uma visualização de lista 4

Formatar ou converter valores de dados para exibição

A renderização acima tem um problema. A ReleaseDateTime propriedade não é apenas uma data, é um DateTime. Assim, ele exibe com mais precisão do que você precisa. Uma solução é adicionar uma propriedade string à classe Recording que retorna o equivalente a ReleaseDateTime.ToString("d"). Nomear essa propriedade ReleaseDate indica que ela retorna uma data, e não uma data e hora. Nomeá-lo ReleaseDateAsString ainda indica que ele retorna uma cadeia de caracteres.

Uma solução mais flexível é usar um conversor de valor. Aqui está um exemplo de como criar seu próprio conversor de valor. Adicione o seguinte código ao seu arquivo de código-fonte Recording.cs.

public class StringFormatter : Microsoft.UI.Xaml.Data.IValueConverter
{
    // This converts the value object to the string to display.
    // This will work with most simple types.
    public object Convert(object value, Type targetType,
        object parameter, string language)
    {
        // Retrieve the format string and use it to format the value.
        string formatString = parameter as string;
        if (!string.IsNullOrEmpty(formatString))
        {
            return string.Format(formatString, value);
        }

        // If the format string is null or empty, simply
        // call ToString() on the value.
        return value.ToString();
    }

    // No need to implement converting back on a one-way binding
    public object ConvertBack(object value, Type targetType,
        object parameter, string language)
    {
        throw new NotImplementedException();
    }
}

Pode-se agora adicionar uma instância de StringFormatter como recurso e usá-la na associação do TextBlock que exibe a propriedade ReleaseDateTime.

<Grid.Resources>
    ...
    <local:StringFormatter x:Key="StringFormatterValueConverter"/>
</Grid.Resources>
...
<TextBlock Text="{Binding ReleaseDateTime,
    Converter={StaticResource StringFormatterValueConverter},
    ConverterParameter=Released: \{0:d\}}"/>
...

Como você pode ver, para flexibilidade de formatação, a marcação passa uma cadeia de caracteres de formato para o conversor por meio do parâmetro conversor. No exemplo de código mostrado neste tópico, o conversor de valor C# usa esse parâmetro.

Aqui está o resultado.

exibir uma data com formatação personalizada

Diferenças entre Binding e x:Bind

Ao trabalhar com a associação de dados em aplicativos WinUI, você pode encontrar dois mecanismos de vinculação principais: Binding e x:Bind. Embora ambos sirvam ao propósito de conectar elementos da interface do usuário a fontes de dados, eles têm diferenças distintas:

  • x:Bind: Oferece verificação em tempo de compilação, desempenho otimizado e um sistema de tipos rigoroso. É ideal para cenários em que você conhece a estrutura de dados em tempo de compilação.
  • Binding: Fornece avaliação de tempo de execução e é mais flexível para cenários dinâmicos, como quando a estrutura de dados não é conhecida em tempo de compilação.

Cenários não suportados por x:Bind

Embora x:Bind seja poderoso, você não pode usá-lo em determinados cenários:

  • Estruturas de dados dinâmicas: se a estrutura de dados não for conhecida em tempo de compilação, você não poderá usar x:Bindo .
  • Vinculação elemento-a-elemento: x:Bind não suporta vinculação diretamente entre dois elementos da interface do usuário.
  • A vinculação a um DataContext: x:Bind não herda automaticamente as propriedades do elemento pai DataContext.
  • Ligações bidirecionais com Mode=TwoWay: Embora suportado, x:Bind requer a implementação explícita de INotifyPropertyChanged para qualquer propriedade que pretende que a interface do usuário atualize quando a fonte for alterada, quer se trate de uma associação unidirecional ou bidirecional. A principal diferença com as ligações bidirecionais é que as alterações também fluem da interface do usuário de volta para a origem.

Para obter exemplos práticos e uma compreensão mais profunda de quando usar cada um deles, consulte os seguintes tópicos: