具有不相符類型的資料繫結

已完成

有時候您使用的資料不符合顯示資料的控制項屬性資料類型。 例如,您可能有以您想顯示於 Label 控制項的 decimal 類型儲存的貨幣值 (格式化為貨幣)。 更複雜的範例是模組中呈現的天氣應用程式。 影像應該根據天氣預報的 SunnyCloudy 列舉值顯示,但您無法將來源的列舉值繫結至目標的影像屬性。 本單元探討您可以轉換資料的方式。

字串格式

有一個常見的類型不相符情況,就是您想要顯示為格式化字串的內建類型。 就像當您想要顯示 DateTime 值的部分,或您想要將 decimal 類型格式化為貨幣時一樣。

假設您想要在帳單上顯示到期金額,而且您的資料物件上有此屬性:

public decimal BillAmount { get; set; }

最終欠款金額為 22.0304。 您可使用兩個標籤控制項來顯示文字和貨幣金額,如以下程式碼片段所示:

<HorizontalStackLayout>
    <Label Text="You owe" Margin="0,0,5,0" />
    <Label Text="{Binding BillAmount}" />
    <Label Text="to the bank" Margin="5,0,0,0" />
</HorizontalStackLayout>

這會將字串輸出至看起來像 You owe 22.0304 to the bank 的 UI,但遺漏貨幣符號,且根據當地貨幣,可能有太多或太少的小數位數。 一般而言,您會以程式碼中的 "C" (或貨幣) 格式指定名稱將值處理為字串,如下所示:

string formattedBillAmount = string.Format("{0:C}", BillAmount);

但是若要在資料繫結中使用格式設定,您必須讓資料物件提供格式化字串作為不同的屬性,或以某種方式攔截它並自行格式化。 幸運的是,.NET MAUI 繫結提供一種方式,以 StringFormat 繫結屬性來格式化字串。 格式字串會遵循與 String.Format 方法相同的規則。 您需以單引號括住格式字串,才不會因大括弧而造成 XAML 剖析器產生混淆。 字串格式參數 0 是由繫結所處理的屬性值。

<Label Text="{Binding BillAmount, StringFormat='You owe {0:C} to the bank'}" />

請考慮下列 XAML,其示範如何使用兩種方式來顯示 BillAmount

<VerticalStackLayout Padding="10">
    <HorizontalStackLayout>
        <Label Text="You owe" Margin="0,0,5,0" />
        <Label Text="{Binding BillAmount}" />
        <Label Text="to the bank" Margin="5,0,0,0" />
    </HorizontalStackLayout>
    <HorizontalStackLayout>
        <Label Text="{Binding BillAmount, StringFormat='You owe {0:C} to the bank'}" />
    </HorizontalStackLayout>
</VerticalStackLayout>

下圖說明 XAML 輸出在畫面上產生的內容:

.NET MAUI Android 應用程式的螢幕擷取畫面,其中顯示兩個不同的標籤控制項 (包含與積欠帳單相關的文字)。一個標籤的文字會以美元貨幣格式化。

使用 StringFormat 繫結屬性的 XAML 比其他 XAML 簡單,而且您可存取 .NET 的強大字串格式系統。

自訂類型轉換

當您將值顯示為字串時,StringFormat 繫結屬性很方便,但是當您想要從另一個類型轉換 ColorImage 之類的項目時,則不方便。 在這些情況下,您必須撰寫自訂轉換程式碼。

假設您提示使用者選擇密碼,而且您想要在 UI 中使用色彩來指出密碼的強度。 強度有三個等級:弱、好、強。 強度是以密碼中的字元數為基礎。 若要立即向使用者提供其密碼強度的意見反應,您希望包含密碼的 Entry 控制項背景根據強度變更。 下圖示範這三種等級的強度:弱、好、強。

.NET MAUI Android 應用程式的螢幕擷取畫面,其中包含三個項目控制項,而每個控制項都有不同的彩色背景。

在螢幕擷取畫面中的三個輸入控制項中,第一個控制項已輸入四個字元並具有紅色背景。 第二個控制項已輸入九個字元並具有黃色背景。 最後一個輸入控制項有 15 個字元並具有藍色背景。

這些等級會指派給 Strength 列舉:

private enum Strength
{
    Weak,
    Good,
    Strong
}

資料物件會指派為頁面的 BindingContext,其中包含具有 PasswordStrength 屬性的密碼強度。 當使用者鍵入密碼時,PasswordStrength 屬性會根據密碼的長度變更。 因為資料物件包含 PasswordStrength 屬性,因此您可將該屬性繫結至 Entry 控制項的 BackgroundColor

<Entry BackgroundColor="{Binding PasswordStrength} ... />

不過,這裡還是有問題。 當 BackgroundColorColor 時,PasswordStrength 的類型為 Strength。 這些類型彼此不相容。 .NET MAUI 提供修正這些類型不符問題的方法,也就是繫結的 Converter 屬性。

顧名思義,繫結轉換器只會在繫結來源和目標之間轉換。 轉換器會透過 IValueConverter 介面實作。

實作 IValueConverter

您可將轉換邏輯建立到實作 IValueConverter 介面的類別中。 一般而言,這些類別的名稱是以名稱 Converter 結尾,使其用途明確。

IValueConverter 介面會定義兩種方法:

  • Convert—從繫結來源的屬性轉換成繫結目標的 UI 屬性。

  • ConvertBack—從繫結目標的 UI 屬性轉換回繫結來源的屬性。

    這個方法很少使用,而且不會在此案例中使用。 大部分轉換器都會擲回 NotSupportedException,表示不支援此轉換。

以下是介面的合約:

public interface IValueConverter
{
    object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture);
    object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture);
}

回想一下,我們正在處理的情況是將 Entry.BackgroundColor 屬性繫結至資料物件的 PasswordStrength 屬性,這是 Strength 列舉。

<Entry BackgroundColor="{Binding PasswordStrength} ... />

Strength 列舉必須轉換成 Color。 因此,轉換器必須評估已提供哪個 Strength 值,並傳回不同的 Color。 下列程式碼可示範此轉換:

namespace MyProject.Converters;

class StrengthToColorConverter : IValueConverter
{
    public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
    {
        return (Strength)value! switch
        {
            Strength.Weak => Colors.OrangeRed,
            Strength.Good => Colors.Yellow,
            Strength.Strong => Colors.LightBlue,
            _ => Colors.LightBlue
        };
    }

    public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) =>
        throw new NotImplementedException();
}

讓我們來詳細說明此程式碼:

  • Convert 方法有四個參數。 除非您有使用最後兩個參數的特定理由,否則通常可加以捨棄。
  • value 參數包含傳入值。 在此範例中,這是 Strength 列舉值。
  • targetType 參數會被忽略。 但是,您可使用此參數來驗證搭配轉換器使用的屬性類型是否為 Color。 為了簡單起見,此範例省略這點。
  • 參數運算式用於根據 Strength 值傳回不同的色彩。

在 XAML 中使用轉換器

建立轉換器類別後,您必須建立其執行個體並在繫結中加以參考。 具現化轉換器的標準方法是在根元素的資源字典中。

首先,將 XML 命名空間對應至包含轉換器的 .NET 命名空間。 在下列程式碼範例中,cvt XML 命名空間會對應至 MyProject.Converters .NET 命名空間:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:cvt="clr-namespace:MyProject.Converters"
             ...

接下來,在 ContentPage.Resources中建立執行個體:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:cvt="clr-namespace:MyProject.Converters"
             x:Class="MyProject.PasswordExample">
    <ContentPage.Resources>
        <cvt:StrengthToColorConverter x:Key="StrengthToColorConverter" />
    </ContentPage.Resources>

現在,轉換器的執行個體位於資源字典中且索引鍵為 StrengthToColorConverter,其恰好與類型同名。 這是命名轉換器的典型方式,因為您通常只要在 XAML 中重複使用單一轉換器。 如果您基於某些原因而需要多個轉換器執行個體,則其間的索引鍵必須不同。

最後,參考繫結上的轉換器。 由於轉換器位於資源字典中,因此您可使用 {StaticResource} 標記延伸來參考它。 轉換器會指派給 Converter 繫結屬性:

<Entry BackgroundColor="{Binding PasswordStrength, Converter={StaticResource StrengthToColorConverter}}" ... />

檢定您的知識

1.

若要在資料繫結中顯示值並格式化為字串,哪個才是最佳方法?

2.

資料繫結轉換會使用哪一種類型?