プレースホルダー画像と定型テキストを含む UI を設計した場合、このチュートリアルでは、データ バインディングを使用して実際のデータに接続する方法を示します。 データの書式設定、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 サンプルでは、次のようなバインドを使用して、メイン 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 が更新されます。
この動作をテストするには、現在選択されているイメージを削除するボタンを一時的に追加します。 イメージを選択すると詳細ページに移動するため、このボタンは最終バージョンではありません。 ただし、最終的な PhotoLab サンプルでは、XAML が (ObservableCollection<T> メソッド呼び出しを介して) ページ コンストラクターで初期化されますが、InitializeComponent コレクションは後で Images メソッドに設定されるため、GetItemsAsyncの動作は依然として重要です。
削除ボタンを追加する
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}" />
これで、アプリを実行すると、スライダー ラベルを含め、すべてが機能します。
されたエフェクト スライダー
Binding と x:Bind の違い
UWP アプリで XAML でデータ バインディングを作成する場合は、 Binding と x:Bindを選択できます。 主な違いを次に示します。
-
x:Bind: コンパイル時の検証を可能にし、パフォーマンスを向上させ、厳密な型指定を行う。 これは、コンパイル時にデータ構造が認識されるシナリオに最適です。 -
Binding: 実行時の評価と、実行時にデータ構造が決定される場合など、動的なシナリオに対する柔軟性が向上します。
x:Bind でサポートされていないシナリオ
x:Bindは非常に効率的ですが、特定のシナリオでは制限があります。
-
動的データ構造:
x:Bindは、実行時にデータ構造が決定されるときに使用できません。 -
要素間バインド: 2 つの UI 要素間の直接バインドは、
x:Bindではサポートされていません。 -
DataContext 継承:
Bindingとは異なり、x:Bindは親要素のDataContextを自動的に継承しません。 -
双方向バインド:
x:Bindは双方向バインディングをサポートし、UI からソース プロパティに変更を戻せるようにします。 ソース プロパティが (一方向または双方向のバインディングで) 変更されたときに UI を更新するには、データ オブジェクトにINotifyPropertyChangedを実装する必要があります。
詳細と例については、次のリソースを参照してください。
結論
このチュートリアルでは、データ バインディングについて説明し、使用可能な機能の一部を示しました。 まとめる前に注意が必要です。すべてがバインド可能なわけではありません。また、接続しようとしている値がバインドしようとしているプロパティと互換性がない場合があります。 バインドには多くの柔軟性がありますが、あらゆる状況で機能するわけではありません。
バインディングによって対処されない問題の 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 オブジェクトを無視します)。
さらに進む
このラボを完了したので、問題に取り組むための十分なバインディング知識が得ました。
ご覧のように、詳細ページでズーム レベルを変更すると、後方に移動してから同じ画像をもう一度選択すると、自動的にリセットされます。 各画像のズーム レベルを個別に保持および復元する方法を理解できますか? がんばってください。
このチュートリアルで必要なすべての情報が必要ですが、さらにガイダンスが必要な場合は、データ バインディングドキュメントをクリックするだけで済みます。 ここから始めます。