Compartir a través de


Introducción al enlace de datos de Windows

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

Requisitos previos

En este tema suponemos que sabe cómo crear una aplicación básica de SDK para Windows App. Para obtener instrucciones sobre cómo crear la primera aplicación de SDK de Windows App, consulte Creación del primer proyecto de WinUI 3 (SDK de Windows App).

Creación del proyecto

Cree un nuevo proyecto de C# de aplicación en blanco empaquetado (WinUI 3 en escritorio). Llámale "Quickstart".

Enlace a un solo elemento

Cada enlace consta de un destino de enlace y de 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). Este ejemplo muestra cómo enlazar un control a un solo elemento. El destino es la propiedad Text de TextBlock. El origen es una instancia de una clase simple denominada Recording que representa una grabación de audio. Veamos primero la clase.

Agregue una nueva clase al proyecto y asigne un nombre a la clase 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 Recording();
        public Recording DefaultRecording { get { return defaultRecording; } }
    }
}

Después, expón la clase de origen de enlace desde la clase que representa la ventana de marcado. Eso lo haremos agregando una propiedad de tipo RecordingViewModel a MainWindow.xaml.cs.

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

La última parte es enlazar un TextBlock a la propiedad ViewModel.DefaultRecording.OneLineSummary.

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

Este es el resultado.

Binding a textblock

Enlazar a una colección de elementos.

Un escenario común es enlazar a una colección de objetos profesionales. En C#, la clase ObservableCollection<T> genérica es una buena elección de colección para el enlace de datos porque implementa las interfaces INotifyPropertyChanged y INotifyCollectionChanged. Estas interfaces proporcionan notificación de cambios a los enlaces cuando los elementos se añaden o se eliminan o cuando cambia una propiedad de la lista en sí. Si quieres que los controles enlazados se actualicen con los cambios en las propiedades de objetos de la colección, el objeto profesional también debe implementar INotifyPropertyChanged. Para obtener más información, consulta el tema Enlace de datos en profundidad.

El siguiente ejemplo enlaza una ListView a una colección de objetos Recording. Empecemos agregando la colección a nuestro modelo de vista. Solo tienes que agregar estos miembros nuevos a la clase RecordingViewModel.

public class RecordingViewModel
{
    ...
    private ObservableCollection<Recording> recordings = new ObservableCollection<Recording>();
    public ObservableCollection<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) });
    }
}

Después, enlaza un control ListView a la propiedad ViewModel.Recordings.

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

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

Binding a list view 1

Para solucionar este problema, podemos tanto invalidar ToString para devolver el valor de OneLineSummary, o podemos proporcionar una plantilla de datos. La opción de la plantilla de datos es una solución más habitual y más flexible. Estableces una plantilla de datos mediante la propiedad ContentTemplate de un control de contenido o la propiedad ItemTemplate de un control de elementos. Hay dos formas en las que podríamos diseñar una plantilla de datos para Recording 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>

Binding a list view 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>

Binding a list view 3

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

Agregar una vista de detalles

Puedes elegir mostrar todos los detalles de objetos Recording en los elementos de ListView. Pero eso ocupa una gran cantidad de espacio. En su lugar, puedes mostrar solo los datos suficientes en el elemento para identificarlo y luego, cuando el usuario realiza una selección, se pueden mostrar todos los detalles del elemento seleccionado en una parte independiente de la interfaz de usuario conocida como la vista de detalles. Este tipo de organización es también conocido como una vista maestra/detallada o una vista de lista/detalles.

Esto se puede llevar a cabo de dos maneras. Puedes enlazar la vista de detalles a la propiedad SelectedItem de la ListView. O puedes usar CollectionViewSource, en cuyo caso enlaza tanto ListView como la vista de detalles a CollectionViewSource (que se encargará en tu lugar del elemento actualmente seleccionado). A continuación se muestran ambas técnicas y proporcionan los mismos resultados (que 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 te mostramos a continuación requieren la extensión más flexible (pero menos eficaz) Extensión de marcado {Binding}.

Primero, esta es la técnica SelectedItem. Para una aplicación de C#, el único cambio necesario es para el marcado.

<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 la técnica CollectionViewSource, primero agrega un CollectionViewSource como recurso de ventana.

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

Y luego ajusta los enlaces en la ListView (a la que ya no necesitas llamar) y en la vista de detalles usa el CollectionViewSource. Ten en cuenta al enlazar la lista de detalles directamente al CollectionViewSource, estás dando por sentado que quieres enlazar el elemento actual a los enlaces donde la ruta de acceso no se encuentra en la propia colección. No es necesario especificar la propiedad CurrentItem como la ruta de acceso del enlace, aunque sí puedes hacerlo si encuentras 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.

Binding a list view 4

Formato o conversión de valores de datos para mostrar

Hay un problema con la representación anterior. La propiedad ReleaseDateTime no es solo una fecha, es dateTime. Por lo tanto, se muestra con más precisión de la que necesitamos. Una solución es agregar una propiedad de cadena para la clase Recording que devuelve el equivalente de ReleaseDateTime.ToString("d"). Darle a esa propiedad el nombre ReleaseDate indicaría que devuelve una fecha, y no una fecha y hora. Darle el nombre ReleaseDateAsString indicaría, además, que devuelve una cadena.

Una solución más flexible es usar algo conocido como un convertidor de valores. Este es un ejemplo de cómo crear tu propio convertidor de valores. Agregue el siguiente código al archivo de código fuente 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();
    }
}

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

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

Como puedes ver más arriba, para la flexibilidad de los formatos, usamos el marcado para pasar una cadena de formato al convertidor mediante el parámetro de convertidor. En el ejemplo de código que muestra este tema, el convertidor de valores de C# hace uso de ese parámetro.

Este es el resultado.

displaying a date with custom formatting

Consulte también