共用方式為


Windows 資料系結概觀

本主題說明如何在 Windows 應用程式 SDK 應用程式中將控制項 (或其他 UI 元素) 繫結到單一項目,或將項目控制項繫結到項目集合。 此外,我們還會說明如何控制項目的呈現、根據選擇來實作詳細資料檢視、以及轉換資料以供顯示。 如需詳細資訊,請參閱深入了解資料繫結

必要條件

本主題假設您知道如何建立基本的 Windows 應用程式 SDK 應用程式。 如需建立第一個 Windows 應用程式 SDK 應用程式的指示,請參閱建立您的第一個 WinUI 3 (Windows 應用程式 SDK) 專案

建立專案

建立新的空白應用程式,封裝 (傳統型 WinUI 3) C# 專案。 將其命名為「快速入門」。

繫結到單一項目

每個繫結是由繫結目標和繫結來源所組成。 通常,目標是控制項或其他 UI 元素的屬性,而來源是類別執行個體 (資料模型或檢視模型) 的屬性。 這個範例示範如何將控制項繫結到單一項目。 目標是 TextBlockText 屬性。 來源是名為 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>

結果如下。

系結 textblock

繫結到項目集合

常見的一個情況是繫結到商業物件的集合。 在 C# 中,ObservableCollection<T> 泛型類別是適用於資料繫結的集合選擇,因為它實作 INotifyPropertyChangedINotifyCollectionChanged 介面。 當加入或移除項目,或清單本身的屬性變更時,這些介面會向繫結提供變更通知。 如果您希望繫結控制項隨著集合中物件屬性的變更一起更新,那麼商業物件也應該實作 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 中的每個項目,呼叫 ToStringToString 的預設實作是傳回類型名稱。

繫結清單檢視 1

若要解決這個問題,我們可以覆寫 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>

繫結清單檢視 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>

繫結清單檢視 3

如需 XAML 語法的詳細資訊,請參閱使用 XAML 建立 UI。 如需控制項配置的詳細資訊,請參閱定義使用 XAML 的版面配置

新增詳細資料檢視

您可以選擇在 ListView 項目中顯示 Recording 物件的所有詳細資料。 但這會佔用大量空間。 相反地,您可以在項目中顯示剛好足夠識別它的資料,然後當使用者做出選擇時,您可以在另一個稱為詳細資料檢視的 UI 中,顯示選定項目的所有詳細資料。 這種安排也稱為主要/詳細資料檢視,或清單/詳細資料檢視。

共有兩種可以著手。 您可以將詳細資料檢視繫結到 ListViewSelectedItem 屬性。 或者,您可以使用 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}}" ...>
...

以下是各種情況的相同結果。

繫結清單檢視 4

格式化或轉換資料值以供顯示

以上轉譯的結果有一個問題。 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# 值轉換器會使用該參數。

結果如下。

顯示具有自定義格式的日期

另請參閱