Tutorial: Erstellen einer einfachen Fotoanzeige mit WinUI 3

Hinweis

Informationen zu den Vorteilen von WinUI 3 sowie anderen App-Typoptionen finden Sie unter Übersicht über die App-Entwicklungsoptionen.

In diesem Thema gehen wir durch den Prozess der Erstellung eines neuen WinUI 3-Projekts in Visual Studio und erstellen dann eine einfache App zum Anzeigen von Fotos. Es werden Steuerelemente, Layoutbereiche und Datenbindung verwendet. Außerdem wird sowohl XAML-Markup (deklarativ) als auch entweder C#-Code oder C++-Code (imperativ oder prozedural) geschrieben. Verwenden Sie die Sprachauswahl oberhalb des Thementitels, um C# oder C++/WinRT auszuwählen.

Tipp

Der Quellcode in diesem Thema wird sowohl in C# als auch in C++/WinRT bereitgestellt. C++-Entwickler*innen finden weitere Details und Konzepte zur Erläuterung der Funktionsweise des hier gezeigten Codes in der Dokumentation zu C++/WinRT. Zu den relevanten Themen gehören XAML-Steuerelemente: Binden an eine C++/WinRT-Eigenschaft, XAML-Elementsteuerelemente: Binden an eine C++/WinRT-Sammlung und C++/WinRT-Beispielanwendung eines Foto-Editors.

Schritt 1: Installieren von Tools für das Windows App SDK

Informationen zum Einrichten Ihres Entwicklungscomputers finden Sie unter Installieren von Tools für das Windows App SDK. Sie können dann optional mit Erstellen Ihres ersten WinUI 3-Projekts fortfahren.

Wichtig

Sie finden Themen mit Versionshinweisen zusammen mit dem Thema Windows App SDK – Veröffentlichungskanäle. Es gibt Versionshinweise für jeden Kanal. Überprüfen Sie unbedingt alle Einschränkungen und bekannten Probleme in den Versionshinweisen, da sich diese möglicherweise auf die Ausführung dieses Tutorials und der App, die wir erstellen, auswirken können.

Schritt 2: Erstellen eines neuen Projekts

Erstellen Sie in Visual Studio ein neues C#- oder C++-Projekt auf der Grundlage der Projektvorlage Leere App, Gepackt (WinUI 3 in Desktop). Benennen Sie das Projekt SimplePhotos, und deaktivieren Sie die Option Projektmappe und Projekt im selben Verzeichnis platzieren (sodass die Ordnerstruktur der in diesem Tutorial beschriebenen entspricht). Sie können auf das neueste Release (keine Vorschau) des Clientbetriebssystems abzielen.

Schritt 3: Kopieren von Objektdateien

Die App, die wir erstellen, beinhaltet Bilddateien in Form von Objektdateien, und dies sind die Fotos, die angezeigt werden. In diesem Abschnitt fügen Sie diese Objekte zu Ihrem Projekt hinzu. Zuerst müssen Sie jedoch eine Kopie der Dateien abrufen.

  1. Klonen Sie also das Windows App SDK-Beispielrepository (siehe WindowsAppSDK-Beispiele) (oder laden Sie es als .zip herunter). Anschließend finden Sie die Ressourcendateien, die wir verwenden werden, im Ordner \WindowsAppSDK-Samples\Samples\PhotoEditor\cs-winui\Assets\Samples. (Verwenden Sie diesen Ordner sowohl für ein C#-Projekt als auch für ein C++/WinRT-Projekt.) Wenn Sie diese Dateien im Repository online anzeigen möchten, navigieren Sie zu WindowsAppSDK-Samples/Samples/PhotoEditor/cs-winui/Assets/Samples/.

  2. Wählen Sie den Ordner Beispiele im Datei-Explorer aus, und kopieren Sie ihn in die Zwischenablage.

  1. Navigieren Sie zu Projektmappen-Explorer in Visual Studio. Klicken Sie mit der rechten Maustaste auf den Ordner Objekte (untergeordnetes Element des Projektknotens), und klicken Sie anschließend auf Ordner in Datei-Explorer öffnen. Dadurch wird der Ordner Objekte im Datei-Explorer geöffnet.

  2. Fügen Sie den soeben kopierten Ordner Beispiele in den Ordner Objekte ein.

Schritt 4: Hinzufügen eines GridView-Steuerelements

Unsere App muss Zeilen und Spalten von Fotos anzeigen. Mit anderen Worten, ein Raster von Bildern. Für eine solche Benutzeroberfläche sind die zu verwendenden Hauptsteuerelemente „Listenansicht“ und „Rasteransicht“.

  1. Öffnen Sie MainWindow.xaml. Derzeit gibt es ein Window-Element und innerhalb dieses Elements einen StackPanel-Layoutbereich. Innerhalb von StackPanel befindet sich ein Button-Steuerelement, das mit einer Ereignishandlermethode eingebunden ist.

    Das Hauptfenster jeder App ist die Ansicht, die beim Ausführen der App zuerst angezeigt wird. In der App, die wir erstellen, besteht die Aufgabe des Hauptfensters darin, die Fotos aus dem Ordner Beispiele zu laden und eine gekachelte Ansicht dieser Bilder zusammen mit verschiedenen Informationen darüber anzuzeigen.

  2. Ersetzen Sie das StackPanel- und Button-Markup durch den Grid-Layoutbereich und das GridView-Steuerelement aus der nachstehenden Auflistung.

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

    Tipp

    x:Name identifiziert ein XAML-Element, damit Sie an anderer Stelle im XAML-Code und im CodeBehind darauf verweisen können.

  3. C#. Öffnen Sie MainWindow.xaml.cs, und löschen Sie die myButton_Click-Methode.

  4. C++/WinRT. Öffnen Sie MainWindow.xaml.h und MainWindow.xaml.cpp, und löschen Sie die Methode myButton_Click.

Sie können jetzt die Erstellung und Ausführung starten, aber das Fenster ist in dieser Phase leer. Damit das GridView-Steuerelement Objekte anzeigt, müssen wir ihm eine Sammlung von Objekten hinzufügen, die angezeigt werden können. Damit fangen wir als nächstes an.

Hintergrundinformationen zu einigen der gerade erwähnten Typen finden Sie unter Layoutbereiche und Steuerelemente für Windows-Apps.

Schritt 5: Das ImageFileInfo-Modell

Ein Modell (im Sinne von Modellen, Ansichten und Ansichtsmodellen) ist eine Klasse, die bis zu einem gewissen Grad ein reales Objekt oder Konzept (z. B. ein Bankkonto) darstellt. Es ist eine Abstraktion dieser realen Sache. In diesem Abschnitt fügen wir zu unserem Projekt eine neue Klasse namens ImageFileInfo hinzu. ImageFileInfo ist ein Modell einer Bilddatei, z. B. ein Foto. Dieser Abschnitt bringt uns der Anzeige von Fotos in der Benutzeroberfläche (UI) der App einen Schritt näher.

Tipp

In Vorbereitung auf das folgende Codebeispiel führen wir den Begriff beobachtbar ein. Eine Eigenschaft, die dynamisch an ein XAML-Steuerelement gebunden werden kann (sodass die Benutzeroberfläche jedes Mal aktualisiert wird, wenn sich der Eigenschaftswert ändert), wird als beobachtbare Eigenschaft bezeichnet. Dieses Konzept basiert auf dem Softwareentwurfsmuster, das als Beobachter-Muster bekannt ist. In der App, die wir in diesem Tutorial erstellen, werden die Eigenschaften unseres ImageFileInfo-Modells nicht geändert. Trotzdem zeigen wir, wie Sie ImageFileInfo beobachtbar machen können, indem Sie die Schnittstelle INotifyPropertyChanged implementieren.

  1. Klicken Sie mit der rechten Maustaste auf den Projektknoten (SimplePhotos), und klicken Sie auf Hinzufügen>Neues Element.... Wählen Sie unter C#-Elemente>Codedie Option Klasse aus. Legen Sie den Namen auf ImageFileInfo.cs fest, und klicken Sie auf Hinzufügen.

  2. Ersetze den Inhalt von ImageFileInfo.cs durch das folgende Codelisting:

    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. Speichern und schließen Sie die ImageFileInfo.cs-Datei.

Schritt 6: Definieren und Auffüllen einer Eigenschaft für eine Sammlung von Bildern

In diesem Abschnitt fügen wir der MainWindow-Klasse eine neue Eigenschaft hinzu. Die Eigenschaft (namens Images) ist eine Sammlungsklasse, die die Bilder enthält, die angezeigt werden sollen.

  1. Definieren Sie die Eigenschaft wie folgt in MainWindow.xaml.cs:

    ...
    using System.Collections.ObjectModel;
    ...
    namespace SimplePhotos
    {
        public sealed partial class MainWindow : Window
        {
            public ObservableCollection<ImageFileInfo> Images { get; } = 
                new ObservableCollection<ImageFileInfo>();
            ...
        }
    }
    
  2. Der Code zum Auffüllen der neuen Sammlungseigenschaft mit Bildern wird in den Methoden GetItemsAsync und LoadImageInfoAsync weiter unten gezeigt. Fügen Sie die using-Direktiven und die beiden Methodenimplementierungen ebenfalls in MainWindow.xaml.csein. Diese Methoden sind Elemente der Klasse MainWindow. Fügen Sie sie also dort ein, so wie Sie es oben mit der Eigenschaft Bilder gemacht haben.

    ...
    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. Das letzte, was wir in diesem Abschnitt tun müssen, ist, den Konstruktor von MainWindow zu aktualisieren, um GetItemsAsync aufzurufen.

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

Sie können die Erstellung und Ausführung jetzt starten, wenn Sie möchten (um zu überprüfen, ob Sie die Schritte richtig befolgt haben), aber in dieser Phase gibt es im Fenster nicht viel zu sehen. Das liegt daran, dass wir das GridView-Objekt bisher nur angewiesen haben, dass es eine Sammlung von Objekten des Typs ImageFileInfo rendern soll, aber noch nicht, wie.

Denken Sie daran, dass die Images-Eigenschaft eine beobachtbare Sammlung von ImageFileInfo-Objekten ist. Und die letzte Zeile von GetItemsAsync teilt GridView (hat den Namen ImageGridView) mit, dass die Quelle der Elemente (ItemsSource) die Eigenschaft Images ist. Und der Auftrag von GridView besteht dann darin, diese Elemente anzuzeigen.

Aber wir haben GridView noch nichts über die ImageFileInfo-Klasse mitgeteilt. Bisher kann „GridView“ bestenfalls den ToString-Wert jedes ImageFileInfo-Objekts in der Sammlung anzeigen. Und standardmäßig ist dies nur der Name des Typs. Im nächsten Abschnitt erstellen wir eine Datenvorlage, um zu definieren, wie ein ImageFileInfo-Objekt angezeigt werden soll.

Tipp

Ich habe oben den Begriff beobachtbare Sammlung verwendet. In der App, die wir in diesem Tutorial erstellen, ändert sich die Anzahl der Bilder nicht (und wie gesagt, auch nicht die Werte der Eigenschaften der einzelnen Bilder). Es ist jedoch immer noch praktisch und eine bewährte Methode, die Datenbindung zu verwenden, um die Benutzeroberfläche zunächst mit den Daten zu verbinden. Das werden wir also tun.

Schritt 7: Hinzufügen einer Datenvorlage

Zunächst verwenden wir eine skizzenähnliche Vorlage mit Platzhalterdaten. Das wird reichen, bis wir einige Layoutoptionen untersucht haben. Danach können wir die Datenvorlage aktualisieren, um die tatsächlichen Fotos anzuzeigen.

Tipp

Das ist eigentlich eine sehr praktische Sache. Es hat sich gezeigt, dass Menschen eher bereit sind, schnelle Ideen vorzuschlagen und/oder auszuprobieren, die mitunter ganz erhebliche Änderungen mit sich bringen, wenn eine Benutzeroberfläche wie eine Skizze aussieht (mit anderen Worten „Low-Fidelity“ – geringere Genauigkeit). Das liegt daran, dass wir (richtigerweise) davon ausgehen, dass solche Änderungen billig zu haben sein werden.

Andererseits, je fertiger eine Benutzeroberfläche aussieht (je höher die Genauigkeit), desto mehr vermuten wir (wiederum zu Recht), dass viel Arbeit in das aktuelle Aussehen der Benutzeroberfläche geflossen ist. Und das macht uns weniger geneigt, neue Ideen vorzuschlagen oder auszuprobieren.

  1. Öffnen Sie MainWindow.xaml, und ändern Sie den Inhalt des Fensters so, dass es wie dieses Markup aussieht:

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

    Zum Layoutstamm haben wir eine einfache DataTemplate-Ressource hinzugefügt und dieser einen Schlüssel ImageGridView_ItemTemplate zugewiesen. Und wir haben diesen Schlüssel verwendet, um die ItemTemplate der GridView festzulegen. ItemsControl-Elemente wie GridView verfügen über eine ItemTemplate-Eigenschaft (genau wie die ItemsSource-Eigenschaft, die wir bereits gesehen haben). Eine Elementvorlage ist eine Datenvorlage, die verwendet wird, um jedes Element in der Sammlung anzuzeigen.

    Weitere Informationen finden Sie unter Elementcontainer und -vorlagen.

  2. Nun können wir die Datenvorlage in einigen Durchgängen bearbeiten und die darin enthaltenen Elemente ergänzen und bearbeiten, um sie interessanter und nützlicher zu gestalten. Wir geben dem Grid-Stammelement eine Höhe und eine Breite von 300 und einen Rand von 8. Dann fügen wir zwei Zeilendefinitionen hinzu und legen die Höhe der zweiten Zeilendefinition auf Auto fest.

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

    Weitere Informationen hierzu finden Sie unter Ausrichtung, Rand und Abstand.

  3. Die Datenvorlage soll das Bild, den Namen, den Dateityp, die Abmessungen und die Bewertung eines jeden Fotos anzeigen. Wir fügen also jeweils ein Image-Steuerelement, einige TextBlock-Steuerelemente und ein RatingControl-Steuerelement hinzu. Wir ordnenden Text in StackPanel-Layoutbereichen an. Das Bild zeigt zunächst das skizzenähnliche Microsoft Store-Logo des Projekts als Platzhalter an.

  4. Nach all diesen Bearbeitungsschritten sieht die Datenvorlage nun so aus:

    <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>
    

Erstellen Sie jetzt das Projekt, und führen Sie die App aus, um das GridView-Steuerelement mit der soeben erstellten Elementvorlage anzuzeigen. Als Nächstes sehen wir uns an, wie die Elemente angeordnet sind. Wir ändern einige Pinsel und fügen Platz zwischen den Elementen hinzu.

The placeholder item template.

Schritt 8: Bearbeiten des Stils des Elementcontainers

Ein anderes Konzept im Zusammenhang mit ItemsControl-Elementen wie GridView ist der Elementcontainer. Ein Elementcontainer ist ein Inhaltssteuerelement, das ein Element als Wert seiner Content-Eigenschaft anzeigt. Ein ItemsControl-Element erstellt so viele Elementcontainer, wie es benötigt, um die Elemente anzuzeigen, die zu jedem Zeitpunkt auf dem Bildschirm sichtbar sind.

Als Steuerelement verfügt ein Elementcontainer über eine Stil- und eine Steuerelementvorlage. Die Stil- und Steuerelementvorlagen bestimmen, wie der Elementcontainer in seinen verschiedenen Zuständen aussieht (z. B. wenn er ausgewählt ist, wenn der Zeiger darüber fährt und wenn er den Fokus hält). Und wie wir gesehen haben, bestimmt die Elementvorlage (die eine Datenvorlage ist), wie das Element selbst aussieht.

Für GridView ist der Typ seiner Elementcontainer GridViewItem.

In diesem Abschnitt konzentrieren wir uns also auf das Entwerfen des Stils des Elementcontainers. Und dafür erstellen wir eine Style-Ressource für GridViewItem, und legen sie dann als ItemContainerStyle von *GridView fest. Im Stil legen wir die Eigenschaften Background und Margin des Elementcontainers fest, um ihm einen grauen Hintergrund und einen kleinen Rand um die Außenseite zu geben.

  1. Fügen Sie in MainWindow.xaml demselben Grid.Resources-XML-Element, in das wir die Datenvorlage einfügen, eine neue Style-Ressource hinzu.

    <Grid>
        <Grid.Resources>
        ...
            <Style x:Key="ImageGridView_ItemContainerStyle"
                TargetType="GridViewItem">
                <Setter Property="Background" Value="Gray"/>
                <Setter Property="Margin" Value="8"/>
            </Style>
        </Grid.Resources>
    
  2. Als Nächstes verwenden wir den ImageGridView_ItemContainerStyle-Schlüssel, um den ItemContainerStyle von GridView festzulegen.

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

Erstellen Sie die App, und führen Sie sie aus, um herauszufinden, wie sie nun aussieht. Während Sie die Größe des Fensters ändern, sorgt das GridView-Steuerelement dafür, dass die Elemente neu an den Raum angepasst werden. Bei manchen Breiten ist auf der rechten Seite des App-Fensters sehr viel Platz. Es würde besser aussehen, wenn GridView und/oder der Inhalt zentriert würde. Damit werden wir uns somit als Nächstes befassen.

Tipp

Wenn Sie experimentieren möchten, versuchen Sie, für die Eigenschaften Background und Margin unterschiedliche Werten festzulegen, um zu sehen, welche Auswirkung dies hat.

Schritt 9: Experimentieren mit Layout

Möglicherweise fragen Sie sich, ob Sie besser GridView selbst oder den Inhalt zentrieren sollten. Versuchen wir zunächst, GridView zu zentrieren.

  1. Um genau zu sehen, wo sich GridView im Fenster befindet – und was geschieht, während wir mit dem Layout experimentieren – legen wir die Eigenschaft Background auf „red“ (rot) fest.

    <GridView x:Name="ImageGridView"
            ...
            Background="Red">
    </GridView>
    
  2. Dann legen wir die Eigenschaft HorizontalAlignment auf Center fest.

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

    Siehe auch Ausrichtung, Rand, Abstand.

Starten Sie jetzt die Erstellung und Ausführung, und experimentieren Sie mit der Anpassung der Breite des Fensters. Sie können sehen, dass auf beiden Seiten des roten Hintergrunds von GridView die gleiche leere Fläche vorhanden ist. Wir haben also das Ziel erreicht, die Bilder zu zentrieren. Aber wird es jetzt wirklich deutlicher als vorher, dass die Bildlaufleiste zu GridView gehört, und nicht zum Fenster? Daher müssen wir GridView wieder so festlegen, dass es das Fenster ausfüllt. Wir haben gezeigt, dass wir die Bilder in GridView zentrieren sollten (anstatt GridView im Fenster zu zentrieren).

  1. Löschen Sie nun das Attribut HorizontalAlignment, das Sie im vorherigen Schritt hinzugefügt haben.

Schritt 10: Bearbeiten der Elementbereichvorlage

ItemsControl-Elemente legen ihre Elementcontainer in einem so genannten Elementbereich fest. Sie können definieren, welcher Bereichstyp verwendet wird, und Eigenschaften auf diesem Bereich festlegen, indem Sie die Elementansichtsvorlage von GridView bearbeiten. Das werden wir nun in diesem Abschnitt tun.

  1. Fügen Sie in MainWindow.xaml eine ItemsPanelTemplate-Ressource zu unserem Ressourcenverzeichnis hinzu. Der Elementbereich ist vom Typ ItemsWrapGrid, und wir legen seine HorizontalAlignment-Eigenschaft auf Center fest.

    <Grid>
        <Grid.Resources>
        ...
            <ItemsPanelTemplate x:Key="ImageGridView_ItemsPanelTemplate">
                <ItemsWrapGrid Orientation="Horizontal"
                               HorizontalAlignment="Center"/>
            </ItemsPanelTemplate>
        </Grid.Resources>
    
  2. Als Nächstes verwenden wir den Schlüssel ImageGridView_ItemsPanelTemplate, um ItemsPanel von GridView festzulegen.

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

Wenn Sie dieses Mal die >Erstellung und Ausführung starten und mit der Einstellung der Breite des Fensters experimentieren, ist der rote Hintergrund von GridView auf beiden Seiten der Bilder gleich groß. Und da GridView das Fenster füllt, richtet sich die Bildlaufleiste ordentlich zum Rand des Fensters aus, wo Benutzer es erwarten.

  1. Nachdem wir nun mit dem Layout experimentiert haben, entfernen Sie Background="Red" aus GridView.

Schritt 11: Ersetzen des Platzhalterbilds durch ein Foto

Jetzt ist es Zeit, unsere Skizze auf eine höhere Genauigkeitsebene zu bringen. Das bedeutet, dass das Platzhalterbild durch reale Bilder und der Platzhaltertext „lorem ipsum“ durch echte Daten ersetzt wird. Kümmern wir uns zuerst um die Bilder.

Wichtig

Bei der Technik, die wir verwenden, um die Fotos im Ordner Assets\Samples anzuzeigen, werden die Elemente von GridView schrittweise aktualisiert. Das ist insbesondere der Code in den Methoden ImageGridView_ContainerContentChanging und ShowImage im folgenden Codebeispiel, einschließlich der Verwendung der Eigenschaften ContainerContentChangingEventArgs.InRecycleQueue und ContainerContentChangingEventArgs.Phase. Weitere Informationen finden Sie unter Optimieren der ListView- und GridView-Benutzeroberfläche. Aber um es kurz zu machen, GridView lässt uns wissen (durch ein Ereignis), wenn einer seiner Elementcontainer bereit ist, sein Element anzuzeigen. Und dann verfolgen wir, in welcher Phase des Updatelebenszyklus der Elementcontainer sich befindet, damit wir bestimmen können, wann er bereit ist, Fotodaten anzuzeigen.

  1. Fügen Sie in MainWindow.xaml.cs eine neue Methode namens ImageGridView_ContainerContentChanging zu MainWindow hinzu. Dies ist eine Ereignisbehandlungsmethode, und das Ereignis, das behandelt wird, ist ContainerContentChanging. Außerdem müssen wir die Implementierung der ShowImage-Methode bereitstellen, von der ImageGridView_ContainerContentChanging abhängig ist. Fügen Sie die using-Direktiven und die beiden Methodenimplementierungen in MainWindow.xaml.cs ein:

    ...
    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. Dann registrieren Sie in MainWindow.xaml den ImageGridView_ContainerContentChanging-Ereignishandler beim ContainerContentChanging-Ereignis von GridView.

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

Schritt 12: Ersetzen des Platzhaltertexts durch echte Daten

In diesem Abschnitt verwenden wir Einmaldatenbindungen. Eine Einmalbindung eignet sich hervorragend für Daten, die sich zur Laufzeit nicht ändern. Und das bedeutet, dass Einmalbindungen hohe Leistung ermöglichen und einfach zu erstellen sind.

  1. Suchen Sie in MainWindow.xaml die Datenvorlagenressource ImageGridView_ItemTemplate. Wir werden der Datenvorlage mitteilen, dass sie eine Vorlage für die ImageFileInfo-Klasse ist, die, wie Sie sich erinnern, der von unserem GridView-Objekt angezeigte Elementtyp ist.

  2. Fügen Sie dazu einen x:DataType-Wert wie folgt zur Vorlage hinzu:

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

    Wenn Sie nicht mit der oben gezeigten local:-Syntax (oder der xmlns:local-Syntax im öffnenden Tag Window) vertraut sind, schauen Sie sich XAML-Namespaces und Namespacezuordnung an.

    Nachdem wir nun einen x:DataType festgelegt haben, können wir x:Bind-Datenbindungsausdrücke in der Datenvorlage verwenden, um sie an die Eigenschaften des angegebenen Datentyps zu binden (in diesem Fall ImageFileInfo).

  3. Suchen Sie in der Datenvorlage das erste TextBlock-Element (das Element, für das Text derzeit auf ImageTitle festgelegt ist). Ersetzen Sie den Text-Wert wie unten dargestellt.

    Tipp

    Sie können entweder das unten stehende Markup kopieren und einfügen oder IntelliSense in Visual Studio verwenden. Wählen Sie dazu den aktuellen Wert aus, der sich innerhalb der Anführungszeichen befindet, und geben Sie { ein. IntelliSense fügt automatisch die schließende Klammer hinzu und zeigt eine Codevervollständigungsliste an. Sie könnten nach unten zu x:Bind scrollen, und darauf doppelklicken. Es kann jedoch effizienter sein, x: einzugeben (beachten Sie, wie x:Bind dann an den Anfang der Vervollständigungsliste gefiltert wird), und die TAB-TASTE zu drücken. Drücken Sie nun die LEERTASTE, geben Sie ImageT ein (so viel vom Eigenschaftennamen ImageTitle, wie erforderlich ist, um ihn am Anfang der Vervollständigungsliste anzuzeigen), und drücken Sie die TAB-TASTE.

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

    Ein x:Bind-Ausdruck verknüpft den Wert einer UI-Eigenschaft mit dem Wert einer data-object-Eigenschaft. Das hängt natürlich davon ab, dass x:DataType zunächst auf den Typ dieser data-object-Eigenschaft festgelegt wird, damit Tools und Laufzeitumgebung wissen, welche Eigenschaften für die Bindung verfügbar sind.

    Weitere Informationen finden Sie unter {x:Bind}-Markuperweiterung und Ausführliche Informationen zu Datenbindungen.

  4. Ersetzen Sie auf dieselbe Weise die Werte der anderen TextBlock-Objekte und des RatingControl-Objekts. Das Ergebnis lautet wie folgt:

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

Wenn Sie die App jetzt erstellen und ausführen, werden anstelle von Platzhaltern echte Fotos sowie realer Text (und andere Daten) angezeigt. Visuell und funktionell ist diese einfache kleine App jetzt fertig. Aber lassen Sie uns zum Abschluss noch eine letzte kleine Datenbindung vornehmen.

The finished app.

Schritt 13: Binden von „GridView“ an die Bildersammlung (nur C#)

Wichtig

Führen Sie diesen letzten Schritt nur aus, wenn Sie ein C#-Projekt erstellt haben.

Tipp

Sie werden feststellen, dass es einige Dinge gibt (in der Regel im Zusammenhang mit einer dynamisch generierten Benutzeroberfläche), die Sie nicht im XAML-Markup erledigen können. Aber im Allgemeinen gilt: Wenn man etwas im Markup tun kann, dann ist dies zu bevorzugen. Es gibt eine etwas einfachere Trennung zwischen der Ansicht, die das XAML-Markup darstellt, und dem Modell (oder Ansichtsmodell), das der imperative Code darstellt. Und das kann normalerweise die Arbeitsabläufe in Tools und zwischen Teammitgliedern verbessern.

Derzeit verwenden wir imperativen Code, um die ItemsSource-Eigenschaft des GridView-Objekts der Images-Eigenschaft von MainWindow zuzuordnen. Das können wir aber stattdessen im Markup tun.

  1. Löschen Sie in der Klasse MainWindow die letzte Zeile von GetItemsAsync (oder kommentieren Sie sie aus). Dadurch wird ItemsSource von ImageGridView auf den Wert der Eigenschaft Images festgelegt.

  2. Suchen Sie dann in MainWindow.xaml das GridView-Objekt mit dem Namen ImageGridView, und fügen Sie ein ItemsSource-Attribut wie dieses hinzu. Wenn Sie möchten, können Sie diese Änderung mit IntelliSense vornehmen.

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

Der Wert der Eigenschaft Images ändert sich zur Laufzeit für diese bestimmte App nicht. Da Images jedoch vom Typ ObservableCollection<T> ist, kann sich der Inhalt der Sammlung ändern (das heißt, Elemente können hinzugefügt oder gelöscht werden), und die Bindung erkennt automatisch die Änderungen und aktualisiert die Benutzeroberfläche.

Schlussbemerkung

In diesem Tutorial haben wir mit Visual Studio eine einfache WinUI 3-Anwendung erstellt, die Fotos anzeigt. Wir hoffen, dass Sie in diesem Tutorial Erfahrungen mit der Arbeit mit Steuerelementen, Layoutbereichen, Datenbindungen und GridView-Benutzeroberflächenoptimierung in einer WinUI 3-App sammeln konnten.

Weitere Informationen

Tutorial: Erstellen einer einfachen Fotoanzeige für mehrere Plattformen