チュートリアル: WinUI 3 を使ってシンプルなフォト ビューアーを作成する

注意

WinUI 3 の利点と、その他のアプリの種類のオプションについては、「アプリ開発オプションの概要」を参照してください。

このトピックでは、Visual Studio で新しい WinUI 3 プロジェクトを作成し、写真を表示する単純なアプリをビルドするプロセスについて説明します。 ここでは、コントロール、レイアウト パネル、データ バインディングを使用します。 また、XAML マークアップ ("宣言型") と C# コードまたは C++ コード ("命令型" または "手続き型") の両方を記述します。 トピック タイトルの上にある言語ピッカーを使用して、C# または C++/WinRT を選択します。

ヒント

このトピックのソース コードは、C# と C++/WinRT の両方で提供されています。 C++ の開発者は、ここで示すコードのしくみに関する詳細と概念を C++/WinRT ドキュメントで確認できます。 関連するトピックには、XAML コントロール: C++/WinRT プロパティへのバインドXAML 項目コントロール: C++/WinRT コレクションへのバインドフォト エディター C++/WinRT サンプル アプリケーションなどがあります。

手順 1: Windows App SDK 用のツールをインストールする

開発コンピューターをセット アップするには、「Windows App SDK 用のツールをインストールする」を参照してください。 必要に応じて、「最初の WinUI 3 プロジェクトを作成する」に従うこともできます。

重要

リリース ノートのトピックと、「Windows App SDK のリリース チャネル」のトピックをご覧ください。 各チャネルのリリース ノートがあります。 これらのリリース ノートに記載されている "制限事項と既知の問題" を必ず確認してください。このチュートリアルに従ったり、ビルドしたアプリを実行したりする場合の結果に影響を与える可能性があるためです。

手順 2: 新しいプロジェクトを作成する

Visual Studio で [Blank App, Packaged (WinUI 3 in Desktop)] (空のアプリ、パッケージ (WinUI 3 in Desktop)) プロジェクト テンプレートを使用して、新しい C# プロジェクトまたは C++ プロジェクトを作成します。 プロジェクトに「SimplePhotos」という名前を付け、(このチュートリアルで説明されているフォルダー構造と一致するように) [ソリューションとプロジェクトを同じディレクトリに配置する] をオフにします。 クライアント オペレーティング システムの最新リリース (プレビューではないもの) をターゲットにすることができます。

手順 3: 資産ファイルをコピーする

ビルドするアプリには、画像ファイル (表示される写真) はアセット ファイルの形式で保持されます。 このセクションでは、プロジェクトにこのようなアセットを追加します。 ただし、最初にファイルのコピーを取得する必要があります。

  1. そのため、Windows App SDK サンプル リポジトリをクローン (または .zip として ダウンロード) します (「WindowsAppSDK-Samples」を参照)。 これを行うと、使用するアセット ファイルがフォルダー \WindowsAppSDK-Samples\Samples\PhotoEditor\cs-winui\Assets\Samples に見つかります (このフォルダーは、C# と C++/WinRT プロジェクトの両方に使用します)。 オンラインでこれらのファイルをリポジトリ内に表示するには、WindowsAppSDK-Samples/Samples/PhotoEditor/cs-winui/Assets/Samples/ にアクセスします。

  2. エクスプローラーでその Samples フォルダーを選択し、クリップボードにコピーします。

  1. Visual Studio のソリューション エクスプローラーに移動します。 Assets フォルダー (プロジェクト ノードの子) を右クリックし、[エクスプローラーでフォルダーを開く] をクリックします。 これによって Assets フォルダーがエクスプローラーで開きます。

  2. コピーした Samples フォルダーを (Assets フォルダーに) 貼り付けます。

手順4: GridView コントロールを追加する

アプリでは、写真の行と列を表示する必要があります。 つまり、画像のグリッドです。 このような UI の場合、使用する主なコントロールはリスト ビューとグリッド ビューです。

  1. MainWindow.xamlを開きます。 現在 Window 要素があり、その中に StackPanel レイアウト パネルがあります。 StackPanel 内には Button コントロールがあり、イベント ハンドラー メソッドにフックされています。

    アプリのメイン ウィンドウは、アプリを実行したときに最初に表示されるビューを表しています。 ビルドするアプリでは、メイン ウィンドウのジョブは Samples フォルダーから写真を読み込み、それらの画像のタイル ビューをこれらに関するさまざまな情報と共に表示することです。

  2. StackPanel および Button マークアップを、次の一覧に示す Grid レイアウト パネルと GridView コントロールに置き換えます。

    <Window ...>
        <Grid>
            <GridView x:Name="ImageGridView"/>
        </Grid>
    </Window>
    

    ヒント

    x:Name は、XAML およびコードビハインド内の任意の場所から参照できるように XAML 要素を表しています。

  3. C#MainWindow.xaml.cs を開き、myButton_Click メソッドを削除します。

  4. C++/WinRTMainWindow.xaml.hMainWindow.xaml.cpp を開き、myButton_Click メソッドを削除します。

今すぐビルドして実行 "できます" が、この段階ではウィンドウは空になります。 GridView コントロールに何かを表示するには、表示するオブジェクトのコレクションを指定する必要があります。 次に、これに着手します。

先ほど説明したいくつかの種類の背景情報については、レイアウト パネルに関する記事と「Windows アプリ用のコントロール」を参照してください。

手順 5: ImageFileInfo モデル

(モデル、ビュー、ビュー モデルの意味における) "モデル" は、ある程度は現実世界のオブジェクトまたは概念 (銀行口座など) を表すクラスです。 これは、その現実世界のものの "抽象化" です。 このセクションでは、ImageFileInfo という新しいクラスをプロジェクトに追加します。 ImageFileInfo は、写真などの画像ファイルのモデルになります。 このセクションでは、アプリのユーザー インターフェイス (UI) で写真を表示できるようにするための手順について説明します。

ヒント

以下のコード例を準備するために、"監視可能" という用語を紹介しましょう。 XAML コントロールに動的にバインドできるプロパティ (プロパティ値が変更されるたびに UI が更新されるようにする) は、"監視可能な" プロパティと呼ばれます。 この概念は、オブザーバー パターンと呼ばれるソフトウェアの設計パターンに基づいています。 このチュートリアルでビルドするアプリでは、ImageFileInfo モデルのプロパティは変更されません。 しかし、それでも、INotifyPropertyChanged インターフェイスを実装することで、ImageFileInfo を監視可能にする方法を示します。

  1. プロジェクト ノード (SimplePhotos) を右クリックし、[追加]>[新しい項目] をクリックします。[C# の項目]>[コード] で、[クラス] を選択します。 名前を ImageFileInfo.cs に設定し、[追加] をクリックします。

  2. ImageFileInfo.cs の内容を、以下に示すコード リストで置き換えます。

    using Microsoft.UI.Xaml.Media.Imaging;
    using System;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Threading.Tasks;
    using Windows.Storage;
    using Windows.Storage.FileProperties;
    using Windows.Storage.Streams;
    
    namespace SimplePhotos
    {
        public class ImageFileInfo : INotifyPropertyChanged
        {
            public ImageFileInfo(ImageProperties properties,
                StorageFile imageFile,
                string name,
                string type)
            {
                ImageProperties = properties;
                ImageName = name;
                ImageFileType = type;
                ImageFile = imageFile;
                var rating = (int)properties.Rating;
                var random = new Random();
                ImageRating = rating == 0 ? random.Next(1, 5) : rating;
            }
    
            public StorageFile ImageFile { get; }
    
            public ImageProperties ImageProperties { get; }
    
            public async Task<BitmapImage> GetImageSourceAsync()
            {
                using IRandomAccessStream fileStream = await ImageFile.OpenReadAsync();
    
                // Create a bitmap to be the image source.
                BitmapImage bitmapImage = new();
                bitmapImage.SetSource(fileStream);
    
                return bitmapImage;
            }
    
            public async Task<BitmapImage> GetImageThumbnailAsync()
            {
                StorageItemThumbnail thumbnail = 
                    await ImageFile.GetThumbnailAsync(ThumbnailMode.PicturesView);
                // Create a bitmap to be the image source.
                var bitmapImage = new BitmapImage();
                bitmapImage.SetSource(thumbnail);
                thumbnail.Dispose();
    
                return bitmapImage;
            }
    
            public string ImageName { get; }
    
            public string ImageFileType { get; }
    
            public string ImageDimensions => $"{ImageProperties.Width} x {ImageProperties.Height}";
    
            public string ImageTitle
            {
                get => string.IsNullOrEmpty(ImageProperties.Title) ? ImageName : ImageProperties.Title;
                set
                {
                    if (ImageProperties.Title != value)
                    {
                        ImageProperties.Title = value;
                        _ = ImageProperties.SavePropertiesAsync();
                        OnPropertyChanged();
                    }
                }
            }
    
            public int ImageRating
            {
                get => (int)ImageProperties.Rating;
                set
                {
                    if (ImageProperties.Rating != value)
                    {
                        ImageProperties.Rating = (uint)value;
                        _ = ImageProperties.SavePropertiesAsync();
                        OnPropertyChanged();
                    }
                }
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected void OnPropertyChanged([CallerMemberName] string propertyName = null) =>
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    
  3. ImageFileInfo.cs ファイルを保存し、閉じます。

手順 6: 画像のコレクションのプロパティを定義して設定する

このセクションでは、MainWindow クラスに新しいプロパティを追加します。 (Images という名前の) プロパティは、表示する画像を含むコレクション クラスになります。

  1. このようなプロパティを MainWindow.xaml.cs で定義します。

    ...
    using System.Collections.ObjectModel;
    ...
    namespace SimplePhotos
    {
        public sealed partial class MainWindow : Window
        {
            public ObservableCollection<ImageFileInfo> Images { get; } = 
                new ObservableCollection<ImageFileInfo>();
            ...
        }
    }
    
  2. 新しいコレクション プロパティに画像を設定するコードは、以下の GetItemsAsync メソッドと LoadImageInfoAsync メソッドに示されています。 using ディレクティブと 2 つのメソッド実装も MainWindow.xaml.cs に貼り付けます。 これらのメソッドは MainWindow クラスのメンバーであるため、上記の Images プロパティの場合と同じように、クラス内に貼り付けます。

    ...
    using System.Threading.Tasks;
    using Windows.ApplicationModel;
    using Windows.Storage;
    using Windows.Storage.Search;
    ...
    private async Task GetItemsAsync()
    {
        StorageFolder appInstalledFolder = Package.Current.InstalledLocation;
        StorageFolder picturesFolder = await appInstalledFolder.GetFolderAsync("Assets\\Samples");
    
        var result = picturesFolder.CreateFileQueryWithOptions(new QueryOptions());
    
        IReadOnlyList<StorageFile> imageFiles = await result.GetFilesAsync();
        foreach (StorageFile file in imageFiles)
        {
            Images.Add(await LoadImageInfoAsync(file));
        }
    
        ImageGridView.ItemsSource = Images;
    }
    
    public async static Task<ImageFileInfo> LoadImageInfoAsync(StorageFile file)
    {
        var properties = await file.Properties.GetImagePropertiesAsync();
        ImageFileInfo info = new(properties, 
                                 file, file.DisplayName, file.DisplayType);
    
        return info;
    }
    
  3. このセクションで最後に行う必要があるのは、GetItemsAsync を呼び出すように MainWindow のコンストラクターを更新することです。

    public MainWindow()
    {
        ...
        GetItemsAsync();
    }
    

(手順に正常に従ったことを確認するために) 必要に応じてこの時点でビルドして実行できますが、この段階ではウィンドウに表示される内容はあまりありません。 これまでに行ったことは、ImageFileInfo 型のオブジェクトのコレクションをレンダリングするように GridView に指示することであり、GridView ではまだそれを行う方法を十分に認識していないためです。

Images プロパティは ImageFileInfo オブジェクトの監視可能なコレクションであることを覚えておいてください。 また、GetItemsAsync の最後の行では、その項目のソース (ItemsSource) が Images プロパティであることを GridView (ImageGridView という名前) に通知します。 GridView のジョブでは、それらの項目を表示します。

ただし、ImageFileInfo クラスについて GridView にはまだ何も指示していません。 そのため、今のところ実行できるのは、コレクション内の各 ImageFileInfo オブジェクトの ToString 値を表示することです。 既定では、これは型の名前だけです。 次のセクションでは、ImageFileInfo オブジェクトの表示方法を定義するデータ テンプレートを作成します。

ヒント

上では "監視可能なコレクション" という用語を使用しました。 このチュートリアルでビルドするアプリでは、画像の数は変わりません (また、説明したように、各画像のプロパティの値も変わりません)。 それでも、データ バインディングを使用して最初に UI をデータに接続すると便利であり、これをお勧めします。 そのため、このようにします。

手順 7: データ テンプレートを追加する

まず、スケッチのようなプレースホルダー データ テンプレートを使用しましょう。 これは、いくつかのレイアウト オプションの探索が完了するまで役立ちます。 その後、実際の写真を表示するようにデータ テンプレートを更新できます。

ヒント

これは実際に非常に実用的な作業です。 UI がスケッチのように見える (つまり、再現性が低い) 場合、ユーザーは進んで即座のアイデアを提案したり試したりする (かなり大きな変更が必要になることがある) 傾向が強いことがわかっています。 それは、そのような変更は気軽に試すことができると (正しく) 推測しているからです。

一方、UI の外観の完成度が高いほど (再現性が高いほど)、現在の外観にするために多くの作業が行われたと (この場合も正しく) 推測します。 そのため、新しいアイデアを提案したり試したりする傾向が低くなります。

  1. MainWindow.xaml を開き、次のマークアップのようになるよう Window の内容を変更します。

    <Window ...>
        <Grid>
            <Grid.Resources>
                <DataTemplate x:Key="ImageGridView_ItemTemplate">
                    <Grid/>
                </DataTemplate>
            </Grid.Resources>
            <GridView x:Name="ImageGridView"
                    ItemTemplate="{StaticResource ImageGridView_ItemTemplate}">
            </GridView>
        </Grid>
    </Window>
    

    レイアウト ルートに、単純な DataTemplate リソースを追加し、キー ImageGridView_ItemTemplate を指定しました。 また、この同じキーを使用して GridViewItemTemplate を設定しました。 GridView などの項目コントロールには ItemTemplate プロパティがあります (前に見た ItemsSource プロパティと同様)。 項目テンプレートはデータ テンプレートです。コレクション内の各項目を表示するために使用されます。

    詳細については、「項目コンテナーとテンプレート」を参照してください。

  2. これで、データ テンプレートに対して数回の編集パス (その中の要素の追加や編集) を行って、より興味深く便利にすることができます。 ルートの Grid の Height と Width に 300、Margin に 8 を指定します。 次に、2 つの行定義を追加し、2 番目の行定義の Height を Auto に設定します。

    <DataTemplate x:Key="ImageGridView_ItemTemplate">
        <Grid Height="300"
              Width="300"
              Margin="8">
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
        </Grid>
    </DataTemplate>
    

    詳しくは、「配置、余白、パディング」をご覧ください。

  3. データ テンプレートには、各写真の画像、名前、ファイルの種類、ディメンション、評価を表示する必要があります。 そのため、それぞれ Image コントロール、いくつかの TextBlock コントロール、RatingControl コントロールを追加します。 StackPanel レイアウト パネル内にテキストをレイアウトします。 Image では、最初はプロジェクトのスケッチのような Microsoft Store ロゴをプレースホルダーとして表示します。

  4. これらのすべての編集の後、データ テンプレートの外観は次のようになります。

    <DataTemplate x:Key="ImageGridView_ItemTemplate">
        <Grid Height="300"
              Width="300"
              Margin="8">
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
    
            <Image x:Name="ItemImage"
                   Source="Assets/StoreLogo.png"
                   Stretch="Uniform" />
    
            <StackPanel Orientation="Vertical"
                        Grid.Row="1">
                <TextBlock Text="ImageTitle"
                           HorizontalAlignment="Center"
                           Style="{StaticResource SubtitleTextBlockStyle}" />
                <StackPanel Orientation="Horizontal"
                            HorizontalAlignment="Center">
                    <TextBlock Text="ImageFileType"
                               HorizontalAlignment="Center"
                               Style="{StaticResource CaptionTextBlockStyle}" />
                    <TextBlock Text="ImageDimensions"
                               HorizontalAlignment="Center"
                               Style="{StaticResource CaptionTextBlockStyle}"
                               Margin="8,0,0,0" />
                </StackPanel>
    
                <RatingControl Value="3" IsReadOnly="True"/>
            </StackPanel>
        </Grid>
    </DataTemplate>
    

ここでプロジェクトをビルドし、アプリを実行して、作成した項目テンプレートで GridView コントロールを確認します。 次に、項目がどのようにレイアウトされているかを見ていきます。いくつかのブラシを変更し、項目間にスペースを追加します。

The placeholder item template.

手順 8: 項目コンテナーのスタイルを編集する

GridView などの項目コントロールに関連するもう 1 つの概念は、"項目コンテナー" です。 項目コンテナーは、Content プロパティの値として項目を表示する "コンテンツ コントロール" です。 項目コントロールでは、いつでも画面に表示される項目を表示するために、必要な数の項目コンテナーを作成します。

コントロールである項目コンテナーには、スタイルとコントロール テンプレートがあります。 そのスタイルとコントロール テンプレートでは、項目コンテナーがさまざまな状態 (選択、ポインター オーバー、フォーカスなど) でどのように表示されるかを決定します。 また、見てきたように、項目テンプレート (データ テンプレート) によって、項目自体の外観が決まります。

GridView の場合、その項目コンテナーの種類は GridViewItem です。

このセクションでは、項目コンテナーのスタイルの設計に焦点を当てます。 そのため、GridViewItemStyle リソースを作成し、それを *GridViewItemContainerStyle として設定します。 スタイルでは、項目コンテナーの Background および Margin プロパティを設定して、灰色の背景と外側の余白を少し設定します。

  1. MainWindow.xaml で、データ テンプレートを配置したのと同じ Grid.Resources XML 要素に新しい Style リソースを追加します。

    <Grid>
        <Grid.Resources>
        ...
            <Style x:Key="ImageGridView_ItemContainerStyle"
                TargetType="GridViewItem">
                <Setter Property="Background" Value="Gray"/>
                <Setter Property="Margin" Value="8"/>
            </Style>
        </Grid.Resources>
    
  2. 次に、ImageGridView_ItemContainerStyle キーを使用して GridViewItemContainerStyle を設定します。

    <GridView x:Name="ImageGridView"
            ...
            ItemContainerStyle="{StaticResource ImageGridView_ItemContainerStyle}">
    </GridView>
    

アプリをビルドして実行し、この時点での結果を確認します。 ウィンドウのサイズを変更すると、GridView コントロールによって、項目がスペースに最適に収まるように再配置されます。 幅によっては、アプリ ウィンドウの右余白が大きくなることがあります。 GridView またはその内容が中央揃えの場合は、外観が良くなります。 そのため、次はこの点に関する対応を行います。

ヒント

実験したい場合は、Background および Margin セッターをさまざまな値に設定して、その結果がどうなるか試してみてください。

手順 9: レイアウトの実験

GridView 自体を中央に配置するのと、内容を中央に配置するのとどちらが最善なのか疑問に思うかもしれません。 最初に GridView を中心に配置してみましょう。

  1. GridView がウィンドウ内のどこにあり、レイアウトを試すと何が起こるかを正確かつ簡単に確認できるように、Background プロパティを赤に設定します。

    <GridView x:Name="ImageGridView"
            ...
            Background="Red">
    </GridView>
    
  2. 次に、HorizontalAlignment プロパティを Center に設定します。

    <GridView x:Name="ImageGridView"
            ...
            HorizontalAlignment="Center">
    </GridView>
    

    配置、余白、パディング」も参照してください。

ここでビルドして実行し、ウィンドウの幅を調整して試してみます。 GridView の赤い背景のいずれかの側に同じ量の空きスペースがあることがわかります。 そこで、画像を中央に配置するという目標を達成しました。 ただし、スクロール バーが GridView に属していて、ウィンドウには属していないということが以前よりも明確になりました。 そのため、ウィンドウを塗りつぶすために GridView を元に戻す必要があります。 (ウィンドウで GridView を中央に配置する代わりに) GridView で画像を中央に配置する必要があることを示しました。

  1. そのため、前の手順で追加した HorizontalAlignment 属性を削除します。

手順 10: 項目パネル テンプレートを編集する

項目コントロールでは、"項目パネル" と呼ばれるものの内部に項目コンテナーをレイアウトします。 GridView の "項目パネル テンプレート" を編集することで、使用するパネルの種類を定義し、そのパネルでプロパティを設定できます。 そのため、このセクションではこれを行います。

  1. MainWindow.xaml で、ItemsPanelTemplate リソースをリソース ディクショナリに追加します。 項目パネルは ItemsWrapGrid 型です。HorizontalAlignment プロパティを Center に設定します。

    <Grid>
        <Grid.Resources>
        ...
            <ItemsPanelTemplate x:Key="ImageGridView_ItemsPanelTemplate">
                <ItemsWrapGrid Orientation="Horizontal"
                               HorizontalAlignment="Center"/>
            </ItemsPanelTemplate>
        </Grid.Resources>
    
  2. 次に、ImageGridView_ItemsPanelTemplate キーを使用して GridViewItemsPanel を設定します。

    <GridView x:Name="ImageGridView"
            ...
            ItemsPanel="{StaticResource ImageGridView_ItemsPanelTemplate}">
    </GridView>
    

この時点でビルドして実行し、ウィンドウの幅を調整して試してみると、画像のいずれかの側では GridView の赤い背景の量が等しくなります。 また、GridView によってウィンドウが塗りつぶされるので、スクロール バーは、ユーザーの期待どおりにウィンドウの端とうまく合わせられます。

  1. レイアウトの実験が完了したので、GridView から Background="Red" を削除します。

手順 11: プレースホルダー画像を写真に置き換える

次は、スケッチの再現度をより高いレベルにします。つまり、プレースホルダー画像を実際のものに置き換え、"lorem ipsum" スタイルのプレースホルダー テキストを実際のデータに置き換えます。 最初に画像を処理しましょう。

重要

Assets\Samples フォルダー内の写真を表示するために使用する手法では、GridView の項目を段階的に更新する必要があります。 具体的には、ContainerContentChangingEventArgs.InRecycleQueueContainerContentChangingEventArgs.Phase プロパティの使用など、以下のコード例の ImageGridView_ContainerContentChangingShowImage メソッドのコードです。 詳しくは、「ListView と GridView の UI の最適化」をご覧ください。 しかし簡単に言えば、GridView によって、項目コンテナーの 1 つで項目を表示する準備ができたときに (イベントを通じて) 通知されます。 次に、項目コンテナーが更新ライフサイクルのどのフェーズになっているかを追跡して、写真データを表示する準備ができたかを判断できるようにします。

  1. MainWindow.xaml.cs で、ImageGridView_ContainerContentChanging という名前の新しいメソッドを MainWindow に追加します。 これはイベント処理メソッドであり、処理するイベントは ContainerContentChanging です。 また、ImageGridView_ContainerContentChanging が依存する ShowImage メソッドの実装も提供する必要があります。 using ディレクティブと 2 つのメソッド実装を MainWindow.xaml.cs に貼り付けます。

    ...
    using Microsoft.UI.Xaml.Controls;
    ...
    private void ImageGridView_ContainerContentChanging(
        ListViewBase sender,
        ContainerContentChangingEventArgs args)
    {
        if (args.InRecycleQueue)
        {
            var templateRoot = args.ItemContainer.ContentTemplateRoot as Grid;
            var image = templateRoot.FindName("ItemImage") as Image;
            image.Source = null;
        }
    
        if (args.Phase == 0)
        {
            args.RegisterUpdateCallback(ShowImage);
            args.Handled = true;
        }
    }
    
    private async void ShowImage(ListViewBase sender, ContainerContentChangingEventArgs args)
    {
        if (args.Phase == 1)
        {
            // It's phase 1, so show this item's image.
            var templateRoot = args.ItemContainer.ContentTemplateRoot as Grid;
            var image = templateRoot.FindName("ItemImage") as Image;
            var item = args.Item as ImageFileInfo;
            image.Source = await item.GetImageThumbnailAsync();
        }
    }
    
  1. 次に、MainWindow.xaml で、ImageGridView_ContainerContentChanging イベント ハンドラーを GridViewContainerContentChanging イベントに登録します。

    <GridView x:Name="ImageGridView"
            ...
            ContainerContentChanging="ImageGridView_ContainerContentChanging">
    </GridView>
    

手順 12: プレースホルダー テキストを実際のデータに置き換える

このセクションでは、1 回限りのデータ バインディングを使用します。 1 回限りのバインディングは、実行時に変更されないデータに適しています。 つまり、1 回限りのバインディングは高パフォーマンスで作成が簡単です。

  1. MainWindow.xaml で、ImageGridView_ItemTemplate データ テンプレート リソースを見つけます。 ここでは、そのジョブが ImageFileInfo クラスのテンプレートであることをデータ テンプレートに指示します。これは、GridView で表示している項目の種類です。

  2. これを行うには、次のように x:DataType 値をテンプレートに追加します。

    <DataTemplate x:Key="ImageGridView_ItemTemplate"
                  x:DataType="local:ImageFileInfo">
        ...
    

    上記の local: 構文 (または開いている Window タグに既に含まれている xmlns:local 構文) に慣れていない場合は、XAML 名前空間と名前空間マッピングに関するページを参照してください。

    x:DataType を設定したので、データ テンプレートの x:Bind データ バインディング式を使用して、指定したデータ型のプロパティ (この場合は ImageFileInfo) にバインドできます。

  3. データ テンプレートで、最初の TextBlock 要素 (Text が現在 ImageTitle に設定されているもの) を見つけます。 次に示すように、Text の値を置き換えます。

    ヒント

    以下のマークアップをコピーして貼り付けるか、Visual Studio で IntelliSense を使用できます。 これを行うには、引用符の内側にある現在の値を選択し、「{」と入力します。 IntelliSense によって右中かっこが自動的に追加され、コード補完リストが表示されます。 x:Bind まで下にスクロールして、ダブルクリック "できます"。 ただし、「x:」と入力して (x:Bind がフィルター処理され補完リストの上部に来ます)、Tab キーを押す方が効率的な場合があります。 ここでスペース キーを押し、「ImageT」と入力 (補完リストの先頭にプロパティ名 ImageTitle が表示されるために必要なだけ入力) し、Tab キーを押します。

    <TextBlock Text="{x:Bind ImageTitle}"
        ... />
    

    x:Bind 式では、UI プロパティの値と data-object プロパティの値をリンクします。 もちろんこのためには、バインドできるプロパティをツールとランタイムで認識できるように、最初に x:DataType をその data-object の型に設定する必要があります。

    詳細については、{x:Bind} マークアップ拡張データ バインディングの詳細に関する記事を参照してください。

  4. 同様に、他の TextBlockRatingControl の値を置き換えます。 結果は次のとおりです。

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

ここでアプリをビルドして実行すると、プレースホルダーの代わりに、実際の写真と実際のテキスト (およびその他のデータ) が表示されます。 視覚的にも機能的にも、この単純で小さなアプリはこれで完成です。 しかし、最終章では、最後のデータ バインディングをもう 1 つ実行してみましょう。

The finished app.

手順 13: GridView を画像のコレクションにバインドする (C# のみ)

重要

この最後の手順は、C# プロジェクトを作成した場合にのみ実行します。

ヒント

XAML マークアップでは実行できないこと (通常は動的に生成された UI に関連する) があることがわかります。 しかし、一般的にマークアップで何かを行うことが "できる" 場合は、それが望ましいです。 これによって、XAML マークアップが表す "ビュー" と、命令型コードが表す "モデル" (または "ビュー モデル") がより明確に区別されます。 また、ツールやチーム メンバー間のワークフローが改善される傾向があります。

現在、命令型コードを使用して GridViewItemsSource プロパティを MainWindowImages プロパティに関連付けています。 しかし、代わりにマークアップでこれを行うことができます。

  1. MainWindow クラスで、GetItemsAsync の最後の行を削除 (またはコメントアウト) します。この最後の行では、ImageGridViewItemsSourceImages プロパティの値に設定されています。

  2. 次に MainWindow.xaml で、ImageGridView という名前の GridView を探し、次のように ItemsSource 属性を追加します。 必要に応じて、IntelliSense を使用してこの変更を行うことができます。

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

Images プロパティの値は、この特定のアプリの実行時には変更されません。 ただし、Images は型 ObservableCollection<T> であるため、コレクションの "内容" は変更される可能性があります (つまり、要素が追加または削除される可能性があります)。また、バインディングによって自動的に変更が通知され、UI が更新されます。

まとめ

このチュートリアルでは、Visual Studio を使用して、写真を表示する単純な WinUI 3 アプリをビルドするプロセスについて説明しました。 このチュートリアルでは、コントロール、レイアウト パネル、データ バインディング、GridView UI の最適化を使用した WinUI 3 アプリでの作業を経験しました。

関連項目

チュートリアル: 複数のプラットフォームを対象とするシンプルなフォト ビューアーを構築する