Windows 數據系結概觀
本主題說明如何將控件(或其他UI元素)系結至單一專案,或將專案控件系結至Windows App SDK 應用程式中的專案集合。 此外,我們會示範如何控制項目的渲染、根據選取項目實作詳細檢視,以及轉換數據以供顯示。 如需詳細資訊,請參閱 深入探討數據綁定。
先決條件
本主題假設您知道如何建立基本的 Windows App SDK 應用程式。 如需建立第一個 Windows App SDK 應用程式的指示,請參閱 建立您的第一個 WinUI 3 (Windows App SDK) 專案。
建立專案
建立新的 空白應用程式、已封裝 (WinUI 3 in Desktop) C# 專案。 將它命名為 「Quickstart」。
系結至單一項目
每個系結都包含系結目標和系結來源。 一般而言,目標是控件或其他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>
我們尚未為 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 項目中顯示
有兩種方式可以解決此問題。 您可以將詳細檢視系結至 ListView的 SelectedItem 屬性。 或者,您可以使用 CollectionViewSource,在這種情況下,您會將 ListView
和詳細檢視綁定到 CollectionViewSource
(這樣會自動為您處理當前選取的項目)。 這兩種技術如下所示,而且兩者都提供相同的結果(如下圖所示)。
注意
到目前為止在本主題中,我們只使用了更具彈性(但效能較低)的 {Binding} 標記延伸,但以下兩種技術都需要更有彈性的 {x:Bind} 標記延伸。
首先,這裡是 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
新增為頂層 Grid
的資源。
<Grid.Resources>
<CollectionViewSource x:Name="RecordingsCollection" Source="{x:Bind ViewModel.Recordings}"/>
</Grid.Resources>
注意
WinUI 中的 Window 類別沒有 Resources
屬性。 您可以將 CollectionViewSource
新增至最上層的 Grid
(或其他上層 UI 元素,例如 StackPanel
)中。 如果您在 Page
內工作,您可以將 CollectionViewSource
新增至 Page.Resources
。
然後調整 ListView 上的系結(現已無需命名),並在詳細檢視中使用 CollectionViewSource。 請注意,藉由將詳細檢視系結直接設定至 CollectionViewSource
,表示您想要系結至系結中的目前項目,而此時無法在集合本身中找到路徑。 您不需要將 CurrentItem
屬性指定為系結的路徑,不過如果有任何模棱兩可的情況,您可以這麼做。
...
<ListView ItemsSource="{Binding Source={StaticResource RecordingsCollection}}">
...
<StackPanel DataContext="{Binding Source={StaticResource RecordingsCollection}}" ...>
...
每個案例的結果都相同。
格式化或轉換要顯示的數據值
上述轉譯有問題。
ReleaseDateTime
屬性不只是日期,它是 DateTime。 因此,它顯示的精確度比我們需要的要高。 其中一個解決方案是將字串屬性加入 Recording
類別,以傳回對等的 ReleaseDateTime.ToString("d")
。 命名該屬性 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
系結中使用。
<Grid.Resources>
...
<local:StringFormatter x:Key="StringFormatterValueConverter"/>
</Grid.Resources>
...
<TextBlock Text="{Binding ReleaseDateTime,
Converter={StaticResource StringFormatterValueConverter},
ConverterParameter=Released: \{0:d\}}"/>
...
如上所示,針對格式化彈性,我們會使用標記,透過轉換器參數將格式字串傳遞至轉換器。 在本主題所示的程式代碼範例中,C# 值轉換器會使用該參數。
以下是結果。