Compartir a través de


Información general sobre la vinculación de datos

En este tema se muestra cómo enlazar un control (u otro elemento de interfaz de usuario) a un solo elemento o enlazar un control de elementos a una colección de elementos de una aplicación para la Plataforma universal de Windows (UWP). Además, mostramos cómo controlar la representación de elementos, implementar una vista de detalles basada en una selección y convertir datos para mostrarlos. Para obtener información más detallada, consulte Vinculación de datos en profundidad.

Prerrequisitos

En este tema se supone que sabes cómo crear una aplicación básica para UWP. Para obtener instrucciones sobre cómo crear tu primera aplicación para UWP, consulta Introducción a las aplicaciones de Windows.

Creación del proyecto

Cree un nuevo proyecto Aplicación vacía (Windows Universal). Asígnelo el nombre "Inicio rápido".

Vinculación a un solo elemento

Cada enlace consta de un destino de enlace y un origen de enlace. Normalmente, el destino es una propiedad de un control u otro elemento de interfaz de usuario, y el origen es una propiedad de una instancia de clase (un modelo de datos o un modelo de vista). En este ejemplo se muestra cómo enlazar un control a un solo elemento. El destino es la propiedad Text de un TextBlock. El origen es una instancia de una clase simple denominada Recording que representa una grabación de audio. Echemos un vistazo primero a la clase.

Si usa C# o C++/CX, agregue una nueva clase al proyecto y asigne un nombre a la clase Recording.

Si usas de C++/WinRT, agrega nuevos Archivo Midl (.idl) elementos al proyecto, denominados como se muestra en la lista de ejemplo de código de C++/WinRT siguiente. Reemplace el contenido de esos nuevos archivos por el código MIDL 3.0 que se muestra en la lista, compile el proyecto para generar Recording.h y .cpp y y , y RecordingViewModel.h.cppagregue código a los archivos generados para que coincidan con la lista. Para obtener más información sobre esos archivos generados y cómo copiarlos en el proyecto, consulta los "controles XAML" en ; y "enlace a una propiedad de C++/WinRT" en.

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"

A continuación, exponga la clase de origen de la vinculación desde la clase que representa su página de marcado. Para ello, agregamos una propiedad de tipo RecordingViewModel a MainPage.

Si usa C++/WinRT, actualice MainPage.idlprimero . Construye el proyecto para regenerar MainPage.h y .cpp, e integra los cambios en esos archivos generados en los de tu proyecto.

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();
}

La última pieza es enlazar un TextBlock a la propiedad 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>

Si utilizas de C++/WinRT, necesitas eliminar la función MainPage::ClickHandler para que el proyecto se compile.

Este es el resultado.

Encuadernación de un bloque de texto

Vinculación a una colección de elementos

Un escenario común es enlazar a una colección de objetos empresariales. En C# y Visual Basic, la clase ObservableCollection<T> es una buena opción de colección para el enlace de datos, ya que implementa las interfaces INotifyPropertyChanged y INotifyCollectionChanged. Estas interfaces proporcionan una notificación de cambio a los enlaces cuando se agregan o quitan elementos o cambia una propiedad de la propia lista. Si desea que los controles enlazados se actualicen con cambios en las propiedades de los objetos de la colección, el objeto de negocio también debe implementar INotifyPropertyChanged. Para obtener más información, consulta Vinculación de datos en profundidad .

Si usas C++/WinRT, puedes aprender más sobre la vinculación a una colección observable en los controles de elementos de XAML ; vincularte a una colección de C++/WinRT. Si lee primero ese tema, la intención de la lista de código de C++/WinRT que se muestra a continuación será más clara.

En este siguiente ejemplo se enlaza un ListView a una colección de Recording objetos. Comencemos agregando la colección al modelo de visualización. Solo tiene que agregar estos nuevos miembros a la clase 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;
        };
    }
};

A continuación, enlaza un ListView a la propiedad ViewModel.Recordings.

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

Todavía no hemos proporcionado una plantilla de datos para la clase Recording de , por lo que lo mejor que puede hacer el marco de interfaz de usuario es llamar a toString para cada elemento de la ListView. La implementación predeterminada de ToString es devolver el nombre de tipo.

Enlazar una vista de lista 1

Para solucionar esto, podemos invalidar ToString para devolver el valor de OneLineSummary o podemos proporcionar una plantilla de datos. La opción de plantilla de datos es una solución más habitual y una más flexible. Puede especificar una plantilla de datos mediante la propiedad ContentTemplate de un control de contenido o la propiedad ItemTemplate de un control de elementos. Aquí hay dos maneras de diseñar una plantilla de datos para Grabación junto con una ilustración del 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>

Vinculación de una lista de vista 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>

Vinculación de una ListView 3

Para obtener más información sobre la sintaxis XAML, consulta Crear una interfaz de usuario con XAML. Para obtener más información sobre el diseño de control, consulta Definir diseños con XAML.

Adición de una vista de detalles

Puede elegir mostrar todos los detalles de los objetos de grabación en los elementos de ListView. Pero eso ocupa mucho espacio. En su lugar, puede mostrar solo suficientes datos en el elemento para identificarlo y, a continuación, cuando el usuario realiza una selección, puede mostrar todos los detalles del elemento seleccionado en una parte independiente de la interfaz de usuario conocida como la vista de detalles. Esta disposición también se conoce como una vista maestra o de detalles, o una vista de lista o detalles.

Hay dos maneras de hacerlo. Puede enlazar la vista de detalles a la propiedad SelectedItem del ListView. O bien, puedes usar un CollectionViewSource, en cuyo caso enlazas tanto el ListView como la vista de detalles a la CollectionViewSource (de este modo, se encarga del elemento seleccionado actualmente). Ambas técnicas se muestran a continuación y proporcionan los mismos resultados (se muestran en la ilustración).

Nota:

Hasta ahora, en este tema, solo hemos usado la extensión de marcado {x:Bind}, pero ambas técnicas que mostraremos a continuación requieren la extensión de marcado {Binding}, que es más flexible pero menos eficaz, .

Si usa extensiones de componentes de C++/WinRT o Visual C++ (C++/CX), para usar el extensión de marcado {Binding}, deberá agregar el atributo BindableAttribute a cualquier clase en tiempo de ejecución a la que quiera enlazar. Para usar {x:Bind}, no necesita ese atributo.

Importante

Si usas de C++/WinRT, el atributo BindableAttribute está disponible si has instalado windows SDK versión 10.0.17763.0 (Windows 10, versión 1809) o posterior. Sin ese atributo, deberá implementar las interfaces ICustomPropertyProvider y ICustomProperty para poder usar la extensión de marcado {Binding}.

En primer lugar, aquí está la técnica ElementoSeleccionado.

// 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
{
    ...
};

El único otro cambio necesario es en el marcado.

<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 la técnica CollectionViewSource, agregue primero un CollectionViewSource de como un recurso de página.

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

A continuación, ajuste los enlaces en el ListView (que ya no necesita ser nombrado) y en la vista de detalles, para usar el CollectionViewSource. Tenga en cuenta que al vincular directamente la vista de detalles a la CollectionViewSource, está indicando que desea enlazar al elemento actual en los casos donde la ruta no se puede encontrar en la colección. No es necesario especificar la propiedad CurrentItem como la ruta de acceso para la vinculación, aunque puede hacerlo si hay alguna ambigüedad.

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

Y este es el resultado idéntico en cada caso.

Nota:

Si usa C++, la interfaz de usuario no será exactamente similar a la ilustración siguiente: la representación de la propiedad ReleaseDateTime es diferente. Consulte la siguiente sección para obtener más información sobre esto.

Enlazar una vista de lista 4

Formato o conversión de valores de datos para su visualización

Hay un problema con la representación anterior. La propiedad ReleaseDateTime no es solo una fecha, es un DateTime (si está usando C++, entonces es un Calendar). Por lo tanto, en C#, se muestra con más precisión de lo que necesitamos. Y en C++ se representa como un nombre de tipo. Una solución consiste en agregar una propiedad de cadena a la clase Recording que devuelve el equivalente de this.ReleaseDateTime.ToString("d"). Asignar un nombre a esa propiedad ReleaseDate indicaría que devuelve una fecha y no una fecha y hora. Asignarle un nombre ReleaseDateAsString indicaría aún más que devuelve una cadena.

Una solución más flexible consiste en usar algo conocido como convertidor de valores. Este es un ejemplo de cómo crear su propio convertidor de valores. Si usa C#, agregue el código siguiente al Recording.cs archivo de código fuente. Si usaS C++/WinRT, agrega un nuevo elemento Midl File (.idl) al proyecto, denominado como se muestra en la lista de código de C++/WinRT siguiente, compila el proyecto para generar StringFormatter.h y .cpp, agrega esos archivos al proyecto y, a continuación, pega los listados de código en ellos. También agregue #include "StringFormatter.h" a 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();
    }
};
...

Nota:

Para la lista de código de C++/WinRT anterior, en StringFormatter.idl, usamos el atributo predeterminado para declarar IValueConverter como la interfaz predeterminada. En la lista, StringFormatter solo tiene un constructor y ningún método, por lo que no se genera ninguna interfaz predeterminada para él. El atributo default es óptimo si no va a agregar miembros de instancia a StringFormatter, ya que no se requerirá QueryInterface para llamar a los métodos de IValueConverter. Como alternativa, puede solicitar que se genere una interfaz predeterminada de IStringFormatter, y para ello, debe anotar la clase de tiempo de ejecución con el atributo default_interface. Esta opción es óptima si agrega miembros de instancia a StringFormatter a los que se llama con más frecuencia que a los métodos de IValueConverter, ya que no se requerirá el uso de QueryInterface para llamar a los miembros de instancia.

Ahora podemos agregar una instancia de StringFormatter como un recurso de página y usarla en el enlace del TextBlock que muestra la propiedad ReleaseDateTime.

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

Como puede ver anteriormente, para la flexibilidad de formato, usamos el marcado para pasar una cadena de formato al convertidor mediante el parámetro del convertidor. En los ejemplos de código que se muestran en este tema, solo el convertidor de valores de C# usa ese parámetro. Pero podría pasar fácilmente una cadena de formato de estilo C++como parámetro del convertidor y usarla en el convertidor de valores con una función de formato como wprintf o swprintf.

Este es el resultado.

mostrar una fecha con formato personalizado

Nota:

A partir de Windows 10, versión 1607, el marco XAML proporciona un convertidor booleano a visibilidad integrado. El convertidor asigna true al valor de enumeración Visibility.Visible y false a Visibility.Collapsed para que puedas enlazar una propiedad Visibility a un booleano sin tener que crear un convertidor. Para usar el convertidor integrado, la versión mínima del SDK de destino de la aplicación debe ser 14393 o posterior. No se puede usar cuando la aplicación tiene como destino versiones anteriores de Windows 10. Para obtener más información sobre las versiones de destino, consulta código adaptable a versiones.

Consulte también