共用方式為


Windows 數據系結概觀

本主題說明如何將控件(或其他UI元素)系結至單一專案,或將專案控件系結至Windows App SDK 應用程式中的專案集合。 此外,我們會示範如何控制項目的渲染、根據選取項目實作詳細檢視,以及轉換數據以供顯示。 如需詳細資訊,請參閱 深入探討數據綁定

先決條件

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

建立專案

建立新的 空白應用程式、已封裝 (WinUI 3 in Desktop) C# 專案。 將它命名為 「Quickstart」。

系結至單一項目

每個系結都包含系結目標和系結來源。 一般而言,目標是控件或其他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>

以下是結果。

綁定文本塊

資料綁定至項目集合

常見的情境是繫結至業務對象的集合。 在 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>

我們尚未為 類別提供數據範本,因此 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 項目中顯示 物件的所有詳細資料。 但這佔用了很多空間。 相反地,您可以在專案中顯示足夠的數據來識別它,然後在使用者進行選取時,您可以在稱為詳細數據檢視的個別 UI 中顯示所選專案的所有詳細數據。 這種安排也稱為主要/詳細數據檢視,或清單/詳細數據檢視。

有兩種方式可以解決此問題。 您可以將詳細檢視系結至 ListViewSelectedItem 屬性。 或者,您可以使用 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}}" ...>
...

每個案例的結果都相同。

繫結列表視圖 4

格式化或轉換要顯示的數據值

上述轉譯有問題。 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# 值轉換器會使用該參數。

以下是結果。

顯示具有自訂格式設定的日期