次の方法で共有


チュートリアル: データ バインディングを作成する

プレースホルダー画像、"lorem ipsum" 定型テキスト、およびまだ何もしないコントロールでいっぱいの見栄えの良い UI を設計して実装したとします。 次に、実際のデータに接続し、設計プロトタイプから生きたアプリに変換します。

このチュートリアルでは、定型句をデータ バインディングに置き換え、UI とデータの間に他の直接リンクを作成する方法について説明します。 また、表示用にデータを書式設定または変換し、UI とデータの同期を維持する方法についても説明します。このチュートリアルを完了すると、XAML および C# コードのシンプルさと編成が向上し、保守と拡張が容易になります。

まず、PhotoLab サンプルの簡略化されたバージョンから始めます。 このスターター バージョンには、完全なデータ レイヤーと基本的な XAML ページ レイアウトが含まれており、コードを簡単に参照できるように多くの機能が残されています。 このチュートリアルは完全なアプリに対応していないため、カスタム アニメーションやアダプティブ レイアウトなどの機能を確認するには、必ず最終バージョンを確認してください。 最終的なバージョンは、Windows-appsample-photo-lab リポジトリのルート フォルダーにあります。

PhotoLab サンプル アプリには 2 つのページがあります。 メイン ページ には、各イメージ ファイルに関するいくつかの情報と共に、フォト ギャラリー ビューが表示されます。

フォト ラボのメイン ページのスクリーンショット。

の詳細ページ では、選択した後に写真が1枚表示されます。 ポップアップ編集メニューを使用すると、写真の変更、名前の変更、保存を行うことができます。

フォト ラボの詳細ページのスクリーンショット。

[前提条件]

  • Visual Studio 2019 以降: Visual Studio をダウンロードします (Community エディションは無料です)。
  • Windows SDK (10.0.17763.0 以降): 最新の Windows SDK (無料) をダウンロード
  • Windows 10 バージョン 1809 以降

パート 0: GitHub からスターター コードを取得する

このチュートリアルでは、PhotoLab サンプルの簡略化されたバージョンから始めます。

  1. サンプルの GitHub ページに移動します:https://github.com/Microsoft/Windows-appsample-photo-lab

  2. 次に、サンプルを複製またはダウンロードする必要があります。 複製またはダウンロード ボタンを選択します。 サブメニューが表示されます。 PhotoLab サンプルの GitHub ページ内の [複製またはダウンロード] メニュー

    GitHub に慣れていない場合:

    ある。 ["ダウンロード ZIP"] を選択し、ファイルをローカルに保存します。 これにより、必要なすべてのプロジェクト ファイルを含む .zip ファイルがダウンロードされます。

    b。 ファイルを抽出します。 エクスプローラーを使用して、ダウンロードした .zip ファイルを参照して右クリックし、[すべて展開]選択します。..

    c. サンプルのローカル コピーを参照し、Windows-appsample-photo-lab-master\xaml-basics-starting-points\data-binding ディレクトリに移動します。

    GitHub に慣れている場合:

    ある。 リポジトリのメイン ブランチをローカルに複製します。

    b。 Windows-appsample-photo-lab\xaml-basics-starting-points\data-binding ディレクトリを参照します。

  3. Photolab.sln をダブルクリックして、Visual Studio でソリューションを開きます。

パート 1: プレースホルダーを置き換える

ここでは、データ テンプレート XAML で 1 回限りのバインドを作成して、プレースホルダー コンテンツではなく、実際の画像と画像メタデータを表示します。

1 回限りのバインドは読み取り専用で変更されないデータ用です。つまり、高パフォーマンスで簡単に作成できるため、GridView コントロールと ListView コントロールに大きなデータ セットを表示できます。

プレースホルダーを 1 回限りのバインドに置き換える

  1. xaml-basics-starting-points\data-binding フォルダーを開き、Visual Studio で PhotoLab.sln ファイルを起動します。

  2. ソリューション プラットフォーム が Arm ではなく x86 または x64 に設定されていることを確認してから、アプリを実行します。 これは、バインドが追加される前の UI プレースホルダーを含むアプリの状態を示しています。

    プレースホルダー 画像とテキスト を使用してアプリを実行する

  3. MainPage.xaml を開き、DataTemplateという名前の を検索します。 このテンプレートは、データ バインディングを使用するように更新します。

    以前:

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate">
    

    x:Key 値は、データ オブジェクトを表示するためにこのテンプレートを選択するために ImageGridView によって使用されます。

  4. テンプレートに x:DataType 値を追加します。

    その後:

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
    

    x:DataType は、これがテンプレートである型を示します。 この場合は、ImageFileInfo クラスのテンプレートです (local: は、ファイルの先頭付近にある xmlns 宣言で定義されているローカル名前空間を示します)。

    x:DataType は、次に説明するように、データ テンプレートで x:Bind 式を使用する場合に必要です。

  5. DataTemplateで、Image という名前の ItemImage 要素を見つけて、Source の値を次のように置き換えます。

    以前:

    <Image x:Name="ItemImage"
           Source="/Assets/StoreLogo.png"
           Stretch="Uniform" />
    

    その後:

    <Image x:Name="ItemImage"
           Source="{x:Bind ImageSource}"
           Stretch="Uniform" />
    

    x:Name は XAML 要素を識別するため、XAML と分離コードの別の場所で参照できます。

    x:Bind 式は、データ オブジェクトの プロパティから値を取得することによって、UI プロパティに値を提供します。 テンプレートでは、示されるプロパティは、x:DataType が設定されている任意のプロパティです。 そのため、この場合、データ ソースは ImageFileInfo.ImageSource プロパティです。

    x:Bind 値を使用すると、エディターはデータ型について知ることができるため、x:Bind 式でプロパティ名を入力する代わりに IntelliSense を使用できます。 貼り付けたコードで試してみてください。x:Bind 直後にカーソルを置き、Space キーを押してバインドできるプロパティの一覧を表示します。

  6. 他の UI コントロールの値も同じように置き換えます。 (コピー/貼り付けではなく、IntelliSense でこれを実行してみてください)。

    以前:

    <TextBlock Text="Placeholder" ... />
    <StackPanel ... >
        <TextBlock Text="PNG file" ... />
        <TextBlock Text="50 x 50" ... />
    </StackPanel>
    <muxc:RatingControl Value="3" ... />
    

    その後:

    <TextBlock Text="{x:Bind ImageTitle}" ... />
    <StackPanel ... >
        <TextBlock Text="{x:Bind ImageFileType}" ... />
        <TextBlock Text="{x:Bind ImageDimensions}" ... />
    </StackPanel>
    <muxc:RatingControl Value="{x:Bind ImageRating}" ... />
    

アプリを実行して、これまでの外観を確認します。 プレースホルダーはもうありません。 良いスタートを切っています。

プレースホルダーの代わりに実際の画像とテキストを使用してアプリを実行

さらに実験する場合は、新しい TextBlock をデータ テンプレートに追加し、x:Bind IntelliSense のトリックを使用して表示するプロパティを見つけます。

ここでは、ギャラリー ビューをイメージ コレクションに接続するために、ページ XAML で 1 回限りのバインドを作成します。これを分離コードで行う既存の手続き型コードを置き換えます。 また、[削除] ボタンを作成して、イメージがコレクションから削除されたときにギャラリー ビューがどのように変化するかを確認します。 同時に、従来のイベント ハンドラーよりも柔軟性を高めるために、イベントをイベント ハンドラーにバインドする方法について説明します。

これまでに説明したすべてのバインディングはデータ テンプレート内にあり、x:DataType 値によって示されるクラスのプロパティを参照します。 ページの残りの XAML はどうですか?

データ テンプレートの外部にある x:Bind 式は、常にページ自体にバインドされます。 つまり、分離コードに配置したものや XAML で宣言したものを、ページ上の他の UI コントロールのカスタム プロパティやプロパティ(x:Name 値がある限り)を含めて参照できます。

PhotoLab サンプルでは、このようなバインドの用途の 1 つとして、コードビハインドで行う代わりに、メイン GridView コントロールを画像コレクションに直接接続するという方法があります。 後で、他の例が表示されます。

メインの GridView コントロールを Images コレクションにバインドする

  1. MainPage.xaml.csで、GetItemsAsync メソッドを見つけて、ItemsSourceを設定するコードを削除します。

    以前:

    ImageGridView.ItemsSource = Images;
    

    その後:

    // Replaced with XAML binding:
    // ImageGridView.ItemsSource = Images;
    
  2. MainPage.xaml で、GridView という名前の ImageGridView を見つけて、ItemsSource 属性を追加します。 値には、コード・ビハインドで実装されている x:Bind プロパティを参照する Images 式を使用します。

    以前:

    <GridView x:Name="ImageGridView"
    

    その後:

    <GridView x:Name="ImageGridView"
              ItemsSource="{x:Bind Images}"
    

    Images プロパティは ObservableCollection<ImageFileInfo>型であるため、GridView に表示される個々の項目は ImageFileInfo型です。 これは、パート 1 で説明されている x:DataType 値と一致します。

これまでに確認したすべてのバインディングは、1 回限りの読み取り専用のバインドです。これは、プレーン x:Bind 式の既定の動作です。 データは初期化時にのみ読み込まれるため、高パフォーマンスのバインドが可能になり、大規模なデータ セットの複数の複雑なビューをサポートするのに最適です。

先ほど追加した ItemsSource バインディングも、変更されていないプロパティ値に対する 1 回限りの読み取り専用バインディングですが、ここでは重要な違いがあります。 Images プロパティの変更されない値は、コレクションの単一の特定のインスタンスであり、次に示すように 1 回初期化されます。

private ObservableCollection<ImageFileInfo> Images { get; }
    = new ObservableCollection<ImageFileInfo>();

Images プロパティの値は変更されませんが、プロパティが ObservableCollection<T>型であるため、コレクションの コンテンツ が変更される可能性があり、バインドによって自動的に変更が通知され、UI が更新されます。

これをテストするには、現在選択されているイメージを削除するボタンを一時的に追加します。 イメージを選択すると詳細ページに移動するため、このボタンは最終バージョンではありません。 ただし、ページ コンストラクター (ObservableCollection<T> メソッド呼び出し) で XAML が初期化されますが、InitializeComponent コレクションは後で Images メソッドに設定されるため、GetItemsAsync の動作は最終的な PhotoLab サンプルで引き続き重要です。

削除ボタンを追加する

  1. MainPage.xaml で、MainCommandBar 名前の を見つけて、ズーム ボタンの前に新しいボタンを追加します。 (ズーム コントロールはまだ機能しません。これらは、チュートリアルの次の部分でフックします)。

    <AppBarButton Icon="Delete"
                  Label="Delete selected image"
                  Click="{x:Bind DeleteSelectedImage}" />
    

    既に XAML に慣れている場合、この Click 値は通常とは異なる場合があります。 以前のバージョンの XAML では、これを特定のイベント ハンドラーシグネチャを持つメソッドに設定する必要がありました。これには、通常、イベント送信者のパラメーターとイベント固有の引数オブジェクトが含まれます。 イベント引数が必要な場合でもこの手法を使用できますが、x:Bindを使用すると、他のメソッドにも接続できます。 たとえば、イベント データが不要な場合は、ここで行うのと同様に、パラメーターのないメソッドに接続できます。

  2. MainPage.xaml.csで、DeleteSelectedImage メソッドを追加します。

    private void DeleteSelectedImage() =>
        Images.Remove(ImageGridView.SelectedItem as ImageFileInfo);
    

    このメソッドは、選択したイメージを Images コレクションから削除するだけです。

次に、アプリを実行し、ボタンを使用していくつかのイメージを削除します。 ご覧のように、データ バインディングと ObservableCollection<T> の種類により、UI は自動的に更新されます。

このコードは、実行中のアプリの ImageFileInfo コレクションから Images インスタンスのみを削除します。 コンピューターからイメージ ファイルは削除されません。

パート 3: ズーム スライダーを設定する

このパートでは、データ テンプレート内のコントロールから、テンプレートの外側にあるズーム スライダーへの一方向バインドを作成します。 また、TextBlock.TextImage.Sourceなどの最も明白なものだけでなく、多くのコントロール プロパティでデータ バインディングを使用できることも学習します。

画像データ テンプレートをズーム スライダーにバインドする

  • DataTemplate という名前の ImageGridView_DefaultItemTemplate を見つけ、テンプレートの上部にある **Height** コントロールの WidthGrid の値を置き換えます。

    の前に

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
        <Grid Height="200"
              Width="200"
              Margin="{StaticResource LargeItemMargin}">
    

    後の

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
        <Grid Height="{Binding Value, ElementName=ZoomSlider}"
              Width="{Binding Value, ElementName=ZoomSlider}"
              Margin="{StaticResource LargeItemMargin}">
    

これらは Binding 式であり、x:Bind 式ではないことに気付きましたか? これはデータ バインディングを行う古い方法であり、ほとんどの場合は廃止されています。 x:Bind は、Binding が行うほぼすべてのことを行います。 ただし、データ テンプレートで x:Bind を使用すると、x:DataType 値で宣言された型にバインドされます。 では、テンプレート内の何かをページ XAML やコードビハインドでの何かにバインドするにはどうすればよいですか? 古いスタイルの Binding 式を使用する必要があります。

Binding 式は x:DataType 値を認識しませんが、これらの Binding 式には、ほぼ同じように動作する ElementName 値があります。 これにより、バインド エンジンに、バインド値 が、ページ上の指定した要素 (つまり、その 値を持つ要素) の プロパティへのバインドであることを示します。 コードビハインドでプロパティを {Binding MyCodeBehindProperty, ElementName=page} にバインドしたい場合、それは XAML の page 要素で設定された x:NamePage を参照するようになります。

既定では、Binding 式は 1方法です。つまり、バインドされたプロパティ値が変更されると UI が自動的に更新されます。

これに対し、x:Bind の既定値は一度限りです。つまり、バインドされたプロパティへの変更は無視されます。 これは、最も高パフォーマンスのオプションであり、ほとんどのバインディングは静的な読み取り専用データに対して行われるため、これが既定です。

ここでのレッスンでは、値を変更できるプロパティで x:Bind を使用する場合は、必ず Mode=OneWay または Mode=TwoWayを追加してください。 この例については、次のセクションで説明します。

アプリを実行し、スライダーを使用してイメージ テンプレートのディメンションを変更します。 ご覧のように、効果は多くのコードを必要とせずに非常に強力です。

を示すズーム スライダーを使用してアプリを実行する

課題として、他の UI プロパティをズーム スライダー Value プロパティにバインドするか、ズーム スライダーの後に追加する他のスライダーにバインドしてみてください。 たとえば、既定値が FontSizeの新しいスライダーに TitleTextBlock プロパティをバインドできます。 適切な最小値と最大値を設定してください。

パート 4: ズーム エクスペリエンスを向上させる

このパートでは、分離コードにカスタム ItemSize プロパティを追加し、イメージ テンプレートから新しいプロパティへの一方向バインドを作成します。 ItemSize の値は、ズーム スライダーや、[画面に合わせる ] トグルやウィンドウ サイズなどのその他の要因によって更新され、より洗練されたエクスペリエンスを実現します。

組み込みのコントロール プロパティとは異なり、カスタム プロパティは、一方向と双方向のバインドを使用しても、UI を自動的に更新しません。 1 回 バインドで正常に動作しますが、プロパティの変更を実際に UI に表示する場合は、いくつかの作業を行う必要があります。

UI を更新するように ItemSize プロパティを作成する

  1. MainPage.xaml.csで、MainPage インターフェイスを実装するように、INotifyPropertyChanged クラスのシグネチャを変更します。

    以前:

    public sealed partial class MainPage : Page
    

    その後:

    public sealed partial class MainPage : Page, INotifyPropertyChanged
    

    これにより、MainPage に、UI を更新するためにバインディングがリッスンできる PropertyChanged イベント (次に追加) があることをバインディング システムに通知します。

  2. PropertyChanged イベントを MainPage クラスに追加します。

    public event PropertyChangedEventHandler PropertyChanged;
    

    このイベントは、INotifyPropertyChanged インターフェイスに必要な完全な実装を提供します。 ただし、何らかの影響を与えるためには、カスタム プロパティでイベントを明示的に発生させる必要があります。

  3. ItemSize プロパティを追加し、セッターで PropertyChanged イベントを発生させます。

    public double ItemSize
    {
        get => _itemSize;
        set
        {
            if (_itemSize != value)
            {
                _itemSize = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ItemSize)));
            }
        }
    }
    private double _itemSize;
    

    ItemSize プロパティは、プライベート _itemSize フィールドの値を公開します。 このようなバッキング フィールドを使用すると、不要な PropertyChanged イベントが発生する前に、プロパティは新しい値が古い値と同じかどうかを確認できます。

    イベント自体は、Invoke メソッドによって発生します。 疑問符は、PropertyChanged イベントが null であるかどうかを確認します。つまり、イベント ハンドラーがまだ追加されているかどうかを確認します。 一方向または双方向のバインドはすべて舞台裏でイベントハンドラーが追加されていますが、誰も聞いていないなら、ここでは何も起こらないでしょう。 ただし、PropertyChanged が null でない場合、Invoke はイベント ソースへの参照 (this キーワードで表されるページ自体) と、プロパティの名前を示す event-args オブジェクトを使用して呼び出されます。 この情報を使用すると、ItemSize プロパティへの一方向または双方向のバインドに対して、バインドされた UI を更新できるように変更が通知されます。

  4. MainPage.xaml で、DataTemplate という名前の ImageGridView_DefaultItemTemplate を見つけ、テンプレートの上部にある Height コントロールの WidthGrid の値を置き換えます。 (このチュートリアルの前の部分でコントロール間バインディングを行った場合、ValueItemSize に置き換え、ZoomSliderpageに置き換えるだけです。HeightWidthの両方でこれを行ってください!

    の前に

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
        <Grid Height="{Binding Value, ElementName=ZoomSlider}"
            Width="{Binding Value, ElementName=ZoomSlider}"
            Margin="{StaticResource LargeItemMargin}">
    

    後の

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
        <Grid Height="{Binding ItemSize, ElementName=page}"
              Width="{Binding ItemSize, ElementName=page}"
              Margin="{StaticResource LargeItemMargin}">
    

UI が ItemSize の変更に応答できるようになったので、実際にいくつかの変更を加える必要があります。 前述のように、ItemSize 値はさまざまな UI コントロールの現在の状態から計算されますが、これらのコントロールが状態を変更するたびに計算を実行する必要があります。 これを行うには、イベント バインディングを使用して、特定の UI の変更によって、ItemSize更新するヘルパー メソッドが呼び出されるようにします。

ItemSize プロパティ値を更新する

  1. MainPage.xaml.csに DetermineItemSize メソッドを追加します。

    private void DetermineItemSize()
    {
        if (FitScreenToggle != null
            && FitScreenToggle.IsOn == true
            && ImageGridView != null
            && ZoomSlider != null)
        {
            // The 'margins' value represents the total of the margins around the
            // image in the grid item. 8 from the ItemTemplate root grid + 8 from
            // the ItemContainerStyle * (Right + Left). If those values change,
            // this value needs to be updated to match.
            int margins = (int)this.Resources["LargeItemMarginValue"] * 4;
            double gridWidth = ImageGridView.ActualWidth -
                (int)this.Resources["DefaultWindowSidePaddingValue"];
            double ItemWidth = ZoomSlider.Value + margins;
            // We need at least 1 column.
            int columns = (int)Math.Max(gridWidth / ItemWidth, 1);
    
            // Adjust the available grid width to account for margins around each item.
            double adjustedGridWidth = gridWidth - (columns * margins);
    
            ItemSize = (adjustedGridWidth / columns);
        }
        else
        {
            ItemSize = ZoomSlider.Value;
        }
    }
    
  2. MainPage.xaml で、ファイルの先頭に移動し、SizeChanged 要素に Page イベント バインドを追加します。

    以前:

    <Page x:Name="page"
    

    その後:

    <Page x:Name="page"
          SizeChanged="{x:Bind DetermineItemSize}"
    
  3. (Slider セクションで) ZoomSlider という名前の Page.Resources を見つけて、ValueChanged イベント バインディングを追加します。

    以前:

    <Slider x:Name="ZoomSlider"
    

    その後:

    <Slider x:Name="ZoomSlider"
            ValueChanged="{x:Bind DetermineItemSize}"
    
  4. ToggleSwitch という名前の FitScreenToggle を見つけて、Toggled イベント バインディングを追加します。

    以前:

    <ToggleSwitch x:Name="FitScreenToggle"
    

    その後:

    <ToggleSwitch x:Name="FitScreenToggle"
                  Toggled="{x:Bind DetermineItemSize}"
    

アプリを実行し、ズーム スライダーを使用し、[画面に合わせる] 切り替えて、イメージ テンプレートのサイズを変更します。 ご覧のように、最新の変更により、コードを適切に整理したまま、より洗練されたズーム/サイズ変更エクスペリエンスが可能になります。

画面に合わせる機能を有効にしてアプリを実行する

難しい場合は、TextBlock の後に ZoomSlider を追加し、Text プロパティを ItemSize プロパティにバインドしてみてください。 データ テンプレートには含まれていないため、前の x:Bind バインドのように Binding の代わりに ItemSize を使用できます。

パート 5: ユーザーの編集を有効にする

ここでは、画像のタイトル、評価、さまざまな視覚効果など、ユーザーが値を更新できるようにするための双方向バインディングを作成します。

これを実現するには、既存の DetailPageを更新します。これにより、単一画像ビューアー、ズーム コントロール、および編集 UI が提供されます。

ただし、まず、DetailPage をアタッチして、ユーザーがギャラリー ビューで画像をクリックしたときにアプリが移動するようにする必要があります。

DetailPage をアタッチする

  1. MainPage.xaml で、GridViewという名前の ImageGridView を見つけます。 項目をクリックできるようにするには、IsItemClickEnabledTrue に設定し、ItemClick イベント ハンドラーを追加します。

    ヒント

    コピー/貼り付けではなく、次の変更を入力すると、"<新しいイベント ハンドラー>" という IntelliSense ポップアップが表示されます。 Tab キーを押すと、既定のメソッド ハンドラー名が値に入力され、次の手順で示すメソッドが自動的にスタブされます。 その後、F12 キーを押して、バックエンドコード内のメソッドへジャンプできます。

    以前:

    <GridView x:Name="ImageGridView">
    

    その後:

    <GridView x:Name="ImageGridView"
              IsItemClickEnabled="True"
              ItemClick="ImageGridView_ItemClick">
    

    ここでは、x:Bind 式ではなく、従来のイベント ハンドラーを使用しています。 これは、次に示すように、イベント データを表示する必要があるためです。

  2. MainPage.xaml.csで、イベント ハンドラーを追加します (または、最後の手順でヒントを使用した場合は、それを入力します)。

    private void ImageGridView_ItemClick(object sender, ItemClickEventArgs e)
    {
        this.Frame.Navigate(typeof(DetailPage), e.ClickedItem);
    }
    

    このメソッドは、クリックされた項目を渡して詳細ページに移動します。これは、ページを初期化するために ImageFileInfo によって使用される オブジェクトです。 このチュートリアルでは、そのメソッドを実装する必要はありませんが、その動作を確認することができます。

  3. (省略可能)現在選択されているイメージで動作する、以前のプレイポイントで追加したすべてのコントロールを削除またはコメントアウトします。 それらを維持しても何も損なわれませんが、詳細ページに移動せずに画像を選択するのがずっと困難になりました。

2 つのページを接続したので、アプリを実行して見てみましょう。 編集ウィンドウのコントロール以外はすべて機能します。値を変更しようとすると応答しません。

ご覧のように、タイトル テキスト ボックスにタイトルが表示され、変更を入力できます。 変更をコミットするにはフォーカスを別のコントロールに変更する必要がありますが、画面の左上隅のタイトルはまだ更新されません。

すべてのコントロールは、パート 1 で説明したプレーンな x:Bind 式を使用して既にバインドされています。 思い出すと、これはすべての 1 回限りのバインドであることを意味します。これにより、値の変更が登録されない理由が説明されます。 これを修正するには、それらを双方向バインディングに変換する必要があります。

編集コントロールを対話形式にする

  1. DetailPage.xaml で、TextBlock という名前の TitleTextBlock とその後の RatingControl コントロールを見つけ、x:Bind の式を Mode=TwoWayを含むように更新します。

    以前:

    <TextBlock x:Name="TitleTextBlock"
               Text="{x:Bind item.ImageTitle}"
               ... >
    <muxc:RatingControl Value="{x:Bind item.ImageRating}"
                            ... >
    

    その後:

    <TextBlock x:Name="TitleTextBlock"
               Text="{x:Bind item.ImageTitle, Mode=TwoWay}"
               ... >
    <muxc:RatingControl Value="{x:Bind item.ImageRating, Mode=TwoWay}"
                            ... >
    
  2. 評価コントロールの後に来るすべてのエフェクトスライダーに対して同じことを行います。

    <Slider Header="Exposure"    ... Value="{x:Bind item.Exposure, Mode=TwoWay}" ...
    <Slider Header="Temperature" ... Value="{x:Bind item.Temperature, Mode=TwoWay}" ...
    <Slider Header="Tint"        ... Value="{x:Bind item.Tint, Mode=TwoWay}" ...
    <Slider Header="Contrast"    ... Value="{x:Bind item.Contrast, Mode=TwoWay}" ...
    <Slider Header="Saturation"  ... Value="{x:Bind item.Saturation, Mode=TwoWay}" ...
    <Slider Header="Blur"        ... Value="{x:Bind item.Blur, Mode=TwoWay}" ...
    

双方向モードは、予想どおり、どちらの側でも変更が発生するたびにデータが双方向に移動することを意味します。

前に説明した一方向バインドと同様に、これらの双方向バインドは、INotifyPropertyChanged クラスの ImageFileInfo 実装により、バインドされたプロパティが変更されるたびに UI を更新するようになりました。 ただし、双方向バインディングでは、ユーザーがコントロールを操作するたびに、値も UI からバインドされたプロパティに移動します。 XAML 側では、これ以上は必要ありません。

アプリを実行し、編集コントロールを試します。 ご覧のように、変更するとイメージの値に影響が出るようになり、メイン ページに戻ったときにそれらの変更は保持されます。

パート 6: 関数バインドを使用して値を書式設定する

最後の問題が 1 つ残っています。 効果スライダーを移動しても、その横のラベルは変わりません。

に既定のラベル値 を持つ効果スライダーを適用する

このチュートリアルの最後の部分では、表示用のスライダー値を書式設定するバインドを追加します。

効果スライダー ラベルをバインドし、表示の値を書式設定する

  1. TextBlock スライダーの後の Exposure を見つけ、Text の値を次に示すバインド式に置き換えます。

    以前:

    <Slider Header="Exposure" ... />
    <TextBlock ... Text="0.00" />
    

    その後:

    <Slider Header="Exposure" ... />
    <TextBlock ... Text="{x:Bind item.Exposure.ToString('N', culture), Mode=OneWay}" />
    

    メソッドの戻り値にバインドするため、これは関数バインドと呼ばれます。 データ テンプレートを使用している場合は、ページの分離コードまたは x:DataType 型を使用してメソッドにアクセスできる必要があります。 この場合、このメソッドは使い慣れた .NET ToString メソッドです。これは、ページの item プロパティを通じてアクセスし、アイテムの Exposure プロパティを介してアクセスします。 (これは、接続のチェーン内で深く入れ子になっているメソッドとプロパティにバインドする方法を示しています)。

    関数バインドは、他のバインディング ソースをメソッド引数として渡すことができるため、表示用の値を書式設定するための理想的な方法です。バインド式は、一方向モードで期待どおりにそれらの値の変更をリッスンします。 この例では、カルチャの 引数は、バックエンド コードで実装されている不変のフィールドへの参照ですが、PropertyChanged イベントを発生させるプロパティであることも容易に想像できます。 その場合、プロパティ値を変更すると、x:Bind 式は新しい値で ToString を呼び出し、結果で UI を更新します。

  2. 他のエフェクトスライダーのラベルである TextBlockに対しても同じことを行います。

    <Slider Header="Temperature" ... />
    <TextBlock ... Text="{x:Bind item.Temperature.ToString('N', culture), Mode=OneWay}" />
    
    <Slider Header="Tint" ... />
    <TextBlock ... Text="{x:Bind item.Tint.ToString('N', culture), Mode=OneWay}" />
    
    <Slider Header="Contrast" ... />
    <TextBlock ... Text="{x:Bind item.Contrast.ToString('N', culture), Mode=OneWay}" />
    
    <Slider Header="Saturation" ... />
    <TextBlock ... Text="{x:Bind item.Saturation.ToString('N', culture), Mode=OneWay}" />
    
    <Slider Header="Blur" ... />
    <TextBlock ... Text="{x:Bind item.Blur.ToString('N', culture), Mode=OneWay}" />
    

これで、アプリを実行すると、スライダー ラベルを含め、すべてが機能します。

動作ラベルが されたエフェクト スライダー

結論

このチュートリアルでは、データ バインディングについて説明し、使用可能な機能の一部を示しました。 まとめる前に注意が必要です。すべてがバインド可能なわけではありません。また、接続しようとしている値がバインドしようとしているプロパティと互換性がない場合があります。 バインドには多くの柔軟性がありますが、あらゆる状況で機能するわけではありません。

バインディングによって対処されない問題の 1 つの例は、詳細ページのズーム機能と同様に、コントロールにバインドする適切なプロパティがない場合です。 このズーム スライダーは、画像を表示する ScrollViewer と対話する必要がありますが、ScrollViewerChangeView メソッドでのみ更新できます。 この場合、従来のイベント ハンドラーを使用して、ScrollViewer とズーム スライダーの同期を維持します。詳細については、ZoomSlider_ValueChangedMainImageScroll_ViewChanged メソッドと DetailPage メソッドを参照してください。

ただし、バインドは、コードを簡略化し、UI ロジックをデータ ロジックから分離するための強力で柔軟な方法です。 これにより、この分断の両側を調整し、その結果、もう一方の側にバグを導入するリスクを軽減することが簡単になります。

UI とデータの分離の 1 つの例として、ImageFileInfo.ImageTitle プロパティがあります。 このプロパティ (および ImageRating プロパティ) は、パート 4 で作成した ItemSize プロパティとは若干異なります。これは、値がフィールドではなくファイル メタデータ (ImageProperties 型を介して公開) に格納されるためです。 さらに、ImageTitle は、ファイル メタデータにタイトルがない場合に ImageName 値 (ファイル名に設定) を返します。

public string ImageTitle
{
    get => String.IsNullOrEmpty(ImageProperties.Title) ? ImageName : ImageProperties.Title;
    set
    {
        if (ImageProperties.Title != value)
        {
            ImageProperties.Title = value;
            var ignoreResult = ImageProperties.SavePropertiesAsync();
            OnPropertyChanged();
        }
    }
}

ご覧のように、セッターは ImageProperties.Title プロパティを更新し、SavePropertiesAsync を呼び出して新しい値をファイルに書き込みます。 (これは非同期メソッドですが、プロパティで await キーワードを使用することはできません。プロパティのゲッターとセッターはすぐに完了する必要があるため、これは望んでいません。そのため、代わりにメソッドを呼び出し、返される Task オブジェクトを無視します)。

さらに進む

このラボを完了したので、問題に取り組むための十分なバインディング知識が得ました。

ご覧のように、詳細ページでズーム レベルを変更すると、後方に移動してから同じ画像をもう一度選択すると、自動的にリセットされます。 各画像のズーム レベルを個別に保持および復元する方法を理解できますか? がんばってください。

このチュートリアルで必要なすべての情報が必要ですが、さらにガイダンスが必要な場合は、データ バインディングドキュメントをクリックするだけで済みます。 ここから始めます。