型が一致しないデータ バインディング
- 10 分
使用しているデータが、データを表示するコントロール プロパティのデータ型と一致しない場合があります。 たとえば、通貨値を decimal 型に格納し、それを通貨形式で Label コントロールに表示する場合があります。 より複雑な例として、モジュールに表示される天気予報アプリがあります。 画像は天気予報の Sunny または Cloudy 列挙値に基づいて表示される想定ですが、ソースの列挙値をターゲットの画像プロパティにバインドすることはできません。 このユニットでは、データを変換する方法について説明します。
文字列の書式設定
よくある型の不一致は、組み込みの型を書式設定された文字列として表示したい場合です。 たとえば、DateTime 値の一部を表示する場合や、decimal 型を通貨として書式設定する場合です。
請求書の金額を表示する必要があり、データ オブジェクトに次のプロパティがあるとします。
public decimal BillAmount { get; set; }
未払額は最終的に 22.0304 になります。 次のスニペットに示すように、2 つのラベル コントロールを使ってテキストと金額を表示できます。
<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 出力によって画面に生成される内容を示しています。
StringFormat バインディング プロパティを使う XAML は他の XAML よりも単純です。また、.NET の強力な文字列書式設定システムにアクセスすることができます。
カスタムの型変換
StringFormat バインディング プロパティは、値を文字列として表示する場合には便利ですが、Color や Image などを別の型から変換する場合にはそうではありません。 このような場合は、カスタム変換コードを作成する必要があります。
ユーザーにパスワードの選択を求め、UI でパスワードの強度を示す色を使用するとします。 強度には、弱い、良い、強いという 3 つのレベルがあります。 強度は、パスワードの文字数に基づいています。 パスワードの強度に関するフィードバックをすぐにユーザーに表示するには、パスワードを格納する Entry コントロールの背景を、強度に基づいて変更する必要があります。 次の図は、弱い、良い、強いという 3 つの強度のレベルを示しています。
スクリーンショットにある 3 つの入力コントロールのうち、最初のコントロールには 4 文字が入力されており、背景は赤色で表示されています。 2 つ目は 9 文字が入力されており、背景は黄色で表示されています。 最後の入力コントロールは 15 文字であり、背景は青色で表示されています。
これらのレベルは Strength 列挙型に割り当てられています。
private enum Strength
{
Weak,
Good,
Strong
}
データ オブジェクトはページの BindingContext として割り当てられます。これには、PasswordStrength プロパティによるパスワードの強度が格納されます。 ユーザーがパスワードを入力すると、パスワードの長さに応じて PasswordStrength プロパティが変更されます。 データ オブジェクトには PasswordStrength プロパティが格納されているため、そのプロパティを BackgroundColor コントロールの Entry にバインドします。
<Entry BackgroundColor="{Binding PasswordStrength} ... />
しかし、ここには問題があります。
PasswordStrength は Strength 型ですが、BackgroundColor は Color です。 これらの型は互いに互換性がありません。 .NET MAUI には、このような型不一致の問題を修正する方法が用意されています。バインディングの Converter プロパティです。
バインディング コンバーターはその名のとおり、バインディング ソースとターゲット間で変換を行います。 コンバーターは、IValueConverter インターフェイスを介して実装されています。
IValueConverter を実装する
変換ロジックは、IValueConverter インターフェイスを実装するクラスに作成します。 通常、このようなクラスの名前は、その目的を明確にするために Converter という名前を末尾に付けます。
IValueConverter インターフェイスでは、2 つのメソッドが定義されています。
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メソッドには 4 つのパラメーターがあります。 通常、後半 2 つのパラメーターは、特に使う理由がなければ破棄できます。 -
valueパラメーターには受信値が格納されます。 この例では、Strength列挙値です。 -
targetTypeパラメーターは無視されますが、このパラメーターを使用して、コンバーターで使用されているプロパティの型がColorであることを検証できます。 わかりやすくするために、この例ではこれを省略しています。 - switch 式は、
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 全体で再利用するコンバーターは 1 つだけだからです。 何らかの理由で複数のコンバーター インスタンスが必要な場合、それぞれ異なるキーを割り当てる必要があります。
最後に、バインディング上のコンバーターを参照します。 コンバーターはリソース ディクショナリ内にあるため、 {StaticResource} マークアップ拡張機能を使用して参照します。 コンバーターは Converter バインディング プロパティに割り当てられます。
<Entry BackgroundColor="{Binding PasswordStrength, Converter={StaticResource StrengthToColorConverter}}" ... />