共用方式為


建立數據系結

如果您已設計具有預留位置影像和樣板文字的 UI,本教學課程將示範如何使用資料繫結將其連線到實際資料。 學習格式化資料、保持 UI 和資料同步,以及提高程式碼可維護性。

在本教學課程中,您將瞭解如何以資料繫結取代樣板,並在 UI 與資料之間建立其他直接連結。 您也會瞭解如何格式化或轉換資料以供顯示,以及讓 UI 和資料保持同步。當您完成本教學課程時,您可以改善 XAML 和 C# 程式碼的簡單性和組織性,使其更易於維護和擴充。

您可以從 PhotoLab 範例的簡化版本開始。 這個入門版本包含完整的數據層加上基本的 XAML 頁面配置,並排除許多功能,讓程式代碼更容易流覽。 本教學課程不會建置到完整的應用程式,因此請務必查看最終版本,以查看自定義動畫和調適型配置等功能。 您可以在 Windows-appsample-photo-lab 存放庫的根資料夾中找到最終版本。

PhotoLab 範例應用程式有兩個頁面。 主頁面 顯示相簿檢視,並且附有關於每個圖片檔案的一些資訊。

PhotoLab 應用程序主頁的屏幕截圖,顯示帶有圖像元數據的照片庫視圖。

當您選取相片後,詳細資料頁面會顯示該單一相片。 彈出式編輯功能表可讓您變更、重新命名和儲存相片。

PhotoLab 應用程式詳細資訊頁面的螢幕截圖,顯示一張帶有編輯選項的相片。

先決條件

第 0 部分:從 GitHub 取得入門程序代碼

在本教學課程中,您會從 PhotoLab 範例的簡化版本開始。

  1. 移至範例的 GitHub 頁面:https://github.com/Microsoft/Windows-appsample-photo-lab

  2. 接下來,您需要複製或下載範例。 選取 複製或下載 按鈕。 子菜單出現。 PhotoLab 範例的 GitHub 頁面上的複製或下載選單

    如果您不熟悉 GitHub:

    一。 選取 [下載 ZIP 並將檔案儲存在本機。 此動作會下載包含您需要的所有專案檔案的 .zip 檔案。

    b。 擷取檔案。 使用 [檔案總管] 流覽至您剛才下載的 .zip 檔案,以滑鼠右鍵按鍵按鍵按鍵按鍵,然後選取 [[全部解壓縮...]

    丙. 請瀏覽至您本機的範例副本,然後進入 Windows-appsample-photo-lab-master\xaml-basics-starting-points\data-binding 目錄。

    如果您熟悉 GitHub:

    一。 在本機複製儲存庫的主要分支。

    b。 流覽至 Windows-appsample-photo-lab\xaml-basics-starting-points\data-binding 目錄。

  3. 雙擊 Photolab.sln 在Visual Studio中開啟解決方案。

第 1 部分:取代佔位符

在本節中,您會在資料範本 XAML 中建立一次性繫結,以顯示實際影像和影像中繼資料,而不是預留位置內容。

一次性繫結適用於唯讀、不變的資料。 它們具有高效能而且易於建立,因此您可以在 GridViewListView 控制項中顯示大型資料集。

將占位符替換為一次性綁定

  1. 開啟 xaml-basics-starting-points\data-binding 資料夾,並在 Visual Studio 中啟動 PhotoLab.sln 檔案。

  2. 請確定您的 解決方案平臺 已設定為 x86 或 x64,而非 Arm,然後執行應用程式。 在繫結尚未新增之前,此步驟會顯示具有 UI 預留位置的應用程式狀態。

    執行帶有佔位圖片和文字的應用程式

  3. 開啟MainPage.xaml並搜尋名為 DataTemplate。 您將更新此範本以使用資料系結。

    之前:

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate">
    

    x:Key 值由 ImageGridView 使用來選取此範本以顯示資料物件。

  4. x:DataType 值新增至範本。

    之後:

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
    

    x:DataType 表示此範本適用於哪種類型。 在此情況下,這是一個適用於 ImageFileInfo 類別的模板(其中 local: 表示本機命名空間,如文件開頭附近的 xmlns 宣告中所定義)。

    當在資料範本中使用x:Bind運算式時,您需要x:DataType,如下所述。

  5. DataTemplate中,尋找名為 ImageItemImage 元素,並取代其 Source 值,如圖所示。

    之前:

    <Image x:Name="ItemImage"
           Source="/Assets/StoreLogo.png"
           Stretch="Uniform" />
    

    之後:

    <Image x:Name="ItemImage"
           Source="{x:Bind ImageSource}"
           Stretch="Uniform" />
    

    x:Name 識別 XAML 元素,使您能夠在 XAML 和後置代碼的其他地方參考該元素。

    x:Bind 表示式會從 數據物件 屬性取得值,以提供值給 UI 屬性。 在範本中,指示的屬性是所設定的 x:DataType 的屬性。 因此,在此情況下,數據源是 ImageFileInfo.ImageSource 屬性。

    備註

    x:Bind 值也讓編輯器知道數據類型,因此您可以使用 IntelliSense,而不是在 x:Bind 表示式中輸入屬性名稱。 請在您剛貼上的程式碼上試試看:將游標放在 x:Bind 之後,然後按空格鍵以查看您可以繫結的屬性清單。

  6. 以相同方式取代其他UI控制件的值。 (請嘗試使用 IntelliSense 執行這項操作,而不是複製/貼上!)

    之前:

    <TextBlock Text="Placeholder" ... />
    <StackPanel ... >
        <TextBlock Text="PNG file" ... />
        <TextBlock Text="50 x 50" ... />
    </StackPanel>
    <muxc:RatingControl Value="3" ... />
    

    之後:

    <TextBlock Text="{x:Bind ImageTitle}" ... />
    <StackPanel ... >
        <TextBlock Text="{x:Bind ImageFileType}" ... />
        <TextBlock Text="{x:Bind ImageDimensions}" ... />
    </StackPanel>
    <muxc:RatingControl Value="{x:Bind ImageRating}" ... />
    

執行應用程式以查看它到目前為止的外觀。 不再有佔位符! 你開頭很不錯。

使用實際影像和文字執行應用程式,而不是佔位符

備註

如果您想要進一步實驗,請嘗試將新的 TextBlock 新增至數據範本,並使用 x:Bind IntelliSense 技巧來尋找要顯示的屬性。

在本節中,您會在頁面 XAML 中建立一次性繫結,以將資源庫檢視連線到影像集合。 這些繫結會取代後端程式碼中的現有程序程式碼。 您也可以建立「 刪除」 按鈕,以查看從集合中移除影像時圖庫檢視的變更。 同時,您將瞭解如何將事件繫結至事件處理常式,以取得比傳統事件處理常式更大的彈性。

到目前為止所涵蓋的所有系結都位於數據範本內,並參考 x:DataType 值所指示類別的屬性。 您頁面中其餘的 XAML 呢?

x:Bind 資料範本之外的表達式一律繫結至頁面本身。 這表示您可以參考您在後置代碼或 XAML 中宣告的任何項目,包括自訂屬性和頁面上其他 UI 控制項的屬性(只要它們具有 x:Name 值)。

在 PhotoLab 範例中,您可以使用類似這樣的繫結,將主 GridView 控制項直接連線到影像集合,而不是在程式碼後置中執行。 稍後,您會看到其他範例。

將主要 GridView 控件系結至 Images 集合

  1. 在 MainPage.xaml.cs 中,尋找 GetItemsAsync 方法,並移除設定 ItemsSource的程序代碼。

    之前:

    ImageGridView.ItemsSource = Images;
    

    之後:

    // Replaced with XAML binding:
    // ImageGridView.ItemsSource = Images;
    
  2. 在 MainPage.xaml 中,找到名為 GridViewImageGridView,並添加 ItemsSource 屬性。 對於值來說,請使用在後置程式碼中實作的 x:Bind 屬性的 Images 表達式。

    之前:

    <GridView x:Name="ImageGridView"
    

    之後:

    <GridView x:Name="ImageGridView"
              ItemsSource="{x:Bind Images}"
    

    Images 屬性的類型為 ObservableCollection<ImageFileInfo>,因此 GridView 中顯示的個別項目類型為 ImageFileInfo。 此類型符合 x:DataType 第 1 部分中所述的值。

您稍早看到的所有繫結都是一次性的唯讀繫結,而這是純 x:Bind 表達式的預設行為。 資料僅在初始化時載入,這可實現高效能繫結 - 非常適合支援大型資料集的多個複雜視圖。

即便是您剛才新增的 ItemsSource 系結,它也是一次性、只讀的系結至不會改變的屬性值,但這裡有一個需要注意的重要區別。 Images 屬性的未變更值是集合的單一特定實例,初始化一次,如下所示。

private ObservableCollection<ImageFileInfo> Images { get; }
    = new ObservableCollection<ImageFileInfo>();

Images屬性值永遠不會變更,但因為屬性是 ObservableCollection<T>類型 ,所以集合的內容可能會變更,而且繫結會自動注意變更並更新 UI。

若要測試此行為,請暫時新增刪除目前選取影像的按鈕。 此按鈕不在最終版本中,因為選取影像會帶您前往詳細資料頁面。 不過,在最終的 PhotoLab 範例中,的 ObservableCollection<T> 行為仍然很重要,因為 XAML 會在頁面建構函式中初始化 (透過 InitializeComponent 方法呼叫),但 Images 集合稍後會在方法中 GetItemsAsync 填入。

新增刪除按鈕

  1. 在MainPage.xaml中,尋找名為 CommandBar,並在縮放按鈕之前新增按鈕。 (縮放控制目前還無法運作。您將在教程的下一部分中接入這些控制。)

    <AppBarButton Icon="Delete"
                  Label="Delete selected image"
                  Click="{x:Bind DeleteSelectedImage}" />
    

    如果您已經熟悉 XAML,這個 Click 值看起來可能不尋常。 在舊版的 XAML 中,您必須使用特定事件處理程式簽章將這個設定為方法,通常包括事件傳送者和事件特定自變數對象的參數。 當您需要事件自變數時,您仍然可以使用這項技術,但使用 x:Bind,您也可以連線到其他方法。 例如,如果您不需要事件數據,您可以連線到沒有參數的方法,就像我們在這裡所做的一樣。

  2. 在 MainPage.xaml.cs 中,新增 DeleteSelectedImage 方法。

    private void DeleteSelectedImage() =>
        Images.Remove(ImageGridView.SelectedItem as ImageFileInfo);
    

    這個方法只會從 Images 集合中刪除選取的影像。

現在執行應用程式,並使用 按鈕來刪除一些影像。 如您所見,由於數據系結和 ObservableCollection<T> 類型,UI 會自動更新。

備註

此程式代碼只會從執行中應用程式的 ImageFileInfo 集合中刪除 Images 實例。 它不會從電腦刪除圖像檔案。

第3部分:設定縮放滑桿

在此部分中,您將從數據範本中的控制項建立單向系結至位於範本外部的縮放滑桿。 您也將瞭解,您可以搭配許多控件屬性使用數據系結,而不只是最明顯的控件屬性,例如 TextBlock.TextImage.Source

將影像數據範本系結至縮放滑桿

  • 尋找名為 DataTemplateImageGridView_DefaultItemTemplate,然後取代範本頂端 **Height** 控制項的 WidthGrid 的值。

    之前

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
        <Grid Height="200"
              Width="200"
              Margin="{StaticResource LargeItemMargin}">
    

    之後

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
        <Grid Height="{Binding Value, ElementName=ZoomSlider}"
              Width="{Binding Value, ElementName=ZoomSlider}"
              Margin="{StaticResource LargeItemMargin}">
    

您是否注意到這些是 Binding 表示式,而不是 x:Bind 表達式? 這是執行數據系結的舊方式,而且大部分已經過時。 x:Bind 幾乎做了 Binding 所做的一切,而且更多。 不過,當您在數據範本中使用 x:Bind 時,它會系結至 x:DataType 值中所宣告的類型。 那麼,如何將範本中的某個元素綁定到頁面 XAML 或後置程式碼中的某些元素? 您必須使用舊版 Binding 表達式。

Binding 表達式無法辨識 x:DataType 值,但這些 Binding 表示式具有幾乎相同運作方式的 ElementName 值。 這些會告訴繫結引擎,繫結值 是對於頁面上指定元素的 Value 屬性的繫結(也就是說,具有該 x:Name 值的元素)。 如果您想要在後端程式碼中繫結一個屬性,看起來類似於 {Binding MyCodeBehindProperty, ElementName=page},其中 page 參考 XAML 中 x:Name 元素中所設定的 Page 值。

備註

根據預設,Binding 表示式是單方式,這表示當系結屬性值變更時,它們會自動更新UI。

相反地,x:Bind 的預設值是一時間,這表示會忽略系結屬性的任何變更。 這是預設值,因為它是效能最高的選項,而且大部分系結都是靜態只讀數據。

這裡的教訓是,如果您使用 x:Bind 搭配可變更其值的屬性,請務必新增 Mode=OneWayMode=TwoWay。 您將在下一節中看到此範例。

執行應用程式,並使用滑桿來變更影像範本維度。 如您所見,效果相當強大,不需要太多程序代碼。

執行具有縮放滑桿的應用程式,顯示

備註

如需挑戰,請嘗試將其他UI屬性系結至縮放滑桿 Value 屬性,或系結至您在縮放滑桿之後新增的其他滑桿。 例如,您可以將 FontSizeTitleTextBlock 屬性系結至預設值為 24的新滑桿。 請務必設定合理的最小值和最大值。

第4部分:改善縮放體驗

在此部分中,您會將自定義 ItemSize 屬性新增至程式代碼後置,並從映像範本建立單向系結至新屬性。 縮放滑桿和其他因素會更新 ItemSize 值,例如 [符合螢幕大小] 切換和視窗大小,以取得更精細的體驗。

不同於內建控件屬性,您的自定義屬性不會自動更新UI,即使使用單向和雙向系結也一樣。 它們適用於一時間 系結,但如果您想要讓屬性變更實際顯示在 UI 中,您需要執行一些工作。

建立 ItemSize 屬性,使其更新 UI

  1. 在 MainPage.xaml.cs 中,變更 MainPage 類別的簽名,使其實作 INotifyPropertyChanged 介面。

    之前:

    public sealed partial class MainPage : Page
    

    之後:

    public sealed partial class MainPage : Page, INotifyPropertyChanged
    

    這會通知綁定系統,MainPage 有一個 PropertyChanged 事件(即將新增),綁定可以接收該事件以更新使用者介面。

  2. PropertyChanged 事件新增至 MainPage 類別。

    public event PropertyChangedEventHandler PropertyChanged;
    

    此事件提供 INotifyPropertyChanged 介面所需的完整實作。 不過,若要讓它有任何效果,您必須在自定義屬性中明確引發 事件。

  3. 新增 ItemSize 屬性,並在其設值器中觸發 PropertyChanged 事件。

    public double ItemSize
    {
        get => _itemSize;
        set
        {
            if (_itemSize != value)
            {
                _itemSize = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ItemSize)));
            }
        }
    }
    private double _itemSize;
    

    ItemSize 屬性會揭露私用 _itemSize 欄位的值。 透過使用這樣的支援欄位,可以讓屬性在引發潛在不必要的 PropertyChanged 事件之前,檢查新值是否與舊值相同。

    此事件本身是由方法 Invoke 引發的。 問號會檢查 PropertyChanged 事件是否為 null,也就是是否已新增任何事件處理程式。 每個單向或雙向系結都會在幕後新增事件處理程式,但如果沒有人接聽,這裡就不會再發生任何動作。 不過,如果 PropertyChanged 不是 null,則會使用事件來源(由 Invoke 關鍵詞表示,指的是頁面本身)的參考來呼叫 this,並使用一個 event-args 物件來表示屬性名稱。 透過這項資訊,ItemSize 屬性的任何單向或雙向系結都會收到任何變更的通知,以便更新系結的UI。

  4. 在MainPage.xaml中,尋找名為 DataTemplateImageGridView_DefaultItemTemplate,並取代範本頂端 Height 控件的 WidthGrid 值。 (如果您在本教學課程的上一個部分中執行了控件對控件綁定,唯一的變更是將 Value 取代為 ItemSize,將 ZoomSlider 取代為 page。請務必針對 HeightWidth執行這項操作!)

    之前

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
        <Grid Height="{Binding Value, ElementName=ZoomSlider}"
            Width="{Binding Value, ElementName=ZoomSlider}"
            Margin="{StaticResource LargeItemMargin}">
    

    之後

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
        <Grid Height="{Binding ItemSize, ElementName=page}"
              Width="{Binding ItemSize, ElementName=page}"
              Margin="{StaticResource LargeItemMargin}">
    

現在 UI 可以回應 ItemSize 變更,您確實需要做出一些改變。 如先前所述,ItemSize 值是從各種UI控件的目前狀態計算,但每當這些控件變更狀態時,都必須執行計算。 若要這樣做,您將使用事件系結,讓特定 UI 變更會呼叫可更新 ItemSize的協助程式方法。

更新 ItemSize 屬性值

  1. DetermineItemSize 方法新增至 MainPage.xaml.cs。

    private void DetermineItemSize()
    {
        if (FitScreenToggle != null
            && FitScreenToggle.IsOn == true
            && ImageGridView != null
            && ZoomSlider != null)
        {
            // The 'margins' value represents the total of the margins around the
            // image in the grid item. 8 from the ItemTemplate root grid + 8 from
            // the ItemContainerStyle * (Right + Left). If those values change,
            // this value needs to be updated to match.
            int margins = (int)this.Resources["LargeItemMarginValue"] * 4;
            double gridWidth = ImageGridView.ActualWidth -
                (int)this.Resources["DefaultWindowSidePaddingValue"];
            double ItemWidth = ZoomSlider.Value + margins;
            // We need at least 1 column.
            int columns = (int)Math.Max(gridWidth / ItemWidth, 1);
    
            // Adjust the available grid width to account for margins around each item.
            double adjustedGridWidth = gridWidth - (columns * margins);
    
            ItemSize = (adjustedGridWidth / columns);
        }
        else
        {
            ItemSize = ZoomSlider.Value;
        }
    }
    
  2. 在 MainPage.xaml 中,瀏覽至檔案頂端,並將 SizeChanged 事件繫結新增到 Page 元素。

    之前:

    <Page x:Name="page"
    

    之後:

    <Page x:Name="page"
          SizeChanged="{x:Bind DetermineItemSize}"
    
  3. 尋找名為 SliderZoomSlider (在 Page.Resources 區段中),並新增 ValueChanged 事件系結。

    之前:

    <Slider x:Name="ZoomSlider"
    

    之後:

    <Slider x:Name="ZoomSlider"
            ValueChanged="{x:Bind DetermineItemSize}"
    
  4. 尋找名為 ToggleSwitchFitScreenToggle,並新增 Toggled 事件系結。

    之前:

    <ToggleSwitch x:Name="FitScreenToggle"
    

    之後:

    <ToggleSwitch x:Name="FitScreenToggle"
                  Toggled="{x:Bind DetermineItemSize}"
    

執行應用程式,並使用縮放滑桿和 [貼合螢幕] 切換來變更圖片範本尺寸。 如您所見,最新的變更可讓您更精細的縮放/重設大小體驗,同時讓程式代碼保持井然有序。

啟用自適應螢幕功能 運行應用程式

備註

如需挑戰,請嘗試在 TextBlock 之後新增 ZoomSlider,並將 Text 屬性系結至 ItemSize 屬性。 因為它不在數據範本中,所以您可以使用 x:Bind,而不是 Binding,像先前 ItemSize 系結一樣。

第 5 部分:啟用使用者編輯

在這裡,您將建立雙向系結,讓使用者更新值,包括影像標題、評等和各種視覺效果。

為了達成此目的,您將更新現有的 DetailPage,以提供單一影像查看器、縮放控件和編輯UI。

首先,您需要附加 DetailPage,這樣當使用者在畫廊檢視中點擊影像時,應用程式會導覽至該 DetailPage

附加 DetailPage

  1. 在 MainPage.xaml 中,尋找名為 GridViewImageGridView。 若要使項目可點選,請將 IsItemClickEnabled 設為 True 並新增 ItemClick 事件處理程式。

    小提示

    如果您輸入下列變更,而不是複製/貼上,您會看到 IntelliSense 快顯顯示「<新事件處理程式>」。 如果您按下 Tab 鍵,它會以預設方法處理程式名稱填入值,並自動將下一個步驟中顯示的方法存根。 接著,您可以按 F12 瀏覽至後置程式碼中的方法。

    之前:

    <GridView x:Name="ImageGridView">
    

    之後:

    <GridView x:Name="ImageGridView"
              IsItemClickEnabled="True"
              ItemClick="ImageGridView_ItemClick">
    

    備註

    我們在這裡使用傳統的事件處理程式,而不是 x:Bind 表達式。 這是因為我們需要看到事件數據,如下所示。

  2. 在MainPage.xaml.cs中,新增事件處理程式(或填入,如果您在最後一個步驟中使用了提示建議)。

    private void ImageGridView_ItemClick(object sender, ItemClickEventArgs e)
    {
        this.Frame.Navigate(typeof(DetailPage), e.ClickedItem);
    }
    

    這個方法簡單地導航到詳細頁面,並傳入已點選的專案,而該專案是由 ImageFileInfo 使用的 物件,用於初始化頁面。 您不需要在本教學課程中實作該方法,但您可以查看其用途。

  3. (選擇性)刪除或註解掉您在先前播放點中為當前所選影像新增的任何控制項。 保留它們不會有任何害處,但現在不進入詳細頁面就很難選取影像。

既然您已連線這兩個頁面,請執行應用程式並查看。 除了編輯窗格上的控件以外,所有功能都正常運作,當您嘗試更改值時,控件沒有反應。

如您所見,標題文本框會顯示標題,並可讓您輸入變更。 您必須將焦點變更為另一個控件來認可變更,但畫面左上角的標題尚未更新。

所有控制元件都已使用我們在第 1 部分所涵蓋的簡單 x:Bind 表示式進行綁定。 回想一下,這表示這些系結都是一次性使用,這就解釋了為什麼無法檢測到值的變更。 若要修正此問題,我們只需要將它們轉換成雙向系結。

讓編輯控制件成為互動式控制件

  1. 在 DetailPage.xaml 中,尋找名為 TextBlockTitleTextBlock,並尋找在它後面的 RatingControl 控件,並更新它們的 x:Bind 表達式以包含 Mode=TwoWay

    之前:

    <TextBlock x:Name="TitleTextBlock"
               Text="{x:Bind item.ImageTitle}"
               ... >
    <muxc:RatingControl Value="{x:Bind item.ImageRating}"
                            ... >
    

    之後:

    <TextBlock x:Name="TitleTextBlock"
               Text="{x:Bind item.ImageTitle, Mode=TwoWay}"
               ... >
    <muxc:RatingControl Value="{x:Bind item.ImageRating, Mode=TwoWay}"
                            ... >
    
  2. 針對評分控件之後的所有效果滑桿執行相同的動作。

    <Slider Header="Exposure"    ... Value="{x:Bind item.Exposure, Mode=TwoWay}" ...
    <Slider Header="Temperature" ... Value="{x:Bind item.Temperature, Mode=TwoWay}" ...
    <Slider Header="Tint"        ... Value="{x:Bind item.Tint, Mode=TwoWay}" ...
    <Slider Header="Contrast"    ... Value="{x:Bind item.Contrast, Mode=TwoWay}" ...
    <Slider Header="Saturation"  ... Value="{x:Bind item.Saturation, Mode=TwoWay}" ...
    <Slider Header="Blur"        ... Value="{x:Bind item.Blur, Mode=TwoWay}" ...
    

如您所預期的那樣,雙向模式表示每當任一端有變更時,數據都會向兩個方向移動。

如同先前涵蓋的單向系結,這些雙向系結現在會在系結屬性變更時更新UI,這要歸功於 INotifyPropertyChanged 類別中的 ImageFileInfo 實作。 不過,透過雙向系結,每當使用者與控件互動時,值也會從UI移至系結屬性。 XAML 端不需要更多。

執行應用程式並嘗試編輯控制件。 如您所見,當您進行變更時,它現在會影響影像值,當您巡覽回主頁面時,這些變更會持續存在。

第 6 部分:透過函式系結格式化值

最後一個問題仍然存在。 當您移動效果滑桿時,它們旁邊的標籤仍然不會變更。

帶有預設標籤值的效果滑桿

本教學課程的最後一個部分是新增系結,以格式化要顯示的滑桿值。

將效果滑桿的標籤連結起來,並將值格式化以供顯示

  1. TextBlock 滑桿後面尋找 Exposure,並將 Text 值取代為此處顯示的系結表達式。

    之前:

    <Slider Header="Exposure" ... />
    <TextBlock ... Text="0.00" />
    

    之後:

    <Slider Header="Exposure" ... />
    <TextBlock ... Text="{x:Bind item.Exposure.ToString('N', culture), Mode=OneWay}" />
    

    這稱為函式系結,因為您要系結至方法的傳回值。 您必須通過頁面的程式代碼後置或 x:DataType 類型來存取方法(如果您位於數據範本中)。 在此情況下,方法是熟悉的 .NET ToString 方法,這個方法是透過頁面的項目屬性來存取,然後再透過項目的 Exposure 屬性來存取。 (這說明如何系結至連線鏈結中深度巢狀的方法和屬性。

    函式綁定是格式化顯示數值的理想方式,因為您可以將其他綁定來源作為方法的參數傳入,而綁定表達式會像單向模式一樣監聽這些數值的變化。 在此範例中,文化特性 自變數是程序代碼後置中實作之未變更字段的參考,但它可能同樣容易成為引發 PropertyChanged 事件的屬性。 在此情況下,屬性值的任何變更都會導致 x:Bind 表達式使用新值呼叫 ToString,然後再使用結果更新使用者介面。

  2. 針對為其他效果滑桿加上標籤的 TextBlock執行相同動作。

    <Slider Header="Temperature" ... />
    <TextBlock ... Text="{x:Bind item.Temperature.ToString('N', culture), Mode=OneWay}" />
    
    <Slider Header="Tint" ... />
    <TextBlock ... Text="{x:Bind item.Tint.ToString('N', culture), Mode=OneWay}" />
    
    <Slider Header="Contrast" ... />
    <TextBlock ... Text="{x:Bind item.Contrast.ToString('N', culture), Mode=OneWay}" />
    
    <Slider Header="Saturation" ... />
    <TextBlock ... Text="{x:Bind item.Saturation.ToString('N', culture), Mode=OneWay}" />
    
    <Slider Header="Blur" ... />
    <TextBlock ... Text="{x:Bind item.Blur.ToString('N', culture), Mode=OneWay}" />
    

現在當您執行應用程式時,所有功能皆可運作,包括滑桿標籤。

效果滑桿搭配可操作的標籤

Binding 和 x:Bind 之間的差異

在 UWP 應用程式的 XAML 中建立資料繫結時,您可以選擇 Bindingx:Bind。 以下是主要區別:

  • x:Bind:提供編譯時間驗證、更好的效能,並且是強型別。 它最適合在編譯階段已知資料結構的案例。
  • Binding:為動態場景提供運行時評估和更大的靈活性,例如在運行時確定數據結構時。

x:Bind 不支援的案例

雖然效率很高,但 x:Bind 在某些情況下有局限性:

  • 動態資料結構:x:Bind在運行時確定資料結構時不能使用。
  • 元素對元素繫結x:Bind不支援兩個UI元素之間的直接繫結。
  • DataContext 繼承:與Binding不同,x:Bind不會自動繼承父元素的DataContext
  • 雙向繫結x:Bind 支援雙向繫結,允許變更從UI流回來源屬性。 若要讓 UI 在來源屬性變更時更新 (單向或雙向繫結),您必須在資料物件上實作 INotifyPropertyChanged

如需更多詳細資訊和範例,請參閱下列資源:

結論

本教學課程讓您對資料繫結有初步瞭解,並展示一些可用的功能。 在我們結束前有一個提醒:並非所有項目都可以綁定,有時您嘗試連接的值與您嘗試綁定的屬性不相容。 在系結方面有很多彈性,但並非在所有情況下都能運作。

系結未解決問題的其中一個範例是當控件沒有適當的屬性要系結時,如同詳細數據頁面縮放功能一樣。 此縮放滑桿必須與顯示影像的 ScrollViewer 互動,但 ScrollViewer 只能透過其 ChangeView 方法來更新。 在此情況下,我們會使用傳統的事件處理程式,讓 ScrollViewer 和縮放滑桿保持同步;如需詳細資訊,請參閱 ZoomSlider_ValueChanged 中的 MainImageScroll_ViewChangedDetailPage 方法。

不過,系結是一種強大且靈活的方式,可簡化程序代碼,並讓您的UI邏輯與數據邏輯分開。 這可讓您更輕鬆地調整此分割的任一端,同時降低在另一端引入 Bug 的風險。

UI 和數據區隔的其中一個範例是使用 ImageFileInfo.ImageTitle 屬性。 此屬性 (和 ImageRating 屬性) 與您在第 4 部分中建立的 ItemSize 屬性稍有不同,因為值會儲存在檔案元數據中(透過 ImageProperties 類型公開),而不是在欄位中。 此外,如果檔案元數據中沒有標題,ImageTitle 會傳回 ImageName 值(設定為檔名)。

public string ImageTitle
{
    get => String.IsNullOrEmpty(ImageProperties.Title) ? ImageName : ImageProperties.Title;
    set
    {
        if (ImageProperties.Title != value)
        {
            ImageProperties.Title = value;
            var ignoreResult = ImageProperties.SavePropertiesAsync();
            OnPropertyChanged();
        }
    }
}

如您所見,setter 會更新 ImageProperties.Title 屬性,然後呼叫 SavePropertiesAsync,將新值寫入檔案。 (這是個異步方法,但我們無法在屬性中使用 await 關鍵詞,而且您也不應該這樣做,因為屬性的 getter 和 setter 應該立即完成。因此,您應該呼叫這個方法,並忽略其返回的 Task 物件。)

更進一步

既然您已完成此實驗室,您就有足夠的系結知識可自行解決問題。

如您所注意到,如果您在詳細頁面上變更縮放層級,當您返回上一頁然後再次選取相同的影像時,縮放層級會自動重設。 您可以找出如何個別保留和還原每個影像的縮放層級嗎? 祝你好運!

您應該可以在本教學課程中獲得所有需要的資訊,但如果您需要更多指引,資料繫結文件只需點一下即可取得。 從這裡開始: