Condividi tramite


Panoramica del data binding di Windows

Questo argomento illustra come associare un controllo (o un altro elemento dell'interfaccia utente) a un singolo elemento o associare un controllo elementi a una raccolta di elementi in un'app di Windows App SDK. Viene inoltre illustrato come controllare il rendering degli elementi, implementare una visualizzazione dettagli in base a una selezione e convertire i dati per la visualizzazione. Per informazioni più dettagliate, vedere Binding dei dati nel dettaglio.

Prerequisiti

Questo argomento presuppone che tu sappia come creare un'applicazione di base con Windows App SDK. Per istruzioni sulla creazione della prima app di Windows App SDK, vedere Creare il primo progetto WinUI 3 (Windows App SDK).

Creare il progetto

Creare una nuova App vuota , impacchettata (WinUI 3 in Desktop) in C#. Denominarlo "Avvio rapido".

Associazione a un singolo elemento

Ogni associazione è costituita da un target di associazione e un'origine di associazione. In genere, la destinazione è una proprietà di un controllo o di un altro elemento dell'interfaccia utente e l'origine è una proprietà di un'istanza della classe (un modello di dati o un modello di visualizzazione). In questo esempio viene illustrato come associare un controllo a un singolo elemento. La destinazione è la proprietà Text di un TextBlock. L'origine è un'istanza di una classe semplice denominata Recording che rappresenta una registrazione audio. Esaminiamo prima la classe.

Aggiungere una nuova classe al progetto e denominare la 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 Recording();
        public Recording DefaultRecording { get { return defaultRecording; } }
    }
}

Esporre quindi la classe di origine dell'associazione dalla classe che rappresenta la finestra di markup. A questo scopo, aggiungere una proprietà di 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; }
    }
}

L'ultima parte consiste nell'associare una TextBlock alla proprietà ViewModel.DefaultRecording.OneLineSummary.

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

Ecco il risultato.

Rilegatura di un blocco di testo

Associazione a una raccolta di elementi

Uno scenario comune consiste nell'associare una raccolta di oggetti aziendali. In C#, la classe generica ObservableCollection<T> è una scelta ottimale per l'associazione di dati, perché implementa le interfacce INotifyPropertyChanged e INotifyCollectionChanged. Queste interfacce forniscono una notifica di modifica alle associazioni quando gli elementi vengono aggiunti o rimossi o una proprietà dell'elenco stesso cambia. Se si desidera che i controlli associati vengano aggiornati con le modifiche apportate alle proprietà degli oggetti nella raccolta, l'oggetto business deve implementare anche INotifyPropertyChanged. Per altre informazioni, vedere Approfondimento sul Data binding.

Nell'esempio seguente viene associato un ListView a un insieme di oggetti Recording. Per iniziare, aggiungere la raccolta al modello di visualizzazione. È sufficiente aggiungere questi nuovi membri alla classe 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) });
    }
}

Associare quindi un ListView alla proprietà .

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

Non abbiamo ancora fornito un modello di dati per la classe Recording, quindi il meglio che il framework dell'interfaccia utente può fare è chiamare ToString per ogni elemento nel ListView. L'implementazione predefinita di ToString consiste nel restituire il nome del tipo.

Collegamento di una vista elenco 1

Per risolvere questo problema, è possibile eseguire l'override di ToString per restituire il valore di OneLineSummaryoppure è possibile fornire un modello di dati. L'opzione modello di dati è una soluzione più comune e una più flessibile. È possibile specificare un modello di dati utilizzando la proprietà ContentTemplate di un controllo contenuto o la proprietà ItemTemplate di un controllo elementi. Ecco due modi per progettare un modello di dati per Recording insieme a un'illustrazione del risultato.

<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>

Collegamento di una vista elenco 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>

Collegamento di una vista elenco 3

Per altre informazioni sulla sintassi XAML, vedere Creare un'interfaccia utente con XAML. Per altre informazioni sul layout dei controlli, vedere Definire layout con XAML.

Aggiunta di una visualizzazione dettagliata

È possibile scegliere di visualizzare tutti i dettagli degli oggetti Recording in elementi ListView. Ma questo occupa un sacco di spazio. È invece possibile visualizzare dati sufficienti nell'elemento per identificarlo e quindi, quando l'utente effettua una selezione, è possibile visualizzare tutti i dettagli dell'elemento selezionato in una parte separata dell'interfaccia utente nota come visualizzazione dei dettagli. Questa disposizione è nota anche come visualizzazione principale/dettagli o visualizzazione elenco/dettagli.

Ci sono due modi per procedere. È possibile associare la visualizzazione dei dettagli alla proprietà SelectedItem di ListView. Oppure puoi usare un CollectionViewSource, nel qual caso associ sia il ListView che la visualizzazione dei dettagli al CollectionViewSource (facendo così gestisce automaticamente l'elemento attualmente selezionato per te). Entrambe le tecniche sono illustrate di seguito e forniscono entrambi gli stessi risultati (mostrati nella figura).

Nota

Finora in questo argomento è stata usata solo l'estensione di markup {x:Bind} , ma entrambe le tecniche illustrate di seguito richiedono la maggiore flessibilità (ma meno efficiente) estensione di markup {Binding}.

Prima di tutto, ecco la tecnica SelectedItem. Per un'applicazione C#, l'unica modifica necessaria è il markup.

<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>

Per la tecnica di CollectionViewSource, aggiungere prima un CollectionViewSource come risorsa del Griddi primo livello.

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

Nota

La classe Window in WinUI non ha una proprietà Resources. È possibile aggiungere il CollectionViewSource all'elemento Grid di primo livello (o ad altro elemento dell'interfaccia utente padre, ad esempio StackPanel). Se stai lavorando all'interno di un Page, puoi aggiungere il CollectionViewSource al Page.Resources.

Modificare quindi le associazioni nel ListView (che non deve più essere denominato) e nella visualizzazione dei dettagli per usare CollectionViewSource. Si noti che associando la visualizzazione dei dettagli direttamente alla CollectionViewSource, si implica che si vuole eseguire l'associazione all'elemento corrente nelle associazioni in cui non è possibile trovare il percorso nella raccolta stessa. Non è necessario specificare la proprietà CurrentItem come percorso per l'associazione, anche se è possibile farlo in caso di ambiguità.

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

Ed ecco il risultato identico in ogni caso.

Associare una visualizzazione elenco 4

Formattazione o conversione di valori di dati per la visualizzazione

Si è verificato un problema con il rendering precedente. La proprietà ReleaseDateTime non è solo una data, è un DateTime. Quindi, viene visualizzato con maggiore precisione rispetto a quanto necessario. Una soluzione consiste nell'aggiungere una proprietà stringa alla classe Recording che restituisce l'equivalente di ReleaseDateTime.ToString("d"). La denominazione di tale proprietà ReleaseDate indicherà che restituisce una data e non una data e ora. Denominarlo ReleaseDateAsString indicherà ulteriormente che restituisce una stringa.

Una soluzione più flessibile consiste nell'usare un convertitore di valori noto come convertitore di valori. Ecco un esempio di come creare un convertitore di valori personalizzato. Aggiungi il codice seguente al file di codice sorgente 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();
    }
}

È ora possibile aggiungere un'istanza di StringFormatter come risorsa e usarla nel binding di TextBlock che visualizza la proprietà ReleaseDateTime.

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

Come si può notare in precedenza, per la flessibilità di formattazione viene usato il markup per passare una stringa di formato nel convertitore tramite il parametro del convertitore. Nell'esempio di codice illustrato in questo argomento, il convertitore di valori C# usa tale parametro.

Ecco il risultato.

visualizzare una data con formattazione personalizzata