Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Il data binding nelle app WinUI consente di connettere in modo efficiente i controlli alle origini dati. Informazioni su come associare un controllo a un singolo elemento o a una raccolta di elementi, il rendering degli elementi di controllo, implementare visualizzazioni dettagli e formattare i dati per la visualizzazione. Per altri dettagli, vedere Data binding in modo approfondito.
Prerequisiti
Questo argomento presuppone che tu sappia come creare un'app WinUI di base con Windows App SDK. Per istruzioni sulla creazione della prima app WinUI, vedi Creare un'app WinUI.
Creare il progetto
Creare un nuovo progetto C# WinUI Blank App, confezionato. Denominarlo "Avvio rapido".
Eseguire l'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();
public Recording DefaultRecording { get { return defaultRecording; } }
}
}
Esporre quindi la classe di origine dell'associazione dalla classe che rappresenta la finestra di markup. Aggiungere una proprietà di tipo RecordingViewModel a MainWindow.xaml.cs.
namespace Quickstart
{
public sealed partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
}
public RecordingViewModel ViewModel{ get; } = new RecordingViewModel();
}
}
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.
Eseguire l'associazione a una raccolta di elementi
Uno scenario comune consiste nell'associare una raccolta di oggetti aziendali. In C# usare la classe T ObservableCollection<> generica per il data binding. Implementa l'interfaccia INotifyCollectionChanged , che fornisce una notifica di modifica ai binding quando gli elementi vengono aggiunti o rimossi. Tuttavia, a causa di un bug noto della modalità di rilascio winUI con .NET 8 e versioni successive, potrebbe essere necessario usare un elenco<T> in alcuni scenari, soprattutto se la raccolta è statica e non cambia dopo l'inizializzazione. Se l'interfaccia utente deve essere aggiornata quando la raccolta cambia in fase di esecuzione, usare ObservableCollection<T>. Se è sufficiente visualizzare solo un set fisso di elementi, List<T> è sufficiente. Inoltre, se si desidera che i controlli associati vengano aggiornati con le modifiche alle proprietà degli oggetti nell'insieme, tali oggetti devono implementare INotifyPropertyChanged. Per altre informazioni, vedere Approfondimento sul Data binding.
Nota
Usando List<T>, è possibile che non si ricevano notifiche di modifica per i cambiamenti nella raccolta. Se è necessario rispondere alle modifiche, è consigliabile usare ObservableCollection<T>. In questo esempio non è necessario rispondere alle modifiche alla raccolta, quindi List<T> è sufficiente.
Nell'esempio seguente viene associato un controllo ListView a una raccolta di Recording oggetti . Aggiungere prima di tutto la raccolta al modello di visualizzazione. Aggiungere questi nuovi membri alla 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) });
}
}
Associare quindi un controllo ListView alla ViewModel.Recordings proprietà .
<Window x:Class="Quickstart.MainWindow" ... >
<Grid>
<ListView ItemsSource="{x:Bind ViewModel.Recordings}"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</Window>
Non hai ancora fornito un modello di dati per la classe Recording, quindi la cosa migliore che il framework dell'interfaccia utente può fare è chiamare ToString per ogni elemento in ListView. L'implementazione predefinita di ToString restituisce il nome del tipo.
Per risolvere questo problema, è possibile eseguire l'override di ToString per restituire il valore di OneLineSummaryoppure fornire un modello di dati. L'opzione modello di dati è una soluzione più comune e flessibile. È possibile specificare un modello di dati utilizzando la proprietà 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>
<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>
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.
Aggiungere una visualizzazione dettagli
È possibile scegliere di visualizzare tutti i dettagli degli oggetti Recording in elementi ListView. Ma questo approccio occupa un sacco di spazio. È invece possibile visualizzare solo la quantità sufficiente di dati nell'elemento per identificarlo. Quando l'utente effettua una selezione, puoi 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.
È possibile implementare questa disposizione in due modi. È possibile associare la visualizzazione dei dettagli alla proprietà SelectedItem ListView che la visualizzazione dettagli a CollectionViewSource. Questo approccio si occupa dell'elemento attualmente selezionato. Entrambe le tecniche sono illustrate nelle sezioni seguenti e forniscono entrambi gli stessi risultati (illustrati nella figura).
Nota
Finora in questo argomento è stata usata solo l'estensione di markup {x:Bind}. Tuttavia, entrambe le tecniche illustrate nelle sezioni seguenti richiedono l'estensione di markup {Binding} più flessibile (ma meno efficiente).
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 i collegamenti in ListView (che non devono più essere denominati) e nella visualizzazione dei dettagli per usare CollectionViewSource. Associando la visualizzazione dei dettagli direttamente all'oggetto CollectionViewSource, si implica che si vuole eseguire l'associazione all'elemento corrente nelle associazioni in cui il percorso non può essere trovato 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.
Formattare o convertire i valori dei dati per la visualizzazione
Il rendering precedente presenta un problema. La ReleaseDateTime proprietà non è solo una data. Si tratta di un valore DateTime. Quindi, viene visualizzato con maggiore precisione di quanto sia 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 indica che restituisce una data e non una data e ora. Denominandolo ReleaseDateAsString indica inoltre che restituisce una stringa.
Una soluzione più flessibile consiste nell'usare un convertitore di valori. Ecco un esempio di come creare un convertitore di valori personalizzato. Aggiungere il codice seguente al file 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 nell'associazione di TextBlock che visualizza la ReleaseDateTime proprietà .
<Grid.Resources>
...
<local:StringFormatter x:Key="StringFormatterValueConverter"/>
</Grid.Resources>
...
<TextBlock Text="{Binding ReleaseDateTime,
Converter={StaticResource StringFormatterValueConverter},
ConverterParameter=Released: \{0:d\}}"/>
...
Come si può notare, per la flessibilità di formattazione, il markup passa 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.
Differenze tra binding e x:Bind
Quando si usa il data binding nelle app WinUI, è possibile che si verifichino due meccanismi di associazione principali: Binding e x:Bind. Anche se entrambi servono allo scopo di connettere gli elementi dell'interfaccia utente alle origini dati, presentano differenze distinte:
-
x:Bind: offre un controllo in fase di compilazione, migliori prestazioni ed è fortemente tipizzato. È ideale per gli scenari in cui si conosce la struttura dei dati in fase di compilazione. -
Binding: fornisce la valutazione di runtime ed è più flessibile per gli scenari dinamici, ad esempio quando la struttura dei dati non è nota in fase di compilazione.
Scenari non supportati da x:Bind
Sebbene x:Bind sia potente, non è possibile usarlo in determinati scenari:
-
Strutture di dati dinamiche: se la struttura dei dati non è nota in fase di compilazione, non è possibile usare
x:Bind. -
Associazione da elemento a elemento:
x:Bindnon supporta l'associazione diretta tra due elementi dell'interfaccia utente. -
L'associazione a un oggetto
DataContext:x:Bindnon eredita automaticamente l'oggettoDataContextdi un elemento padre. -
Binding bidirezionali con
Mode=TwoWay: Sebbene supportato,x:Bindrichiede l'implementazione esplicita diINotifyPropertyChangedper qualsiasi proprietà che si desidera venga aggiornata dall'interfaccia utente quando l'origine cambia, sia che si utilizzi un binding unidirezionale o bidirezionale. La differenza principale con le associazioni bidirezionali è che anche le modifiche passano dall'interfaccia utente all'origine.
Per esempi pratici e una comprensione più approfondita di quando usarli, vedere gli argomenti seguenti: