共用方式為


Windows 數據系結概觀

WinUI 應用程式中的資料繫結可讓您有效率地將控制項連線到資料來源。 瞭解如何將控制項繫結至單一專案或專案集合、控制專案轉譯、實作詳細資料檢視,以及設定資料格式以供顯示。 如需詳細資訊,請參閱 深入資料繫結

先決條件

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

建立專案

建立新的 WinUI 空白應用程式,封裝的 C# 專案。 將它命名為 「Quickstart」。

繫結至單一項目

每個系結都包含系結目標和系結來源。 一般而言,目標是控件或其他UI元素的屬性,而來源是類別實例的屬性(資料模型或檢視模型)。 這個範例示範如何將控件系結至單一專案。 目標是 TextTextBlock 屬性。 來源是名為 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();
        public Recording DefaultRecording { get { return defaultRecording; } }
    }
}

接下來,從代表您的標記窗口的類別中,公開綁定來源類別。 將類型的 RecordingViewModel 屬性新增至 MainWindow.xaml.cs

namespace Quickstart
{
    public sealed partial class MainWindow : Window
    {
        public MainWindow()
        {
            this.InitializeComponent();
        }
        public RecordingViewModel ViewModel{ get; } = new RecordingViewModel();
    }
}

最後一個部分是將 TextBlock 系結至 ViewModel.DefaultRecording.OneLineSummary 屬性。

<Window x:Class="Quickstart.MainWindow" ... >
    <Grid>
        <TextBlock Text="{x:Bind ViewModel.DefaultRecording.OneLineSummary}"
                   HorizontalAlignment="Center"
                   VerticalAlignment="Center"/>
    </Grid>
</Window>

以下是結果。

WinUI 應用程式的螢幕擷取畫面,顯示系結至單一專案的 TextBlock。

繫結至專案集合

常見的情境是繫結至業務對象的集合。 在 C# 中,使用泛型 ObservableCollection<T> 類別進行資料繫結。 它會實作 INotifyCollectionChanged 介面,當新增或移除專案時,會提供繫結的變更通知。 不過,由於 .NET 8 和更新版本的已知 WinUI 發行模式錯誤,在某些情況下您可能需要使用 清單<T> ,特別是如果您的集合是靜態的,而且初始化之後不會變更。 如果您的 UI 需要在執行階段集合變更時更新,請使用 ObservableCollection<T>。 如果您只需要顯示一組固定的項目, List<T> 就足夠了。 此外,如果您想要系結控制項更新為集合中物件屬性的變更,這些物件應該實作 INotifyPropertyChanged。 如需詳細資訊,請參閱 深入數據綁定

注意

使用 List<T>,您可能不會收到集合變更的變更通知。 如果您需要回應變更,請考慮使用 ObservableCollection<T>。 在此範例中,您不需要回應集合變更,因此 List<T> 就足夠了。

下列範例會將 ListView 繫結至物件集合 Recording 。 首先,將集合新增至您的檢視模型。 將這些新成員新增至班級 RecordingViewModel

public class RecordingViewModel
{
    ...
    private List<Recording> recordings = new();
    public List<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 預設實作會傳回類型名稱。

綁定列表檢視 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。 此方法會為您處理目前選取的項目。 以下各節顯示了這兩種技術,它們都給出了相同的結果(如圖所示)。

注意

到目前為止,在本主題中,您只使用了 {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 新增為頂層 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的實例新增為資源,並在顯示TextBlock屬性時使用綁定ReleaseDateTime來利用它。

<Grid.Resources>
    ...
    <local:StringFormatter x:Key="StringFormatterValueConverter"/>
</Grid.Resources>
...
<TextBlock Text="{Binding ReleaseDateTime,
    Converter={StaticResource StringFormatterValueConverter},
    ConverterParameter=Released: \{0:d\}}"/>
...

如您所見,為了格式化的靈活性,標記通過轉換器參數將格式字符串傳遞到轉換器中。 在本主題所示的程式代碼範例中,C# 值轉換器會使用該參數。

以下是結果。

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

Binding 和 x:Bind 之間的差異

在 WinUI 應用程式中使用資料繫結時,您可能會遇到兩個主要繫結機制: Bindingx:Bind。 雖然兩者都用於將 UI 元素連接到資料來源,但它們有明顯的差異:

  • x:Bind:提供編譯時間檢查、更好的效能,並且是強型別。 它非常適合您在編譯階段知道資料結構的案例。
  • Binding:提供執行階段評估,且針對動態案例更靈活,例如在編譯階段不知道資料結構時。

x:Bind 不支援的案例

雖然功能強大,但 x:Bind 在某些情況下您無法使用它:

  • 動態資料結構:如果資料結構在編譯階段未知,則無法使用 x:Bind
  • 元素對元素繫結x:Bind 不支援直接在兩個 UI 元素之間繫結。
  • 繫結至 DataContextx:Bind 不會自動繼承父元素的DataContext屬性。
  • Mode=TwoWay向繫結:雖然支援,x:Bind然而,無論使用單向或雙向繫結,針對您想要 UI 在來源變更時更新的任何屬性都需明確實現INotifyPropertyChanged。 雙向繫結的主要差異在於,變更也會從 UI 流回來源。

如需實際範例,以及更深入地了解何時使用它們,請參閱下列主題: