次の方法で共有


コンパイル済みのバインディング

サンプルを参照します。 サンプルを参照する

.NET Multi-platform App UI (.NET MAUI) データ バインディングには、次の 2 つのメインの問題があります。

  1. バインディング式はコンパイル時に検証されません。 代わりに、バインドは実行時に解決されます。 そのため、アプリケーションが予期したとおりに動作しない場合やエラーメッセージが表示される場合、実行時まで無効なバインドが検出されません。
  2. これらはコスト効率が高くありません。 バインドは実行時に汎用オブジェクト検査 (リフレクション) を使用して解決され、これを行うオーバーヘッドはプラットフォームによって異なります。

コンパイル済みのバインドでは、実行時ではなく、コンパイル時にバインド式を解決すれば、.NET MAUI アプリケーションでのデータ バインディングのパフォーマンスが向上します。 さらに、このバインド式のコンパイル時の検証では、無効なバインドがビルド エラーとして報告されるため、開発者のトラブルシューティング エクスペリエンスを向上させることができます。

重要

NativeAOT アプリや完全トリミングが有効なアプリでは、文字列ベースのバインドではなく、コンパイル済みのバインドが必要です。 詳細については、「 .NET MAUI アプリの作成 および Native AOT のデプロイを参照してください。

XAML でのコンパイル済みのバインド

XAML でコンパイル済みのバインドを使用するには、VisualElementx:DataType 属性を VisualElement とその子がバインドされるオブジェクトの型に設定します。 BindingContext が設定されているのと同じレベルで、ビュー階層に x:DataType 属性を設定することをお勧めします。 ただし、この属性は、ビュー階層の任意の場所で再定義できます。

重要

コンパイルされたバインディングは、XAML コンパイルを使用する必要があります。これは、.NET MAUI で既定で有効になっています。 XAML コンパイルを無効にした場合は、有効にする必要があります。 詳細については、「XAML Compilation」(XAML のコンパイル) を参照してください。

XAML でコンパイル済みのバインドを使用するには、x:DataType 属性を文字列リテラル、または x:Type マークアップ拡張を使用する型に設定する必要があります。 XAML のコンパイル時に、無効なバインド式はすべてビルド エラーとして報告されます。 しかし、XAML コンパイラでは、最初に見つかった無効なバインド式についてのみ、ビルド エラーが報告されます。 BindingContext が XAML またはコードで設定されているかどうかに関係なく、VisualElement またはその子で定義されている有効なバインド式はすべてコンパイルされます。 バインド式をコンパイルすると、コンパイル済みのコードが生成され、ソース のプロパティから値が取得されて、マークアップで指定されているターゲット のプロパティに設定されます。 さらに、バインド式に応じて、生成されるコードでは、ソース プロパティの値の変更が監視され、ターゲット プロパティが更新される場合があります。また、ターゲット からソース に変更がプッシュ バックされる場合があります。

重要

コンパイル済みのバインドは、Source プロパティを定義するどの XAML バインド式でも無効になっています。 これは、コンパイル時に解決できない、x:Reference マークアップ拡張を使用して、Source プロパティが常に設定されるためです。

さらに、XAML でのコンパイル済みのバインドは現在、マルチバインディングではサポートされていません。

既定では、.NET MAUI では、コンパイル済みバインドを使用しない XAML バインドのビルド警告は生成されません。 ただし、$(MauiStrictXamlCompilation) ビルド プロパティをアプリのプロジェクト ファイル (*.csproj) の true に設定することで、生成されるコンパイル済みバインドの警告をオプトインできます。

<MauiStrictXamlCompilation>true</MauiStrictXamlCompilation>

既定では、.NET MAUI はコンパイル済みバインドを使用しない XAML バインドに対してビルド警告を生成します。

XAML コンパイル バインドの警告の詳細については、「XAML コンパイル 済みバインドの警告を参照してください。

XAML でコンパイル済みのバインドを使用する

次の例では、.NET MAUI ビューと viewmodel プロパティの間でコンパイル済みバインディングを使用する方法を示します。

<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.CompiledColorSelectorPage"
             x:DataType="local:HslColorViewModel"
             Title="Compiled Color Selector">
    <ContentPage.BindingContext>
        <local:HslColorViewModel Color="Sienna" />
    </ContentPage.BindingContext>
    ...
    <StackLayout>
        <BoxView Color="{Binding Color}"
                 ... />
        <StackLayout Margin="10, 0">
            <Label Text="{Binding Name}" />
            <Slider Value="{Binding Hue}" />
            <Label Text="{Binding Hue, StringFormat='Hue = {0:F2}'}" />
            <Slider Value="{Binding Saturation}" />
            <Label Text="{Binding Saturation, StringFormat='Saturation = {0:F2}'}" />
            <Slider Value="{Binding Luminosity}" />
            <Label Text="{Binding Luminosity, StringFormat='Luminosity = {0:F2}'}" />
        </StackLayout>
    </StackLayout>    
</ContentPage>

ContentPage は、HslColorViewModel をインスタンス化し、BindingContext プロパティのためのプロパティ要素タグ内の Color プロパティを初期化します。 また、ContentPagex:DataType 属性をビューモデル型として定義し、ContentPage ビュー階層内のバインド式がすべてコンパイルされることを示します。 これは、ビルド エラーとなる、存在しない viewmodel プロパティにバインドするためにバインド式のいずれかを変更することで、確認できます。 この例では、x:DataType 属性を文字列リテラルに設定しますが、x:Type マークアップ拡張機能を使用して型に設定することもできます。 x:Type マークアップ拡張機能の詳細については、「x:Type マークアップ拡張機能」を参照してください。

重要

x:DataType 属性は、ビュー階層の任意の時点で再定義できます。

BoxViewLabel 要素、および Slider ビューでは、ContentPage からバインド コンテキストを継承します。 これらのビューはすべてバインディング ターゲットであり、viewmodel のソース プロパティを参照します。 BoxView.Color プロパティ、および Label.Text プロパティの場合、データ バインディングは OneWay となります。ビューのプロパティは viewmodel のプロパティから設定されます。 しかし、Slider.Value プロパティでは TwoWay バインドが使用されます。 これにより、各 Slider は viewmodel から設定できるようになり、また、viewmodel を各 Slider から設定できるようになります。

この例の最初の実行時に、BoxViewLabel 要素、および Slider 要素はすべて、ビューモデルがインスタンス化されたときに設定された初期の Color プロパティに基づいて、ビューモデルから設定されます。 スライダーの操作に応じて、BoxViewLabel 要素が更新されます。

コンパイル済みのカラー セレクター。

このカラー セレクターの詳細については、「ViewModel とプロパティ変更通知」をご覧ください。

DataTemplate で XAML のコンパイル済みのバインドを使用する

DataTemplate でのバインドは、テンプレート化されているオブジェクトのコンテキストで解釈されます。 そのため、DataTemplate でコンパイル済みのバインドを使用するときには、DataTemplatex:DataType 属性を使用して、そのデータ オブジェクトの型を宣言する必要があります。 これを行わないと、 DataTemplate が親スコープから正しくない x:DataType を継承する可能性があります。

<ContentPage ...
             x:DataType="local:AnimalsPageViewModel">
    <!-- Binding to AnimalsPageViewModel.Animals -->
    <CollectionView ItemsSource="{Binding Animals}">
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <!-- incorrect: compiler thinks you want to bind to AnimalsPageViewModel.Name -->  
                <Label Text="{Binding Name}" />
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</ContentPage>

次の例は、DataTemplatex:DataTypeを正しく設定する方法を示しています。

<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.CompiledColorListPage"
             Title="Compiled Color List">
    <Grid>
        ...
        <ListView x:Name="colorListView"
                  ItemsSource="{x:Static local:NamedColor.All}"
                  ... >
            <ListView.ItemTemplate>
                <DataTemplate x:DataType="local:NamedColor">
                    <ViewCell>
                        <StackLayout Orientation="Horizontal">
                            <BoxView Color="{Binding Color}"
                                     ... />
                            <Label Text="{Binding FriendlyName}"
                                   ... />
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
        <!-- The BoxView doesn't use compiled bindings -->
        <BoxView Color="{Binding Source={x:Reference colorListView}, Path=SelectedItem.Color}"
                 ... />
    </Grid>
</ContentPage>

ListView.ItemsSource プロパティは、静的な NamedColor.All プロパティに設定されます。 NamedColor クラスでは .NET リフレクションを使用して、Colors クラスのすべての静的なパブリック フィールドを列挙し、静的な All プロパティからアクセス可能なコレクションにその名前と共に格納します。 そのため、ListView には NamedColor インスタンスがすべて取り込まれます。 ListView の各項目については、その項目のバインド コンテキストが NamedColor オブジェクトに設定されます。 ViewCell 内の BoxView および Label 要素は、NamedColor プロパティにバインドされます。

DataTemplate は、NamedColor 型になるように x:DataType 属性を定義し、DataTemplate ビュー階層内のバインド式がすべてコンパイルされることを示します。 これは、ビルド エラーとなる、存在しない NamedColor プロパティにバインドするためにバインド式のいずれかを変更することで、確認できます。 この例では、x:DataType 属性を文字列リテラルに設定しますが、x:Type マークアップ拡張機能を使用して型に設定することもできます。 x:Type マークアップ拡張機能の詳細については、「x:Type マークアップ拡張機能」を参照してください。

この例が最初に実行されるときに、ListViewNamedColor インスタンスが入力されます。 ListView 内の項目が選択されると、BoxView.Color プロパティは、ListView 内の選択された項目の色に設定されます。

コンパイル済みのカラー リスト。

ListView のその他の項目を選択すると、BoxView の色が更新されます。

Source プロパティを定義するバインドをコンパイルする

.NET MAUI 9 より前の XAML コンパイラでは、BindingContextではなく、Source プロパティを定義するバインディングのコンパイルはスキップしていました。 .NET MAUI 9 では、これらのバインディングをコンパイルして、ランタイム パフォーマンスを向上させることができます。 ただし、この最適化は、既存のアプリ コードの中断を回避するために、既定では有効になっていません。 この最適化を有効にするには、 $(MauiEnableXamlCBindingWithSourceCompilation) ビルド プロパティをアプリのプロジェクト ファイルに true するように設定します。

<MauiEnableXamlCBindingWithSourceCompilation>true</MauiEnableXamlCBindingWithSourceCompilation>

次に、すべてのバインディングに正しい x:DataType で注釈が付けられていることを確認し、親スコープから不適切なデータ型を継承しないようにします。

<HorizontalStackLayout BindingContext="{x:Reference slider}" x:DataType="Slider">
  <Label Text="{Binding Value}" />
  <Label Text="{Binding Text, Source={x:Reference entry}, x:DataType=Entry}" />
</HorizontalStackLayout>

Note

Sourceとのバインドがあるが、親からx:DataTypeを継承している場合は、x:DataTypeSourceの型が一致しない可能性があります。 このシナリオでは、警告が生成され、実行時にバインディング パスを解決するリフレクション ベースのバインドへのフォールバックが発生します。

コンパイル済みのバインドと XAML の従来のバインドを結合する

バインド式は、x:DataType 属性が定義されているビュー階層でのみコンパイルされます。 逆に、x:DataType 属性が定義されていない階層内のすべてのビューでは、従来のバインドが使用されます。 そのため、ページ上でコンパイル済みのバインドと従来のバインドを結合することができます。 たとえば、前のセクションでは、DataTemplate 内のビューでコンパイル済みのバインドが使用されますが、ListView で選択された色に設定されている BoxView では使用されません。

したがって、x:DataType 属性を慎重に構成することで、ページでコンパイル済みのバインドと従来のバインドが使用される可能性があります。 また、ビュー階層の任意の時点で x:Null マークアップ拡張を使用して、x:DataType 属性を null に再定義することができます。 これを行うことで、ビュー階層内のすべてのバインド式で従来のバインドが使用されることが示されます。 次の簡単な例で、この方法を示します。

<StackLayout x:DataType="local:HslColorViewModel">
    <StackLayout.BindingContext>
        <local:HslColorViewModel Color="Sienna" />
    </StackLayout.BindingContext>
    <BoxView Color="{Binding Color}"
             VerticalOptions="FillAndExpand" />
    <StackLayout x:DataType="{x:Null}"
                 Margin="10, 0">
        <Label Text="{Binding Name}" />
        <Slider Value="{Binding Hue}" />
        <Label Text="{Binding Hue, StringFormat='Hue = {0:F2}'}" />
        <Slider Value="{Binding Saturation}" />
        <Label Text="{Binding Saturation, StringFormat='Saturation = {0:F2}'}" />
        <Slider Value="{Binding Luminosity}" />
        <Label Text="{Binding Luminosity, StringFormat='Luminosity = {0:F2}'}" />
    </StackLayout>
</StackLayout>   

ルートの StackLayout では、HslColorViewModel 型になるように x:DataType 属性が設定され、ルートの StackLayout ビュー階層内のバインド式がすべてコンパイルされることが示されます。 しかし、内部の StackLayout では、x:DataType 属性が x:Null マークアップ式を使用して null に再定義されます。 したがって、内部の StackLayout 内のバインド式では、従来のバインドが使用されます。 ルートの StackLayout ビュー階層内の BoxView のみで、従来のバインドが使用されます。

x:Null マークアップ式の詳細については、「x:Null のマークアップ拡張」を参照してください。

XAML コンパイル バインドの警告

次の表に、コンパイルされたバインドに対するコンパイラの警告とその解決方法を示します。

コード メッセージ 固定
XC0022 x:DataTypeが指定されている場合は、バインドをコンパイルして実行時のパフォーマンスを向上させることができます。 XAML に x:DataType を追加して、現在の BindingContextの型を指定します。 バインディング コンテキストが変更されるすべての要素に x:DataType を追加することをお勧めします。
XC0023 x:DataTypeが明示的にnullされていない場合は、バインドをコンパイルして実行時のパフォーマンスを向上させることができます。 x:DataType="{x:Null}"を適切な型に置き換えます。
コード メッセージ
XC0022 x:DataTypeが指定されている場合は、バインドをコンパイルして実行時のパフォーマンスを向上させることができます。

この警告を修正するには、XAML に x:DataType を追加して、現在の BindingContextの型を指定します。 バインディング コンテキストが変更されるすべての要素に x:DataType を追加することをお勧めします。
XC0023 x:DataTypeが明示的にnullされていない場合は、バインドをコンパイルして実行時のパフォーマンスを向上させることができます。

この警告を修正するには、 x:DataType="{x:Null}" を適切な型に置き換えます。
XC0024 x:DataType注釈は外部スコープから取得されるため、バインドが正しくコンパイルされない可能性があります。 すべての DataTemplate XAML 要素に正しい x:DataTypeで注釈を付けます。

この警告を修正するには、すべての DataTemplate 要素に正しい x:DataTypeで注釈が付けられるようにします。
XC0025 明示的に Source プロパティが設定されており、 Source を使用したバインドのコンパイルが有効になっていないため、バインドはコンパイルされませんでした。 プロジェクト ファイルで <MauiEnableXamlCBindingWithSourceCompilation>true</MauiEnableXamlCBindingWithSourceCompilation> を設定して、この最適化を有効にすることを検討し、このバインドに対して正しい x:DataType が指定されていることを確認します。

この警告を修正するには、プロジェクト ファイルで $(MauiEnableXamlCBindingWithSourceCompilation) ビルド プロパティを有効にし、すべてのバインドに適切な x:DataTypeで注釈を付けます。

これらの警告が無視されないようにするには、 $(WarningsAsErrors) ビルド プロパティを使用して、特定の警告を変更してエラーをビルドすることを検討してください。

<WarningsAsErrors>$(WarningsAsErrors);XC0022;XC0023</WarningsAsErrors>

これらの警告を無視するには、特定の警告コードと共に $(NoWarn) ビルド プロパティを使用します。

<NoWarn>$(NoWarn);XC0022;XC0023</NoWarn>

重要

XC0022$(MauiStrictXamlCompilation)ビルドプロパティがtrueに設定されていない限り、XC0023警告は常に抑制されます。

$(TreatWarningsAsErrors) ビルド プロパティをアプリのプロジェクト ファイルtrueに設定しても、特定の XAML コンパイラの警告を無視する場合は、$(NoWarn) ビルド プロパティを使用してこれらの警告を無音にするか、$(WarningsNotAsErrors)ビルド プロパティを使用して特定のコードの重大度を下げてください。

既定では、.NET MAUI はコンパイル済みバインドを使用しない XAML バインドに対してビルド警告を生成します。 コンパイル済みのバインドの警告をエラーとして扱う場合は、アプリのプロジェクト ファイル (*.csproj) でtrueするように$(MauiStrictXamlCompilation)およびビルド プロパティ$(TreatWarningsAsErrors)設定します。

<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<MauiStrictXamlCompilation>true</MauiStrictXamlCompilation>

Note

既定では、フル トリミングまたは NativeAOT を使用してアプリを発行する場合を除き、 $(MauiStrictXamlCompilation) ビルド プロパティは false されます。

コードでのコンパイル済みのバインド

コードで記述されたバインドは、通常、リフレクションを使用して実行時に解決される文字列パスを使用します。 ただし、SetBinding 拡張メソッドには、文字列パスではなく Func 引数を使用してバインドを定義するオーバーロードもあります。

MyLabel.SetBinding(Label.TextProperty, static (Entry entry) => entry.Text);

コンパイル済みのバインドを定義するのに、すべてのメソッドが使用できるわけではありません。 式は、単純なプロパティ アクセス式である必要があります。 次の例は、有効なバインド式と無効なバインド式を示しています。

// Valid: Property access
static (PersonViewModel vm) => vm.Name;
static (PersonViewModel vm) => vm.Address?.Street;

// Valid: Array and indexer access
static (PersonViewModel vm) => vm.PhoneNumbers[0];
static (PersonViewModel vm) => vm.Config["Font"];

// Valid: Casts
static (Label label) => (label.BindingContext as PersonViewModel).Name;
static (Label label) => ((PersonViewModel)label.BindingContext).Name;

// Invalid: Method calls
static (PersonViewModel vm) => vm.GetAddress();
static (PersonViewModel vm) => vm.Address?.ToString();

// Invalid: Complex expressions
static (PersonViewModel vm) => vm.Address?.Street + " " + vm.Address?.City;
static (PersonViewModel vm) => $"Name: {vm.Name}";

さらに、BindingBase.Create メソッドは、Func を使用してオブジェクトにバインドを直接設定し、バインド オブジェクト インスタンスを返します。

myEntry.SetBinding(Entry.TextProperty, new MultiBinding
{
    Bindings = new Collection<BindingBase>
    {
        Binding.Create(static (Entry entry) => entry.FontFamily, source: RelativeBindingSource.Self),
        Binding.Create(static (Entry entry) => entry.FontSize, source: RelativeBindingSource.Self),
        Binding.Create(static (Entry entry) => entry.FontAttributes, source: RelativeBindingSource.Self),
    },
    Converter = new StringConcatenationConverter()
});

これらのコンパイル済みバインド アプローチには、次の利点があります。

  • 実行時ではなく、コンパイル時にバインド式を解決することで、データ バインディングのパフォーマンスが向上します。
  • 無効なバインドがビルド エラーとして報告されるため、開発者のトラブルシューティング エクスペリエンスを向上させることができます。
  • 編集中の Intellisense。

パフォーマンス

コンパイル済みのバインドではデータ バインディングのパフォーマンスが向上しますが、パフォーマンスの利点はさまざまです。

  • プロパティ変更通知を使用するコンパイル済みのバインド (つまり、OneWayOneWayToSource、または TwoWay バインド) は、従来のバインドより約 8 倍の速さで解決されます。
  • プロパティ変更通知を使用しないコンパイル済みのバインド (つまり、OneTime バインド) は、従来のバインドより約 20 倍の速さで解決されます。
  • プロパティ変更通知を使用するコンパイル済みのバインド (つまり、OneWayOneWayToSource、または TwoWay バインド) で BindingContext を設定すると、従来のバインドで BindingContext を設定する場合より約 5 倍の速さとなります。
  • プロパティ変更通知を使用しないコンパイル済みのバインド (つまり、OneTime バインド) で BindingContext を設定すると、従来のバインドで BindingContext を設定する場合より約 7 倍の速さとなります。

モバイル デバイスでのこれらのパフォーマンスの違いは、使用されているプラットフォーム、使用されているオペレーティング システムのバージョン、およびアプリケーションが実行されているデバイスによって拡大する場合があります。