Partilhar via


Visão geral da vinculação de dados

Este tópico mostra como vincular um controle (ou outro elemento da interface do usuário) a um único item ou vincular um controle items a uma coleção de itens em um aplicativo da Plataforma Universal do Windows (UWP). Além disso, mostramos como controlar a renderização de itens, implementar uma exibição de detalhes com base em uma seleção e converter dados para exibição. Para obter informações mais detalhadas, consulte Vinculação de dados detalhada.

Pré-requisitos

Este tópico pressupõe que você saiba como criar um aplicativo UWP básico. Para obter instruções sobre como criar seu primeiro aplicativo UWP, consulte Introdução aos aplicativos do Windows.

Criar o projeto

Crie um novo aplicativo em branco (Windows Universal) projeto. Nomeie-o "Quickstart".

Vinculação 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 destino é 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 a turma primeiro.

Se você estiver usando C# ou C++/CX, adicione uma nova classe ao seu projeto e nomeie a classe como Recording.

Se você estiver usando C++/WinRT , adicione novos itens Midl File (.idl) ao projeto, nomeado conforme mostrado na lista de exemplo de código C++/WinRT abaixo. Substitua o conteúdo desses novos arquivos pelo código de MIDL 3.0 mostrado na listagem, crie o projeto para gerar Recording.h e .cpp e RecordingViewModel.h e .cppe, em seguida, adicione código aos arquivos gerados para corresponder à listagem. Para obter mais informações sobre esses arquivos gerados e como copiá-los no seu projeto, consulte os controlos XAML em ; vincule-se a uma propriedade C++/WinRT em.

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

// RecordingViewModel.idl
import "Recording.idl";

namespace Quickstart
{
    runtimeclass RecordingViewModel
    {
        RecordingViewModel();
        Quickstart.Recording DefaultRecording{ get; };
    }
}

// Recording.h
// Add these fields:
...
#include <sstream>
...
private:
    std::wstring m_artistName;
    std::wstring m_compositionName;
    Windows::Globalization::Calendar m_releaseDateTime;
...

// Recording.cpp
// Implement like this:
...
Recording::Recording(hstring const& artistName, hstring const& compositionName, Windows::Globalization::Calendar const& releaseDateTime) :
    m_artistName{ artistName.c_str() },
    m_compositionName{ compositionName.c_str() },
    m_releaseDateTime{ releaseDateTime } {}

hstring Recording::ArtistName(){ return hstring{ m_artistName }; }
hstring Recording::CompositionName(){ return hstring{ m_compositionName }; }
Windows::Globalization::Calendar Recording::ReleaseDateTime(){ return m_releaseDateTime; }

hstring Recording::OneLineSummary()
{
    std::wstringstream wstringstream;
    wstringstream << m_compositionName.c_str();
    wstringstream << L" by " << m_artistName.c_str();
    wstringstream << L", released: " << m_releaseDateTime.MonthAsNumericString().c_str();
    wstringstream << L"/" << m_releaseDateTime.DayAsString().c_str();
    wstringstream << L"/" << m_releaseDateTime.YearAsString().c_str();
    return hstring{ wstringstream.str().c_str() };
}
...

// RecordingViewModel.h
// Add this field:
...
#include "Recording.h"
...
private:
    Quickstart::Recording m_defaultRecording{ nullptr };
...

// RecordingViewModel.cpp
// Implement like this:
...
Quickstart::Recording RecordingViewModel::DefaultRecording()
{
    Windows::Globalization::Calendar releaseDateTime;
    releaseDateTime.Year(1761);
    releaseDateTime.Month(1);
    releaseDateTime.Day(1);
    m_defaultRecording = winrt::make<Recording>(L"Wolfgang Amadeus Mozart", L"Andante in C for Piano", releaseDateTime);
    return m_defaultRecording;
}
...
// Recording.h
#include <sstream>
namespace Quickstart
{
    public ref class Recording sealed
    {
    private:
        Platform::String^ artistName;
        Platform::String^ compositionName;
        Windows::Globalization::Calendar^ releaseDateTime;
    public:
        Recording(Platform::String^ artistName, Platform::String^ compositionName,
            Windows::Globalization::Calendar^ releaseDateTime) :
            artistName{ artistName },
            compositionName{ compositionName },
            releaseDateTime{ releaseDateTime } {}
        property Platform::String^ ArtistName
        {
            Platform::String^ get() { return this->artistName; }
        }
        property Platform::String^ CompositionName
        {
            Platform::String^ get() { return this->compositionName; }
        }
        property Windows::Globalization::Calendar^ ReleaseDateTime
        {
            Windows::Globalization::Calendar^ get() { return this->releaseDateTime; }
        }
        property Platform::String^ OneLineSummary
        {
            Platform::String^ get()
            {
                std::wstringstream wstringstream;
                wstringstream << this->CompositionName->Data();
                wstringstream << L" by " << this->ArtistName->Data();
                wstringstream << L", released: " << this->ReleaseDateTime->MonthAsNumericString()->Data();
                wstringstream << L"/" << this->ReleaseDateTime->DayAsString()->Data();
                wstringstream << L"/" << this->ReleaseDateTime->YearAsString()->Data();
                return ref new Platform::String(wstringstream.str().c_str());
            }
        }
    };
    public ref class RecordingViewModel sealed
    {
    private:
        Recording ^ defaultRecording;
    public:
        RecordingViewModel()
        {
            Windows::Globalization::Calendar^ releaseDateTime = ref new Windows::Globalization::Calendar();
            releaseDateTime->Year = 1761;
            releaseDateTime->Month = 1;
            releaseDateTime->Day = 1;
            this->defaultRecording = ref new Recording{ L"Wolfgang Amadeus Mozart", L"Andante in C for Piano", releaseDateTime };
        }
        property Recording^ DefaultRecording
        {
            Recording^ get() { return this->defaultRecording; };
        }
    };
}

// Recording.cpp
#include "pch.h"
#include "Recording.h"

Em seguida, exponha a classe de origem de vinculação da classe que representa sua página de marcação. Fazemos isso adicionando uma propriedade do tipo RecordingViewModel à MainPage.

Se estiver a utilizar C++/WinRT, primeiro atualize MainPage.idl. Crie o projeto para regenerar MainPage.h e .cpp, e mescle as alterações nesses arquivos gerados com os do seu projeto.

namespace Quickstart
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
            this.ViewModel = new RecordingViewModel();
        }
        public RecordingViewModel ViewModel{ get; set; }
    }
}
// MainPage.idl
// Add this property:
import "RecordingViewModel.idl";
...
RecordingViewModel ViewModel{ get; };
...

// MainPage.h
// Add this property and this field:
...
#include "RecordingViewModel.h"
...
    Quickstart::RecordingViewModel ViewModel();

private:
    Quickstart::RecordingViewModel m_viewModel{ nullptr };
...

// MainPage.cpp
// Implement like this:
...
MainPage::MainPage()
{
    InitializeComponent();
    m_viewModel = winrt::make<RecordingViewModel>();
}
Quickstart::RecordingViewModel MainPage::ViewModel()
{
    return m_viewModel;
}
...
// MainPage.h
...
#include "Recording.h"

namespace Quickstart
{
    public ref class MainPage sealed
    {
    private:
        RecordingViewModel ^ viewModel;
    public:
        MainPage();

        property RecordingViewModel^ ViewModel
        {
            RecordingViewModel^ get() { return this->viewModel; };
        }
    };
}

// MainPage.cpp
...
MainPage::MainPage()
{
    InitializeComponent();
    this->viewModel = ref new RecordingViewModel();
}

A última parte é vincular um TextBlock à propriedade ViewModel.DefaultRecording.OneLineSummary.

<Page x:Class="Quickstart.MainPage" ... >
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock Text="{x:Bind ViewModel.DefaultRecording.OneLineSummary}"
    HorizontalAlignment="Center"
    VerticalAlignment="Center"/>
    </Grid>
</Page>

Se estiveres a usar C++/WinRT , precisas de remover a função MainPage::ClickHandler para que o projeto seja compilado.

Aqui está o resultado.

Vinculando um bloco de texto

Vinculação a uma coleção de itens

Um cenário comum é associar-se a uma coleção de objetos empresariais. Em C# e Visual Basic, o genérico ObservableCollection<classe T> é uma boa opção de coleta para vinculação de dados, porque implementa o INotifyPropertyChanged e interfaces de INotifyCollectionChanged. Essas interfaces fornecem notificação de alteração para associações quando itens são adicionados ou removidos ou uma propriedade da própria lista é alterada. Se você quiser que seus controles acoplados sejam atualizados com alterações nas propriedades de objetos na coleção, o objeto de negócios também deve implementar INotifyPropertyChanged . Para obter mais informações, consulte Vinculação de dados em profundidade.

Se você estiver usando C++/WinRT , poderá aprender mais sobre a vinculação a uma coleção observável em controles de itens XAML; vincular a uma coleção C++/WinRT. Se você ler esse tópico primeiro, a intenção da listagem de código C++/WinRT mostrada abaixo será mais clara.

Este próximo exemplo vincula um ListView a uma coleção de objetos Recording. Vamos começar adicionando a coleção ao nosso modelo de exibição. Basta adicionar esses novos membros à classe RecordingViewModel.

public class RecordingViewModel
{
    ...
    private ObservableCollection<Recording> recordings = new ObservableCollection<Recording>();
    public ObservableCollection<Recording> Recordings{ get{ return this.recordings; } }
    public RecordingViewModel()
    {
        this.recordings.Add(new Recording(){ ArtistName = "Johann Sebastian Bach",
            CompositionName = "Mass in B minor", ReleaseDateTime = new DateTime(1748, 7, 8) });
        this.recordings.Add(new Recording(){ ArtistName = "Ludwig van Beethoven",
            CompositionName = "Third Symphony", ReleaseDateTime = new DateTime(1805, 2, 11) });
        this.recordings.Add(new Recording(){ ArtistName = "George Frideric Handel",
            CompositionName = "Serse", ReleaseDateTime = new DateTime(1737, 12, 3) });
    }
}
// RecordingViewModel.idl
// Add this property:
...
#include <winrt/Windows.Foundation.Collections.h>
...
Windows.Foundation.Collections.IVector<IInspectable> Recordings{ get; };
...

// RecordingViewModel.h
// Change the constructor declaration, and add this property and this field:
...
    RecordingViewModel();
    Windows::Foundation::Collections::IVector<Windows::Foundation::IInspectable> Recordings();

private:
    Windows::Foundation::Collections::IVector<Windows::Foundation::IInspectable> m_recordings;
...

// RecordingViewModel.cpp
// Update/add implementations like this:
...
RecordingViewModel::RecordingViewModel()
{
    std::vector<Windows::Foundation::IInspectable> recordings;

    Windows::Globalization::Calendar releaseDateTime;
    releaseDateTime.Month(7); releaseDateTime.Day(8); releaseDateTime.Year(1748);
    recordings.push_back(winrt::make<Recording>(L"Johann Sebastian Bach", L"Mass in B minor", releaseDateTime));

    releaseDateTime = Windows::Globalization::Calendar{};
    releaseDateTime.Month(11); releaseDateTime.Day(2); releaseDateTime.Year(1805);
    recordings.push_back(winrt::make<Recording>(L"Ludwig van Beethoven", L"Third Symphony", releaseDateTime));

    releaseDateTime = Windows::Globalization::Calendar{};
    releaseDateTime.Month(3); releaseDateTime.Day(12); releaseDateTime.Year(1737);
    recordings.push_back(winrt::make<Recording>(L"George Frideric Handel", L"Serse", releaseDateTime));

    m_recordings = winrt::single_threaded_observable_vector<Windows::Foundation::IInspectable>(std::move(recordings));
}

Windows::Foundation::Collections::IVector<Windows::Foundation::IInspectable> RecordingViewModel::Recordings() { return m_recordings; }
...
// Recording.h
...
public ref class RecordingViewModel sealed
{
private:
    ...
    Windows::Foundation::Collections::IVector<Recording^>^ recordings;
public:
    RecordingViewModel()
    {
        ...
        releaseDateTime = ref new Windows::Globalization::Calendar();
        releaseDateTime->Year = 1748;
        releaseDateTime->Month = 7;
        releaseDateTime->Day = 8;
        Recording^ recording = ref new Recording{ L"Johann Sebastian Bach", L"Mass in B minor", releaseDateTime };
        this->Recordings->Append(recording);
        releaseDateTime = ref new Windows::Globalization::Calendar();
        releaseDateTime->Year = 1805;
        releaseDateTime->Month = 2;
        releaseDateTime->Day = 11;
        recording = ref new Recording{ L"Ludwig van Beethoven", L"Third Symphony", releaseDateTime };
        this->Recordings->Append(recording);
        releaseDateTime = ref new Windows::Globalization::Calendar();
        releaseDateTime->Year = 1737;
        releaseDateTime->Month = 12;
        releaseDateTime->Day = 3;
        recording = ref new Recording{ L"George Frideric Handel", L"Serse", releaseDateTime };
        this->Recordings->Append(recording);
    }
    ...
    property Windows::Foundation::Collections::IVector<Recording^>^ Recordings
    {
        Windows::Foundation::Collections::IVector<Recording^>^ get()
        {
            if (this->recordings == nullptr)
            {
                this->recordings = ref new Platform::Collections::Vector<Recording^>();
            }
            return this->recordings;
        };
    }
};

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

<Page x:Class="Quickstart.MainPage" ... >
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <ListView ItemsSource="{x:Bind ViewModel.Recordings}"
        HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </Grid>
</Page>

Ainda não fornecemos um modelo de dados para a classe Recording, 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 é retornar o nome do tipo.

Ligação de uma vista de lista 1

Para corrigir isso, podemos substituir ToString para retornar o valor de OneLineSummaryou podemos fornecer um modelo de dados. A opção de modelo de dados é uma solução mais usual e mais 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 de gravação, 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

Você pode optar por exibir todos os detalhes de Gravação objetos em ListView itens. Mas isso ocupa muito espaço. Em vez disso, você pode mostrar apenas dados suficientes no item para identificá-lo e, em seguida, 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. Essa disposição também é conhecida como uma visão mestre/detalhes ou uma visão de lista/detalhes.

Há duas formas de o fazer. Você pode vincular o modo de exibição de detalhes à propriedade SelectedItem do ListView. Ou pode-se usar um CollectionViewSource, caso em que se vincula o ListView e a exibição de detalhes ao CollectionViewSource (isso cuidará do item atualmente selecionado). Ambas as técnicas são mostradas abaixo, e ambas dão os mesmos resultados (mostrado na ilustração).

Observação

Até agora, neste tópico, usamos apenas a extensão de marcação {x:Bind}, mas ambas as técnicas que mostraremos abaixo exigem a extensão de marcação mais flexível (mas menos eficiente) {Binding}.

Se estiver a utilizar C++/WinRT ou as extensões de componente Visual C++ (C++/CX), então, para usar a extensão de marcação {Binding}, precisará de adicionar o atributo BindableAttribute a qualquer classe de tempo de execução à qual deseje vincular. Para usar {x:Bind}, você não precisa desse atributo.

Importante

Se você estiver usando C++/WinRT, o atributo BindableAttribute estará disponível se você tiver instalado o SDK do Windows versão 10.0.17763.0 (Windows 10, versão 1809) ou posterior. Sem este atributo, será necessário implementar as interfaces ICustomPropertyProvider e ICustomProperty para poder usar a extensão de marcação {Binding}.

Primeiro, aqui está a técnica de SelectedItem .

// No code changes necessary for C#.
// Recording.idl
// Add this attribute:
...
[Windows.UI.Xaml.Data.Bindable]
runtimeclass Recording
...
[Windows::UI::Xaml::Data::Bindable]
public ref class Recording sealed
{
    ...
};

A única outra alteração necessária é na marcação.

<Page x:Class="Quickstart.MainPage" ... >
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <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>
</Page>

Para a técnica CollectionViewSource , primeiro adicione um CollectionViewSource como um recurso de página.

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

Em seguida, ajuste as associações no ListView (que não precisa mais ser nomeado) e na exibição de detalhes para usar o CollectionViewSource. Observe que, ao vincular a exibição de detalhes diretamente ao CollectionViewSource, você está implicando que deseja vincular ao item atual em associações em que 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 ligaçã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.

Observação

Se você estiver usando C++, sua interface do usuário não será exatamente como a ilustração abaixo: a renderização da propriedade ReleaseDateTime é diferente. Consulte a seção a seguir para mais discussões sobre isso.

Vinculando uma visualização de lista 4

Formatando ou convertendo valores de dados para exibição

Há um problema com a renderização acima. A propriedade ReleaseDateTime não é apenas uma data, é um DateTime (se estiver a usar C++, então é um Calendar). Assim, em C#, ele está sendo exibido com mais precisão do que precisamos. E em C++ ele está sendo renderizado como um nome de tipo. Uma solução é adicionar uma propriedade de cadeia de caracteres à classe Recording que retorna o equivalente a this.ReleaseDateTime.ToString("d"). Nomear essa propriedade como ReleaseDate indicaria que ela retorna uma data, e não uma data e hora. Nomeá-lo ReleaseDateAsString indicaria ainda que ele retorna uma cadeia de caracteres.

Uma solução mais flexível é usar algo conhecido como conversor de valor. Aqui está um exemplo de como criar seu próprio conversor de valor. Se você estiver usando C#, adicione o código abaixo ao seu Recording.cs arquivo de código-fonte. Se você estiver usando C++/WinRT, adicione um novo item Midl File (.idl) ao projeto, nomeado como mostrado na listagem de exemplo de código C++/WinRT abaixo, crie o projeto para gerar StringFormatter.h e .cpp, adicione esses arquivos ao seu projeto e, em seguida, cole as listagens de código neles. Adicione também #include "StringFormatter.h" ao MainPage.h.

public class StringFormatter : Windows.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();
    }
}
// pch.h
...
#include <winrt/Windows.Globalization.h>

// StringFormatter.idl
namespace Quickstart
{
    runtimeclass StringFormatter : [default] Windows.UI.Xaml.Data.IValueConverter
    {
        StringFormatter();
    }
}

// StringFormatter.h
#pragma once

#include "StringFormatter.g.h"
#include <sstream>

namespace winrt::Quickstart::implementation
{
    struct StringFormatter : StringFormatterT<StringFormatter>
    {
        StringFormatter() = default;

        Windows::Foundation::IInspectable Convert(Windows::Foundation::IInspectable const& value, Windows::UI::Xaml::Interop::TypeName const& targetType, Windows::Foundation::IInspectable const& parameter, hstring const& language);
        Windows::Foundation::IInspectable ConvertBack(Windows::Foundation::IInspectable const& value, Windows::UI::Xaml::Interop::TypeName const& targetType, Windows::Foundation::IInspectable const& parameter, hstring const& language);
    };
}

namespace winrt::Quickstart::factory_implementation
{
    struct StringFormatter : StringFormatterT<StringFormatter, implementation::StringFormatter>
    {
    };
}

// StringFormatter.cpp
#include "pch.h"
#include "StringFormatter.h"
#include "StringFormatter.g.cpp"

namespace winrt::Quickstart::implementation
{
    Windows::Foundation::IInspectable StringFormatter::Convert(Windows::Foundation::IInspectable const& value, Windows::UI::Xaml::Interop::TypeName const& /* targetType */, Windows::Foundation::IInspectable const& /* parameter */, hstring const& /* language */)
    {
        // Retrieve the value as a Calendar.
        Windows::Globalization::Calendar valueAsCalendar{ value.as<Windows::Globalization::Calendar>() };

        std::wstringstream wstringstream;
        wstringstream << L"Released: ";
        wstringstream << valueAsCalendar.MonthAsNumericString().c_str();
        wstringstream << L"/" << valueAsCalendar.DayAsString().c_str();
        wstringstream << L"/" << valueAsCalendar.YearAsString().c_str();
        return winrt::box_value(hstring{ wstringstream.str().c_str() });
    }

    Windows::Foundation::IInspectable StringFormatter::ConvertBack(Windows::Foundation::IInspectable const& /* value */, Windows::UI::Xaml::Interop::TypeName const& /* targetType */, Windows::Foundation::IInspectable const& /* parameter */, hstring const& /* language */)
    {
        throw hresult_not_implemented();
    }
}
...
public ref class StringFormatter sealed : Windows::UI::Xaml::Data::IValueConverter
{
public:
    virtual Platform::Object^ Convert(Platform::Object^ value, TypeName targetType, Platform::Object^ parameter, Platform::String^ language)
    {
        // Retrieve the value as a Calendar.
        Windows::Globalization::Calendar^ valueAsCalendar = dynamic_cast<Windows::Globalization::Calendar^>(value);

        std::wstringstream wstringstream;
        wstringstream << L"Released: ";
        wstringstream << valueAsCalendar->MonthAsNumericString()->Data();
        wstringstream << L"/" << valueAsCalendar->DayAsString()->Data();
        wstringstream << L"/" << valueAsCalendar->YearAsString()->Data();
        return ref new Platform::String(wstringstream.str().c_str());
    }

    // No need to implement converting back on a one-way binding
    virtual Platform::Object^ ConvertBack(Platform::Object^ value, TypeName targetType, Platform::Object^ parameter, Platform::String^ language)
    {
        throw ref new Platform::NotImplementedException();
    }
};
...

Observação

Para a listagem de código C++/WinRT acima, no StringFormatter.idl, usamos o atributo default para declarar IValueConverter como a interface padrão. Na listagem, StringFormatter tem apenas um construtor e nenhum método, portanto, nenhuma interface padrão é gerada para ele. O atributo default é ideal se não fores adicionar membros de instância ao StringFormatter, porque nenhum QueryInterface será necessário para chamar os métodos do IValueConverter. Como alternativa, pode solicitar que uma interface padrão IStringFormatter seja gerada, e faz isso anotando a própria classe de tempo de execução com o atributo default_interface. A opção é ideal se adicionar membros de instância a StringFormatter que são chamados mais frequentemente do que os métodos de IValueConverter, pois assim não será necessário utilizar o QueryInterface para chamar os membros de instância.

Agora podemos adicionar uma instância de StringFormatter como um recurso de página e usá-la na associação do TextBlock que exibe a propriedade ReleaseDateTime .

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

Como você pode ver acima, para flexibilidade de formatação, usamos a marcação para passar uma string de formato para o conversor por meio do parâmetro conversor. Nos exemplos de código mostrados neste tópico, somente o conversor de valor C# usa esse parâmetro. Mas você pode facilmente passar uma cadeia de caracteres de formato estilo C++ como o parâmetro do conversor e usá-la em seu conversor de valor com uma função de formatação como wprintf ou swprintf.

Aqui está o resultado.

exibir uma data com formatação personalizada

Observação

A partir do Windows 10, versão 1607, a estrutura XAML inclui um conversor integrado de Booleano para Visibility. O conversor mapeia verdadeiro para o valor de enumeração Visibility.Visible e falso para Visibility.Collapsed, para poderes associar uma propriedade Visibility a um Boolean sem criar um conversor. Para usar o conversor interno, a versão mínima do SDK de destino do seu aplicativo deve ser 14393 ou posterior. Você não pode usá-lo quando seu aplicativo tem como alvo versões anteriores do Windows 10. Para obter mais informações sobre as versões de destino, consulte o Código Adaptativo de Versão .

Ver também