プレースホルダー画像、"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 サンプルの簡略化されたバージョンから始めます。
サンプルの GitHub ページに移動します:https://github.com/Microsoft/Windows-appsample-photo-lab。
次に、サンプルを複製またはダウンロードする必要があります。 複製またはダウンロード ボタンを選択します。 サブメニューが表示されます。
[複製またはダウンロード] メニュー
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
ディレクトリを参照します。Photolab.sln
をダブルクリックして、Visual Studio でソリューションを開きます。
パート 1: プレースホルダーを置き換える
ここでは、データ テンプレート XAML で 1 回限りのバインドを作成して、プレースホルダー コンテンツではなく、実際の画像と画像メタデータを表示します。
1 回限りのバインドは読み取り専用で変更されないデータ用です。つまり、高パフォーマンスで簡単に作成できるため、GridView
コントロールと ListView
コントロールに大きなデータ セットを表示できます。
プレースホルダーを 1 回限りのバインドに置き換える
xaml-basics-starting-points\data-binding
フォルダーを開き、Visual Studio でPhotoLab.sln
ファイルを起動します。ソリューション プラットフォーム が Arm ではなく x86 または x64 に設定されていることを確認してから、アプリを実行します。 これは、バインドが追加される前の UI プレースホルダーを含むアプリの状態を示しています。
を使用してアプリを実行する
MainPage.xaml を開き、
DataTemplate
という名前の を検索します。 このテンプレートは、データ バインディングを使用するように更新します。以前:
<DataTemplate x:Key="ImageGridView_DefaultItemTemplate">
x:Key
値は、データ オブジェクトを表示するためにこのテンプレートを選択するためにImageGridView
によって使用されます。テンプレートに
x:DataType
値を追加します。その後:
<DataTemplate x:Key="ImageGridView_DefaultItemTemplate" x:DataType="local:ImageFileInfo">
x:DataType
は、これがテンプレートである型を示します。 この場合は、ImageFileInfo
クラスのテンプレートです (local:
は、ファイルの先頭付近にある xmlns 宣言で定義されているローカル名前空間を示します)。x:DataType
は、次に説明するように、データ テンプレートでx:Bind
式を使用する場合に必要です。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 キーを押してバインドできるプロパティの一覧を表示します。他の 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 のトリックを使用して表示するプロパティを見つけます。
パート 2: バインドを使用してギャラリー UI をイメージに接続する
ここでは、ギャラリー ビューをイメージ コレクションに接続するために、ページ XAML で 1 回限りのバインドを作成します。これを分離コードで行う既存の手続き型コードを置き換えます。 また、[削除] ボタンを作成して、イメージがコレクションから削除されたときにギャラリー ビューがどのように変化するかを確認します。 同時に、従来のイベント ハンドラーよりも柔軟性を高めるために、イベントをイベント ハンドラーにバインドする方法について説明します。
これまでに説明したすべてのバインディングはデータ テンプレート内にあり、x:DataType
値によって示されるクラスのプロパティを参照します。 ページの残りの XAML はどうですか?
データ テンプレートの外部にある x:Bind
式は、常にページ自体にバインドされます。 つまり、分離コードに配置したものや XAML で宣言したものを、ページ上の他の UI コントロールのカスタム プロパティやプロパティ(x:Name
値がある限り)を含めて参照できます。
PhotoLab サンプルでは、このようなバインドの用途の 1 つとして、コードビハインドで行う代わりに、メイン GridView
コントロールを画像コレクションに直接接続するという方法があります。 後で、他の例が表示されます。
メインの GridView コントロールを Images コレクションにバインドする
MainPage.xaml.csで、
GetItemsAsync
メソッドを見つけて、ItemsSource
を設定するコードを削除します。以前:
ImageGridView.ItemsSource = Images;
その後:
// Replaced with XAML binding: // ImageGridView.ItemsSource = Images;
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 サンプルで引き続き重要です。
削除ボタンを追加する
MainPage.xaml で、MainCommandBar
名前の を見つけて、ズーム ボタンの前に新しいボタンを追加します。 (ズーム コントロールはまだ機能しません。これらは、チュートリアルの次の部分でフックします)。 <AppBarButton Icon="Delete" Label="Delete selected image" Click="{x:Bind DeleteSelectedImage}" />
既に XAML に慣れている場合、この
Click
値は通常とは異なる場合があります。 以前のバージョンの XAML では、これを特定のイベント ハンドラーシグネチャを持つメソッドに設定する必要がありました。これには、通常、イベント送信者のパラメーターとイベント固有の引数オブジェクトが含まれます。 イベント引数が必要な場合でもこの手法を使用できますが、x:Bind
を使用すると、他のメソッドにも接続できます。 たとえば、イベント データが不要な場合は、ここで行うのと同様に、パラメーターのないメソッドに接続できます。MainPage.xaml.csで、
DeleteSelectedImage
メソッドを追加します。private void DeleteSelectedImage() => Images.Remove(ImageGridView.SelectedItem as ImageFileInfo);
このメソッドは、選択したイメージを
Images
コレクションから削除するだけです。
次に、アプリを実行し、ボタンを使用していくつかのイメージを削除します。 ご覧のように、データ バインディングと ObservableCollection<T>
の種類により、UI は自動的に更新されます。
注
このコードは、実行中のアプリの ImageFileInfo
コレクションから Images
インスタンスのみを削除します。 コンピューターからイメージ ファイルは削除されません。
パート 3: ズーム スライダーを設定する
このパートでは、データ テンプレート内のコントロールから、テンプレートの外側にあるズーム スライダーへの一方向バインドを作成します。 また、TextBlock.Text
や Image.Source
などの最も明白なものだけでなく、多くのコントロール プロパティでデータ バインディングを使用できることも学習します。
画像データ テンプレートをズーム スライダーにバインドする
DataTemplate
という名前のImageGridView_DefaultItemTemplate
を見つけ、テンプレートの上部にある**Height**
コントロールのWidth
とGrid
の値を置き換えます。の前に
<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:Name
値 Page
を参照するようになります。
注
既定では、Binding
式は 1方法です。つまり、バインドされたプロパティ値が変更されると UI が自動的に更新されます。
これに対し、x:Bind
の既定値は一度限りです。つまり、バインドされたプロパティへの変更は無視されます。 これは、最も高パフォーマンスのオプションであり、ほとんどのバインディングは静的な読み取り専用データに対して行われるため、これが既定です。
ここでのレッスンでは、値を変更できるプロパティで x:Bind
を使用する場合は、必ず Mode=OneWay
または Mode=TwoWay
を追加してください。 この例については、次のセクションで説明します。
アプリを実行し、スライダーを使用してイメージ テンプレートのディメンションを変更します。 ご覧のように、効果は多くのコードを必要とせずに非常に強力です。
を示すズーム スライダーを使用してアプリを実行する
注
課題として、他の UI プロパティをズーム スライダー Value
プロパティにバインドするか、ズーム スライダーの後に追加する他のスライダーにバインドしてみてください。 たとえば、既定値が FontSize
の新しいスライダーに TitleTextBlock
の プロパティをバインドできます。 適切な最小値と最大値を設定してください。
パート 4: ズーム エクスペリエンスを向上させる
このパートでは、分離コードにカスタム ItemSize
プロパティを追加し、イメージ テンプレートから新しいプロパティへの一方向バインドを作成します。
ItemSize
の値は、ズーム スライダーや、[画面に合わせる ] トグルやウィンドウ サイズなどのその他の要因によって更新され、より洗練されたエクスペリエンスを実現します。
組み込みのコントロール プロパティとは異なり、カスタム プロパティは、一方向と双方向のバインドを使用しても、UI を自動的に更新しません。 1 回の バインドで正常に動作しますが、プロパティの変更を実際に UI に表示する場合は、いくつかの作業を行う必要があります。
UI を更新するように ItemSize プロパティを作成する
MainPage.xaml.csで、
MainPage
インターフェイスを実装するように、INotifyPropertyChanged
クラスのシグネチャを変更します。以前:
public sealed partial class MainPage : Page
その後:
public sealed partial class MainPage : Page, INotifyPropertyChanged
これにより、
MainPage
に、UI を更新するためにバインディングがリッスンできるPropertyChanged
イベント (次に追加) があることをバインディング システムに通知します。PropertyChanged
イベントをMainPage
クラスに追加します。public event PropertyChangedEventHandler PropertyChanged;
このイベントは、
INotifyPropertyChanged
インターフェイスに必要な完全な実装を提供します。 ただし、何らかの影響を与えるためには、カスタム プロパティでイベントを明示的に発生させる必要があります。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 を更新できるように変更が通知されます。MainPage.xaml で、
DataTemplate
という名前のImageGridView_DefaultItemTemplate
を見つけ、テンプレートの上部にあるHeight
コントロールのWidth
とGrid
の値を置き換えます。 (このチュートリアルの前の部分でコントロール間バインディングを行った場合、Value
をItemSize
に置き換え、ZoomSlider
をpage
に置き換えるだけです。Height
とWidth
の両方でこれを行ってください!の前に
<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 プロパティ値を更新する
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; } }
MainPage.xaml で、ファイルの先頭に移動し、
SizeChanged
要素にPage
イベント バインドを追加します。以前:
<Page x:Name="page"
その後:
<Page x:Name="page" SizeChanged="{x:Bind DetermineItemSize}"
(
Slider
セクションで)ZoomSlider
という名前のPage.Resources
を見つけて、ValueChanged
イベント バインディングを追加します。以前:
<Slider x:Name="ZoomSlider"
その後:
<Slider x:Name="ZoomSlider" ValueChanged="{x:Bind DetermineItemSize}"
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 をアタッチする
MainPage.xaml で、
GridView
という名前のImageGridView
を見つけます。 項目をクリックできるようにするには、IsItemClickEnabled
をTrue
に設定し、ItemClick
イベント ハンドラーを追加します。ヒント
コピー/貼り付けではなく、次の変更を入力すると、"<新しいイベント ハンドラー>" という IntelliSense ポップアップが表示されます。 Tab キーを押すと、既定のメソッド ハンドラー名が値に入力され、次の手順で示すメソッドが自動的にスタブされます。 その後、F12 キーを押して、バックエンドコード内のメソッドへジャンプできます。
以前:
<GridView x:Name="ImageGridView">
その後:
<GridView x:Name="ImageGridView" IsItemClickEnabled="True" ItemClick="ImageGridView_ItemClick">
注
ここでは、x:Bind 式ではなく、従来のイベント ハンドラーを使用しています。 これは、次に示すように、イベント データを表示する必要があるためです。
MainPage.xaml.csで、イベント ハンドラーを追加します (または、最後の手順でヒントを使用した場合は、それを入力します)。
private void ImageGridView_ItemClick(object sender, ItemClickEventArgs e) { this.Frame.Navigate(typeof(DetailPage), e.ClickedItem); }
このメソッドは、クリックされた項目を渡して詳細ページに移動します。これは、ページを初期化するために
ImageFileInfo
によって使用される オブジェクトです。 このチュートリアルでは、そのメソッドを実装する必要はありませんが、その動作を確認することができます。(省略可能)現在選択されているイメージで動作する、以前のプレイポイントで追加したすべてのコントロールを削除またはコメントアウトします。 それらを維持しても何も損なわれませんが、詳細ページに移動せずに画像を選択するのがずっと困難になりました。
2 つのページを接続したので、アプリを実行して見てみましょう。 編集ウィンドウのコントロール以外はすべて機能します。値を変更しようとすると応答しません。
ご覧のように、タイトル テキスト ボックスにタイトルが表示され、変更を入力できます。 変更をコミットするにはフォーカスを別のコントロールに変更する必要がありますが、画面の左上隅のタイトルはまだ更新されません。
すべてのコントロールは、パート 1 で説明したプレーンな x:Bind
式を使用して既にバインドされています。 思い出すと、これはすべての 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}" ... >
評価コントロールの後に来るすべてのエフェクトスライダーに対して同じことを行います。
<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 つ残っています。 効果スライダーを移動しても、その横のラベルは変わりません。
を持つ効果スライダーを適用する
このチュートリアルの最後の部分では、表示用のスライダー値を書式設定するバインドを追加します。
効果スライダー ラベルをバインドし、表示の値を書式設定する
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
型を使用してメソッドにアクセスできる必要があります。 この場合、このメソッドは使い慣れた .NETToString
メソッドです。これは、ページの item プロパティを通じてアクセスし、アイテムのExposure
プロパティを介してアクセスします。 (これは、接続のチェーン内で深く入れ子になっているメソッドとプロパティにバインドする方法を示しています)。関数バインドは、他のバインディング ソースをメソッド引数として渡すことができるため、表示用の値を書式設定するための理想的な方法です。バインド式は、一方向モードで期待どおりにそれらの値の変更をリッスンします。 この例では、カルチャの 引数は、バックエンド コードで実装されている不変のフィールドへの参照ですが、
PropertyChanged
イベントを発生させるプロパティであることも容易に想像できます。 その場合、プロパティ値を変更すると、x:Bind
式は新しい値でToString
を呼び出し、結果で UI を更新します。他のエフェクトスライダーのラベルである
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
と対話する必要がありますが、ScrollViewer
は ChangeView
メソッドでのみ更新できます。 この場合、従来のイベント ハンドラーを使用して、ScrollViewer
とズーム スライダーの同期を維持します。詳細については、ZoomSlider_ValueChanged
の MainImageScroll_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
オブジェクトを無視します)。
さらに進む
このラボを完了したので、問題に取り組むための十分なバインディング知識が得ました。
ご覧のように、詳細ページでズーム レベルを変更すると、後方に移動してから同じ画像をもう一度選択すると、自動的にリセットされます。 各画像のズーム レベルを個別に保持および復元する方法を理解できますか? がんばってください。
このチュートリアルで必要なすべての情報が必要ですが、さらにガイダンスが必要な場合は、データ バインディングドキュメントをクリックするだけで済みます。 ここから始めます。