共用方式為


系結值轉換器

Browse sample. 流覽範例

.NET 多平臺應用程式 UI (.NET MAUI) 數據系結通常會將數據從來源屬性傳送至目標屬性,在某些情況下,從目標屬性傳送至來源屬性。 當來源和目標屬性都屬於相同類型,或其中一種類型可以透過隱含轉換來轉換成其他類型時,傳輸即會簡單明瞭。 若非此情況,則必須採取類型轉換。

在 String 格式設定一文中,您已瞭解如何使用StringFormat數據系結的 屬性,將任何類型轉換成字串。 對於其他類型的轉換,您需要在實作 IValueConverter 介面的類別中撰寫一些特殊程式碼。 實作 IValueConverter 的類別稱為「值轉換器」,但也經常稱為「繫結轉換器」或「繫結值轉換器」

系結值轉換器

假設您希望定義來源屬性為 int 類型但目標屬性為 bool 的資料繫結。 您希望此資料繫結在整數來源等於 0 時產生 false 值,否則產生 true。 這可以使用實作 介面的 IValueConverter 類別來達成:

public class IntToBoolConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (int)value != 0;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (bool)value ? 1 : 0;
    }
}

接著,您可以將這個類別的實例設定為 Converter 類別的 Binding 屬性,或設定為 Converter 標記延伸的 Binding 屬性。 此類別會成為資料繫結的一部分。

當資料在 OneWayTwoWay 繫結從來源移到目標時,會呼叫 Convert 方法。 value 參數是資料繫結來源的物件或值。 此方法必須傳回資料繫結目標類型的值。 這裡示範的方法會將 value 參數轉換為 int,然後將其與 0 比較,以取得 bool 傳回值。

當資料在 TwoWayOneWayToSource 繫結從目標移到來源時,會呼叫 ConvertBack 方法。 ConvertBack 會執行相反的轉換:其會假設 value 參數目標的 bool,並將其轉換成來源的 int 傳回值。

注意

如果數據系結也包含 StringFormat 設定,則會在結果格式化為字串之前叫用值轉換器。

下列範例示範如何在資料系結中使用這個值轉換器:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:DataBindingDemos"
             x:Class="DataBindingDemos.EnableButtonsPage"
             Title="Enable Buttons">
    <ContentPage.Resources>
        <local:IntToBoolConverter x:Key="intToBool" />
    </ContentPage.Resources>

    <StackLayout Padding="10, 0">
        <Entry x:Name="entry1"
               Text=""
               Placeholder="enter search term"
               VerticalOptions="Center" />
        <Button x:DataType="Entry"
                Text="Search"
                HorizontalOptions="Center"
                VerticalOptions="Center"
                IsEnabled="{Binding Source={x:Reference entry1},
                                    Path=Text.Length,
                                    Converter={StaticResource intToBool}}" />
        <Entry x:Name="entry2"
               Text=""
               Placeholder="enter destination"
               VerticalOptions="Center" />
        <Button x:DataType="Entry"
                Text="Submit"
                HorizontalOptions="Center"
                VerticalOptions="Center"
                IsEnabled="{Binding Source={x:Reference entry2},
                                    Path=Text.Length,
                                    Converter={StaticResource intToBool}}" />
    </StackLayout>
</ContentPage>

在這裡範例中,會在 IntToBoolConverter 頁面的資源字典中具現化 。 接著會使用標記延伸來參考,以在兩個 StaticResource 數據系結中設定 Converter 屬性。 在頁面上的多個數據系結之間共用數據轉換器是很常見的。 如果在應用程式的多個頁面中使用值轉換器,您可以在應用層級資源字典中將其具現化。

此範例示範當 執行作業時 Button ,根據使用者輸入檢視的文字來執行作業時, Entry 常見的需求。 Text每個 Entry 的 屬性都會初始化為空字串,因為 Text 屬性預設為 null ,而且數據系結在該情況下將無法運作。 如果 Entry 中未鍵入任何內容,則應該停用 Button。 每個 Button 都會在其 IsEnabled 屬性中包含資料繫結。 資料繫結來源是對應 EntryText 屬性的 Length 屬性。 如果該 Length 屬性不為 0,則值轉換器會傳回 true 並啟用 Button

Enable buttons.

注意

如果您知道某個值轉換器僅限用於 OneWay 繫結,則 ConvertBack 方法只能傳回 null

Convert上述方法假設value自變數的類型為 int ,且傳回值必須是 類型bool。 同樣,ConvertBack 方法會假設 value 引數是 bool 類型,且傳回值為 int。 若非此情況,則會發生執行階段例外狀況。

您可以撰寫更通用且可接受數個不同資料類型的值轉換器。 ConvertConvertBack 方法可以透過 value 參數來使用 asis 運算子,或者呼叫該參數上的 GetType 來判斷其類型,然後進行適當的操作。 每個方法的傳回值預期類型,由 targetType 參數指定。 有時候,值轉換器會與不同目標型別的數據系結搭配使用。 在此情況下,值轉換器可以使用 targetType 自變數來執行正確類型的轉換。

如果要執行的轉換因不同文化特性而有別,請使用針對此用途的 culture 參數。

系結轉換器屬性

值轉換器類別可以具有屬性和泛型參數。 下列值轉換器會將 bool 從來源轉換成目標型 T 別的物件:

public class BoolToObjectConverter<T> : IValueConverter
{
    public T TrueObject { get; set; }
    public T FalseObject { get; set; }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (bool)value ? TrueObject : FalseObject;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return ((T)value).Equals(TrueObject);
    }
}

下列範例示範如何使用這個轉換器來顯示檢視的值 Switch 。 雖然將值轉換器具現化為資源字典中的資源很常見,但此範例示範替代方法。 在這裡,每個值轉換器都會在屬性元素標記之間 Binding.Converter 具現化。 x:TypeArguments 指出泛型引數,且 TrueObjectFalseObject 都設為該類型的物件:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:DataBindingDemos"
             x:Class="DataBindingDemos.SwitchIndicatorsPage"
             Title="Switch Indicators">
    <ContentPage.Resources>
        <Style TargetType="Label">
            <Setter Property="FontSize" Value="18" />
            <Setter Property="VerticalOptions" Value="Center" />
        </Style>

        <Style TargetType="Switch">
            <Setter Property="VerticalOptions" Value="Center" />
        </Style>
    </ContentPage.Resources>

    <StackLayout Padding="10, 0">
        <StackLayout Orientation="Horizontal"
                     VerticalOptions="Center">
            <Label Text="Subscribe?" />
            <Switch x:Name="switch1" />
            <Label>
                <Label.Text>
                    <Binding x:DataType="Switch"
                             Source="{x:Reference switch1}"
                             Path="IsToggled">
                        <Binding.Converter>
                            <local:BoolToObjectConverter x:TypeArguments="x:String"
                                                         TrueObject="Of course!"
                                                         FalseObject="No way!" />
                        </Binding.Converter>
                    </Binding>
                </Label.Text>
            </Label>
        </StackLayout>

        <StackLayout Orientation="Horizontal"
                     VerticalOptions="Center">
            <Label Text="Allow popups?" />
            <Switch x:Name="switch2" />
            <Label>
                <Label.Text>
                    <Binding x:DataType="Switch"
                             Source="{x:Reference switch2}"
                             Path="IsToggled">
                        <Binding.Converter>
                            <local:BoolToObjectConverter x:TypeArguments="x:String"
                                                         TrueObject="Yes"
                                                         FalseObject="No" />
                        </Binding.Converter>
                    </Binding>
                </Label.Text>
                <Label.TextColor>
                    <Binding x:DataType="Switch"
                             Source="{x:Reference switch2}"
                             Path="IsToggled">
                        <Binding.Converter>
                            <local:BoolToObjectConverter x:TypeArguments="Color"
                                                         TrueObject="Green"
                                                         FalseObject="Red" />
                        </Binding.Converter>
                    </Binding>
                </Label.TextColor>
            </Label>
        </StackLayout>

        <StackLayout Orientation="Horizontal"
                     VerticalOptions="Center">
            <Label Text="Learn more?" />
            <Switch x:Name="switch3" />
            <Label FontSize="18"
                   VerticalOptions="Center">
                <Label.Style>
                    <Binding x:DataType="Switch"
                             Source="{x:Reference switch3}"
                             Path="IsToggled">
                        <Binding.Converter>
                            <local:BoolToObjectConverter x:TypeArguments="Style">
                                <local:BoolToObjectConverter.TrueObject>
                                    <Style TargetType="Label">
                                        <Setter Property="Text" Value="Indubitably!" />
                                        <Setter Property="FontAttributes" Value="Italic, Bold" />
                                        <Setter Property="TextColor" Value="Green" />
                                    </Style>
                                </local:BoolToObjectConverter.TrueObject>

                                <local:BoolToObjectConverter.FalseObject>
                                    <Style TargetType="Label">
                                        <Setter Property="Text" Value="Maybe later" />
                                        <Setter Property="FontAttributes" Value="None" />
                                        <Setter Property="TextColor" Value="Red" />
                                    </Style>
                                </local:BoolToObjectConverter.FalseObject>
                            </local:BoolToObjectConverter>
                        </Binding.Converter>
                    </Binding>
                </Label.Style>
            </Label>
        </StackLayout>
    </StackLayout>
</ContentPage>

在此範例中,在三Switch個 和 Label 配對的最後一個Style中,泛型自變數會設定為 ,而且會針對 和FalseObject的值TrueObject提供整個 Style 物件。 這些會覆寫設定於資源字典中 Label 的隱含樣式,因此該樣式中的屬性會明確指派給 Label。 切換 Switch 會導致對應的 Label 反映變更:

Switch indicators.

注意

您也可以使用觸發程式,根據其他檢視實作使用者介面中的變更。 如需詳細資訊,請參閱 觸發程式

係結轉換器參數

Binding 類別會定義 ConverterParameter 屬性,而 Binding 標記延伸也會定義 ConverterParameter 屬性。 如果設定此屬性,則值會傳遞至 ConvertConvertBack 方法作為 parameter 引數。 即使值轉換器的實例在數個數據系結之間共用, ConverterParameter 執行不同轉換也可以不同。

屬性的使用 ConverterParameter 可以透過色彩選取程式來示範。 下列範例顯示 RgbColorViewModel,其具有名為RedGreen類型的float三個屬性,以及Blue用來建構Color值:

public class RgbColorViewModel : INotifyPropertyChanged
{
    Color color;
    string name;

    public event PropertyChangedEventHandler PropertyChanged;

    public float Red
    {
        get { return color.Red; }
        set
        {
            if (color.Red != value)
            {
                Color = new Color(value, color.Green, color.Blue);
            }
        }
    }

    public float Green
    {
        get { return color.Green; }
        set
        {
            if (color.Green != value)
            {
                Color = new Color(color.Red, value, color.Blue);
            }
        }
    }

    public float Blue
    {
        get { return color.Blue; }
        set
        {
            if (color.Blue != value)
            {
                Color = new Color(color.Red, color.Green, value);
            }
        }
    }

    public Color Color
    {
        get { return color; }
        set
        {
            if (color != value)
            {
                color = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Red"));
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Green"));
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Blue"));
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Color"));

                Name = NamedColor.GetNearestColorName(color);
            }
        }
    }

    public string Name
    {
        get { return name; }
        private set
        {
            if (name != value)
            {
                name = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name"));
            }
        }
    }
}

RedGreenBlue 屬性值的範圍可以介於 0 到 1 之間。 不過,建議您以兩位數十六進位值來顯示元件。 若要在 XAML 中顯示這些十六進位值,這些值必須乘以 255、轉換為整數,並在 StringFormat 屬性中以 "X2" 規格進行格式化。 乘以 255 並轉換成整數,可由值轉換器執行。 您可以使用 ConverterParameter 屬性指定乘法因數來讓值轉換器盡可能通用,這表示其會輸入 ConvertConvertBack 方法作為 parameter 引數:

public class FloatToIntConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (int)Math.Round((float)value * GetParameter(parameter));
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (int)value / GetParameter(parameter);
    }

    double GetParameter(object parameter)
    {
        if (parameter is float)
            return (float)parameter;
        else if (parameter is int)
            return (int)parameter;
        else if (parameter is string)
            return float.Parse((string)parameter);

        return 1;
    }
}

在此範例中 Convert ,方法會從 float int 轉換為 ,同時乘以 parameter 值。 方法會將 ConvertBack 整數 value 自變數除以 parameter ,並傳 float 回結果。

視數據系結是在 XAML 或程式碼中定義數據系結而定,自變數的類型 parameter 可能會不同。 如果 ConverterParameter 屬性 Binding 設定於程式碼中,則可能會設為數值:

binding.ConverterParameter = 255;

ConverterParameter 屬性為 Object 類型,因此 C# 編譯器會將常值 255 解譯為整數,並將屬性設定為該值。

不過,在 XAML 中 ConverterParameter ,可能會設定如下:

<Label Text="{Binding Red,
                      Converter={StaticResource doubleToInt},
                      ConverterParameter=255,
                      StringFormat='Red = {0:X2}'}" />

雖然 255 看起來像數位,因為 ConverterParameter 類型 Object為 ,XAML 剖析器會將 255 視為字串。 基於這個理由,值轉換器包含個別GetParameter的方法,可處理 類型float為、 intstring的案例parameter

下列 XAML 範例會在其資源字典中具現化 FloatToIntConverter

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:DataBindingDemos"
             x:Class="DataBindingDemos.RgbColorSelectorPage"
             Title="RGB Color Selector"
             x:DataType="local:RgbColorViewModel">
    <ContentPage.BindingContext>
        <local:RgbColorViewModel Color="Gray" />
    </ContentPage.BindingContext>
    <ContentPage.Resources>
        <Style TargetType="Slider">
            <Setter Property="VerticalOptions" Value="Center" />
        </Style>

        <Style TargetType="Label">
            <Setter Property="HorizontalTextAlignment" Value="Center" />
        </Style>

        <local:FloatToIntConverter x:Key="floatToInt" />
    </ContentPage.Resources>

    <StackLayout Margin="20">
        <BoxView Color="{Binding Color}"
                 HeightRequest="100"
                 WidthRequest="100"
                 HorizontalOptions="Center" />
        <StackLayout Margin="10, 0">
            <Label Text="{Binding Name}" />
            <Slider Value="{Binding Red}" />
            <Label Text="{Binding Red,
                                  Converter={StaticResource floatToInt},
                                  ConverterParameter=255,
                                  StringFormat='Red = {0:X2}'}" />
            <Slider Value="{Binding Green}" />
            <Label Text="{Binding Green,
                                  Converter={StaticResource floatToInt},
                                  ConverterParameter=255,
                                  StringFormat='Green = {0:X2}'}" />
            <Slider Value="{Binding Blue}" />
            <Label>
                <Label.Text>
                    <Binding Path="Blue"
                             StringFormat="Blue = {0:X2}"
                             Converter="{StaticResource floatToInt}">
                        <Binding.ConverterParameter>
                            <x:Single>255</x:Single>
                        </Binding.ConverterParameter>
                    </Binding>
                </Label.Text>
            </Label>
        </StackLayout>
    </StackLayout>
</ContentPage>

RedGreen 屬性的值會以 Binding 標記延伸顯示。 不過,屬性會 Blue 具現化 Binding 類別,以示範如何將明確 float 值設定為 ConverterParameter 屬性:

RGB color selector.