Windows 資料系結概觀
本主題說明如何在 Windows 應用程式 SDK 應用程式中將控制項 (或其他 UI 元素) 繫結到單一項目,或將項目控制項繫結到項目集合。 此外,我們還會說明如何控制項目的呈現、根據選擇來實作詳細資料檢視、以及轉換資料以供顯示。 如需詳細資訊,請參閱深入了解資料繫結。
必要條件
本主題假設您知道如何建立基本的 Windows 應用程式 SDK 應用程式。 如需建立第一個 Windows 應用程式 SDK 應用程式的指示,請參閱建立您的第一個 WinUI 3 (Windows 應用程式 SDK) 專案。
建立專案
建立新的空白應用程式,封裝 (傳統型 WinUI 3) C# 專案。 將其命名為「快速入門」。
繫結到單一項目
每個繫結是由繫結目標和繫結來源所組成。 通常,目標是控制項或其他 UI 元素的屬性,而來源是類別執行個體 (資料模型或檢視模型) 的屬性。 這個範例示範如何將控制項繫結到單一項目。 目標是 TextBlock
的 Text
屬性。 來源是名為 Recording
之簡單類別的執行個體,其代表音訊錄製。 讓我們先看一下這個類別。
將新類別新增至專案,並命名為 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; } }
}
}
接著,從代表標記視窗的類別中公開繫結來源類別。 我們藉由將類型為 RecordingViewModel
的屬性新增至MainWindow.xaml.cs,以執行此作業。
namespace Quickstart
{
public sealed partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
ViewModel = new RecordingViewModel();
}
public RecordingViewModel ViewModel{ get; set; }
}
}
最後一個部分是將 TextBlock
繫結到 ViewModel.DefaultRecording.OneLineSummary
屬性。
<Window x:Class="Quickstart.MainWindow" ... >
<Grid>
<TextBlock Text="{x:Bind ViewModel.DefaultRecording.OneLineSummary}"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</Window>
結果如下。
繫結到項目集合
常見的一個情況是繫結到商業物件的集合。 在 C# 中,ObservableCollection<T> 泛型類別是適用於資料繫結的集合選擇,因為它實作 INotifyPropertyChanged 和 INotifyCollectionChanged 介面。 當加入或移除項目,或清單本身的屬性變更時,這些介面會向繫結提供變更通知。 如果您希望繫結控制項隨著集合中物件屬性的變更一起更新,那麼商業物件也應該實作 INotifyPropertyChanged
。 如需詳細資訊,請參閱深入了解資料繫結。
下一個範例將 ListView 繫結到 Recording
物件的集合。 首先讓我們將集合加入到檢視模型。 只要將這些新成員新增至 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) });
}
}
然後將 ListView 繫結到 ViewModel.Recordings
屬性。
<Window x:Class="Quickstart.MainWindow" ... >
<Grid>
<ListView ItemsSource="{x:Bind ViewModel.Recordings}"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Window>
我們還未提供資料範本給 Recording
類別,因此 UI 架構所能做的只是針對 ListView 中的每個項目,呼叫 ToString。 ToString
的預設實作是傳回類型名稱。
若要解決這個問題,我們可以覆寫 ToString 來傳回 OneLineSummary
的值,不然就是提供資料範本。 資料範本選項是比較常見的解決辦法,而且更有彈性。 您可以使用內容控制項的 ContentTemplate 屬性或項目控制項的 ItemTemplate 屬性來指定資料範本。 以下是為 Recording
設計資料範本的兩種方式,同時提供結果的插圖。
<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>
如需 XAML 語法的詳細資訊,請參閱使用 XAML 建立 UI。 如需控制項配置的詳細資訊,請參閱定義使用 XAML 的版面配置。
新增詳細資料檢視
您可以選擇在 ListView 項目中顯示 Recording
物件的所有詳細資料。 但這會佔用大量空間。 相反地,您可以在項目中顯示剛好足夠識別它的資料,然後當使用者做出選擇時,您可以在另一個稱為詳細資料檢視的 UI 中,顯示選定項目的所有詳細資料。 這種安排也稱為主要/詳細資料檢視,或清單/詳細資料檢視。
共有兩種可以著手。 您可以將詳細資料檢視繫結到 ListView 的 SelectedItem 屬性。 或者,您可以使用 CollectionViewSource,將 ListView
和詳細資料檢視都繫結到 CollectionViewSource
(這麼做可為您處理目前選取的項目)。 以下顯示這兩種技巧,且兩者的結果相同,如圖所示。
注意
本主題到目前為止,我們只使用 {x:Bind} 標記延伸,但以下我們將說明的兩種技巧需要更有彈性 (但效能較低) 的 {Binding} 標記延伸。
首先是 SelectedItem 技術。 對於 C# 應用程式,唯一必要的變更是標記。
<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>
使用 CollectionViewSource 技術時,請先新增作為視窗資源的 CollectionViewSource
。
<Window.Resources>
<CollectionViewSource x:Name="RecordingsCollection" Source="{x:Bind ViewModel.Recordings}"/>
</Window.Resources>
然後,將 ListView (不再需要命名) 和詳細資料檢視上的繫結調整為使用 CollectionViewSource。 請注意,將詳細資料檢視直接繫結到 CollectionViewSource
時,就意味著您想要繫結至在集合本身找不到路徑之繫結中的目前項目。 不需要指定 CurrentItem
屬性作為繫結的路徑 (但如果情況模稜兩可,您可以這樣做)。
...
<ListView ItemsSource="{Binding Source={StaticResource RecordingsCollection}}">
...
<StackPanel DataContext="{Binding Source={StaticResource RecordingsCollection}}" ...>
...
以下是各種情況的相同結果。
格式化或轉換資料值以供顯示
以上轉譯的結果有一個問題。 ReleaseDateTime
屬性不僅是日期,還是 DateTime。 因此,其顯示會比我們所需的精確度更高。 一種解決辦法是將字串屬性加入會傳回 ReleaseDateTime.ToString("d")
對等項目的 Recording
類別。 將該屬性命名為 ReleaseDate
來表示其只會傳回日期,而不傳回日期和時間。 將其命名為 ReleaseDateAsString
以進一步表示傳回字串。
更彈性的解決之道是使用所謂的「值轉換器」。 以下是如何撰寫您自己的值轉換器的範例。 將下列程式碼新增至您的 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();
}
}
現在我們可以加入 StringFormatter
的執行個體作為頁面資源,並在顯示 ReleaseDateTime
屬性的 TextBlock
的繫結中使用它。
<Window.Resources>
<local:StringFormatter x:Key="StringFormatterValueConverter"/>
</Window.Resources>
...
<TextBlock Text="{Binding ReleaseDateTime,
Converter={StaticResource StringFormatterValueConverter},
ConverterParameter=Released: \{0:d\}}"/>
...
如您所見,為了讓格式化有彈性,我們會使用標記,透過轉換器參數將格式字串傳遞至轉換器。 在本主題所顯示的程式碼範例中,C# 值轉換器會使用該參數。
結果如下。