系結值轉換器
.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
屬性。 此類別會成為資料繫結的一部分。
當資料在 OneWay
或 TwoWay
繫結從來源移到目標時,會呼叫 Convert
方法。 value
參數是資料繫結來源的物件或值。 此方法必須傳回資料繫結目標類型的值。 這裡示範的方法會將 value
參數轉換為 int
,然後將其與 0 比較,以取得 bool
傳回值。
當資料在 TwoWay
或 OneWayToSource
繫結從目標移到來源時,會呼叫 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
屬性中包含資料繫結。 資料繫結來源是對應 Entry 之 Text
屬性的 Length
屬性。 如果該 Length
屬性不為 0,則值轉換器會傳回 true
並啟用 Button:
注意
如果您知道某個值轉換器僅限用於 OneWay
繫結,則 ConvertBack
方法只能傳回 null
。
Convert
上述方法假設value
自變數的類型為 int
,且傳回值必須是 類型bool
。 同樣,ConvertBack
方法會假設 value
引數是 bool
類型,且傳回值為 int
。 若非此情況,則會發生執行階段例外狀況。
您可以撰寫更通用且可接受數個不同資料類型的值轉換器。 Convert
和 ConvertBack
方法可以透過 value
參數來使用 as
或 is
運算子,或者呼叫該參數上的 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
指出泛型引數,且 TrueObject
和 FalseObject
都設為該類型的物件:
<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 反映變更:
注意
您也可以使用觸發程式,根據其他檢視實作使用者介面中的變更。 如需詳細資訊,請參閱 觸發程式。
係結轉換器參數
Binding
類別會定義 ConverterParameter
屬性,而 Binding
標記延伸也會定義 ConverterParameter
屬性。 如果設定此屬性,則值會傳遞至 Convert
和 ConvertBack
方法作為 parameter
引數。 即使值轉換器的實例在數個數據系結之間共用, ConverterParameter
執行不同轉換也可以不同。
屬性的使用 ConverterParameter
可以透過色彩選取程式來示範。 下列範例顯示 RgbColorViewModel
,其具有名為Red
、 Green
類型的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"));
}
}
}
}
Red
、 Green
和 Blue
屬性值的範圍可以介於 0 到 1 之間。 不過,建議您以兩位數十六進位值來顯示元件。 若要在 XAML 中顯示這些十六進位值,這些值必須乘以 255、轉換為整數,並在 StringFormat
屬性中以 "X2" 規格進行格式化。 乘以 255 並轉換成整數,可由值轉換器執行。 您可以使用 ConverterParameter
屬性指定乘法因數來讓值轉換器盡可能通用,這表示其會輸入 Convert
和 ConvertBack
方法作為 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
為、 int
或string
的案例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>
Red
和 Green
屬性的值會以 Binding
標記延伸顯示。 不過,屬性會 Blue
具現化 Binding
類別,以示範如何將明確 float
值設定為 ConverterParameter
屬性: