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.
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 UWP (Universal Windows Platform). 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 Analisi approfondita del Data binding.
Prerequisiti
Questo argomento presuppone che tu sappia come creare un'app UWP di base. Per istruzioni sulla creazione della prima app UWP, vedi Introduzione alle app di Windows.
Creare il progetto
Creare un nuovo progetto Applicazione Vuota (Windows Universal). Denominarlo "Avvio rapido".
Collegamento a un singolo elemento
Ogni associazione è costituita da una destinazione di associazione e da una sorgente 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. Guardiamo prima la classe.
Se si usa C# o C++/CX, aggiungere una nuova classe al progetto e denominare la classe Recording.
Se usi C++/WinRT, aggiungi nuovi file midl (.idl) elementi al progetto, denominati come illustrato nell'elenco di codice di esempio C++/WinRT riportato di seguito. Sostituire il contenuto di questi nuovi file con il codice MIDL 3.0 visualizzato nell'elenco, compilare il progetto per generare Recording.h e .cpp e RecordingViewModel.h e .cpp, quindi aggiungere codice ai file generati in modo che corrispondano all'elenco. Per ulteriori informazioni su questi file generati e su come copiarli nel tuo progetto, consulta i controlli XAML ; associa a una proprietà C++/WinRT.
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"
Successivamente, esponi la classe sorgente di associazione dalla classe che rappresenta la tua pagina di markup. Questo si fa aggiungendo una proprietà di tipo RecordingViewModel a MainPage.
Se usi C++/WinRT, aggiorna prima MainPage.idl. Compilare il progetto per rigenerare MainPage.h e .cppe unire le modifiche apportate ai file generati con quelle presenti nel progetto.
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();
}
L'ultima parte consiste nell'associare un TextBlock alla proprietà 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 stai utilizzando C++/WinRT , dovrai rimuovere la funzione MainPage::ClickHandler affinché il progetto venga compilato.
Ecco il risultato.
Collegamento a una raccolta di elementi
Uno scenario comune consiste nell'eseguire l'associazione a una raccolta di oggetti aziendali. In C# e Visual Basic, la classe generica ObservableCollection<T> è una scelta ottimale per il data binding, 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 nell'insieme, l'oggetto business deve implementare anche INotifyPropertyChanged. Per ulteriori informazioni, vedere Associazione dati in modo approfondito.
Se stai utilizzando C++/WinRT, puoi scoprire di più sul binding a una raccolta osservabile nei controlli degli elementi XAML e sull'associazione a una raccolta di C++/WinRT. Se si legge prima questo argomento, la finalità dell'elenco di codice C++/WinRT illustrato di seguito sarà più chiaro.
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 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;
};
}
};
E quindi associare un ListView alla proprietà ViewModel.Recordings.
<Page x:Class="Quickstart.MainPage" ... >
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView ItemsSource="{x:Bind ViewModel.Recordings}"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Page>
Non è ancora stato fornito un modello di dati per la classe di registrazione
Per risolvere questo problema, è possibile eseguire l'override di ToString per restituire il valore di OneLineSummary oppure 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 del contenuto o la proprietà ItemTemplate di un controllo degli elementi. Ecco due modi per creare un modello di dati per Registrazione, accompagnato da 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.
Aggiunta di una visualizzazione dettagliata
È possibile scegliere di visualizzare tutti i dettagli degli elementi Recording in 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 dell'elenco ListView. In alternativa, è possibile usare un CollectionViewSource, nel qual caso si associano sia la ListView che la visualizzazione dettagliata alla CollectionViewSource, che gestisce l'elemento attualmente selezionato per te. Entrambe le tecniche sono illustrate di seguito e forniscono entrambi gli stessi risultati (mostrati nella figura).
Annotazioni
Finora in questo argomento è stata usata solo l'estensione di markup {x:Bind} , ma entrambe le tecniche illustrate di seguito richiedono la maggiore flessibilità offerta dall'estensione di markup più flessibile (ma meno efficiente) {Binding} .
Se usi estensioni del componente C++/WinRT o Visual C++ (C++/CX), per usare l'estensione di markup {Binding}, dovrai aggiungere l'attributo BindableAttribute a qualsiasi classe di runtime a cui vuoi eseguire l'associazione. Per usare {x:Bind}, non è necessario tale attributo.
Importante
Se usi
Prima di tutto, ecco la tecnica 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
{
...
};
L'unica altra modifica necessaria è al markup.
<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>
Per la tecnica di CollectionViewSource, aggiungere prima di tutto un CollectionViewSource come risorsa di pagina.
<Page.Resources>
<CollectionViewSource x:Name="RecordingsCollection" Source="{x:Bind ViewModel.Recordings}"/>
</Page.Resources>
Modificare quindi le associazioni nel ListView (che non deve più essere denominato) e nella visualizzazione dei dettagli per usare il CollectionViewSource . Si noti che associando la visualizzazione dei dettagli direttamente alla CollectionViewSource, si intende associare 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.
Annotazioni
Se usi C++, l'interfaccia utente non sarà esattamente simile alla figura seguente: il rendering della proprietà ReleaseDateTime è diverso. Per altre informazioni, vedere la sezione seguente.
Formattazione o conversione di valori di dati per la visualizzazione
Si è verificato un problema con il rendering precedente. La proprietà ReleaseDateTime this.ReleaseDateTime.ToString("d"). La denominazione della proprietà ReleaseDate indica che restituisce una data e non una data e un'ora.
Denominarla 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. Se si usa C#, aggiungere il codice seguente al Recording.cs file di codice sorgente. Se si usa C++/WinRT, aggiungere un nuovo elemento Midl File (con estensione idl) al progetto, denominato come illustrato nell'elenco di codice C++/WinRT riportato di seguito, compilare il progetto per generare StringFormatter.h e .cpp, aggiungere tali file al progetto e quindi incollare gli elenchi di codice in essi contenuti. Aggiungi anche #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();
}
};
...
Annotazioni
Per l'elenco di codice C++/WinRT precedente, in StringFormatter.idlviene usato l'attributo predefinito per dichiarare IValueConverter come interfaccia predefinita. Nell'elenco StringFormatter è presente solo un costruttore e nessun metodo, quindi non viene generata alcuna interfaccia predefinita. L'attributo default è ottimale se non aggiungerai membri di istanza per StringFormatter, perché non sarà necessario eseguire queryInterface per chiamare i metodi IValueConverter. In alternativa, è possibile richiedere la generazione di un'interfaccia IStringFormatter predefinita annottando la classe di runtime stessa con l'attributo default_interface. Questa opzione è ottimale se si aggiungono membri dell'istanza a StringFormatter che vengono richiamati più spesso rispetto ai metodi di IValueConverter, perché quindi non sarà necessario eseguire un QueryInterface per chiamare i membri dell'istanza.
È ora possibile aggiungere un'istanza di StringFormatter come risorsa di pagina e usarla nell'associazione del TextBlock che visualizza la proprietà ReleaseDateTime.
<Page.Resources>
<local:StringFormatter x:Key="StringFormatterValueConverter"/>
</Page.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. Negli esempi di codice illustrati in questo argomento, solo il convertitore di valori C# usa tale parametro. Tuttavia, è possibile passare facilmente una stringa di formato in stile C++come parametro del convertitore e usarla nel convertitore di valori con una funzione di formattazione come wprintf o swprintf.
Ecco il risultato.
Annotazioni
A partire da Windows 10, versione 1607, il framework XAML fornisce un convertitore "Boolean-to-Visibility" predefinito. Il convertitore esegue il mapping true al valore di enumerazione Visibility.Visible e false a Visibility.Collapsed in modo da poter associare una proprietà Visibility a un valore Boolean senza creare un convertitore. Per usare il convertitore integrato, la versione minima dell'SDK di destinazione dell'app deve essere 14393 o successiva. Non puoi usarla quando la tua app è destinata a versioni precedenti di Windows 10. Per altre informazioni sulle versioni di destinazione, vedere codice adattivo per la versione.