Tutorial: Erstellen einer einfachen Fotoanzeige für mehrere Plattformen
Nachdem Sie zum Einstieg eine einfache WinUI 3-App erstellt haben, fragen Sie sich vielleicht, wie Sie mehr Benutzer*innen erreichen können, ohne Ihre App neu schreiben zu müssen. In diesem Tutorial wird Uno Platform verwendet, um die Reichweite Ihrer vorhandenen C#/WinUI 3-Anwendung zu erhöhen und die Wiederverwendung der Geschäftslogik und Benutzeroberflächenebene auf nativen Mobil-, Web- und Desktopplattformen zu ermöglichen. Mit nur minimalen Änderungen an der einfachen Fotoanzeige-App erhalten Sie eine pixelgenaue, zu diesen Plattformen portierte Kopie der App.
Voraussetzungen
Einrichten Ihres Entwicklungscomputers (lesen Sie Erste Schritte mit WinUI)
Workload „ASP.NET und Webentwicklung“ (für die WebAssembly-Entwicklung)
Installation von „.NET Multi-Platform App UI-Entwicklung“ (für die iOS-, Android- und Mac Catalyst-Entwicklung)
Installation von „.NET-Desktopentwicklung“ (für die Gtk-, Wpf- und Linux Framebuffer-Entwicklung)
Fertigstellen Ihrer Umgebung
Öffnen Sie eine Befehlszeile, eine Instanz von Windows-Terminal (sofern installiert) oder auch eine Eingabeaufforderung oder Windows PowerShell über das Startmenü.
Installieren oder aktualisieren Sie das Tool
uno-check
:Verwenden Sie den folgenden Befehl:
dotnet tool install -g uno.check
So aktualisieren Sie das Tool, wenn bei Ihnen bereits eine ältere Version installiert ist:
dotnet tool update -g uno.check
Führen Sie das Tool mithilfe des folgenden Befehls aus:
uno-check
Befolgen Sie die im Tool angegebenen Anweisungen. Da ihr System geändert werden muss, werden Sie möglicherweise aufgefordert, erhöhte Berechtigungen zu erteilen.
Installieren der Projektmappenvorlagen für Uno Platform
Starten Sie Visual Studio, und klicken Sie auf Continue without code
. Klicken Sie auf der Menüleiste auf Extensions
>Manage Extensions
.
Erweitern Sie im Erweiterungs-Manager den Knoten Online, suchen Sie nach Uno
, und installieren Sie die Erweiterung Uno Platform
, oder laden Sie sie aus Visual Studio Marketplace herunter, und installieren Sie sie. Starten Sie dann Visual Studio neu.
Erstellen einer Anwendung
Nun sind wir bereit für die Erstellung einer plattformübergreifenden Anwendung. Hierzu erstellen wir eine neue Uno Platform-Anwendung. Wir kopieren Code aus dem WinUI 3-Projekt SimplePhotos des vorherigen Tutorials in unser plattformübergreifendes Projekt. Das ist möglich, da Uno Platform die Wiederverwendung Ihrer bereits vorhandenen Codebasis ermöglicht. Features, die von Betriebssystem-APIs der jeweiligen Plattform abhängig sind, können problemlos nach und nach implementiert werden. Dieser Ansatz ist besonders nützlich, um bereits vorhandene Anwendung zu anderen Plattformen zu portieren.
Schon bald können Sie von den Vorteilen dieses Ansatzes profitieren, da Sie mehr Plattformen mit einer vertrauten XAML-Variante und der bereits vorhandenen Codebasis erreichen können.
Öffnen Sie Visual Studio, und wählen Sie File
>New
>Project
aus, um ein neues Projekt zu erstellen:
Suchen Sie nach „Uno“, und wählen Sie die Projektvorlage „Uno Platform-App“ aus:
Erstellen Sie über die Startseite von Visual Studio eine neue C#-Projektmappe mit dem Typ Uno Platform-App. Geben Sie dieser Projektmappe einen anderen Namen (UnoSimplePhotos), um Konflikte mit dem Code aus dem vorherigen Tutorial zu vermeiden. Geben Sie den Projektnamen, den Projektmappennamen und das Verzeichnis an. In diesem Beispiel gehört das plattformübergreifende Projekt UnoSimplePhotos
zu einer Projektmappe namens UnoSimplePhotos
, die sich unter „C:\Projects“ befindet:
Wählen Sie als Nächstes eine Basisvorlage aus, um Ihre einfache Fotogalerieanwendung zu einer plattformübergreifenden Anwendung zu machen.
Die Vorlage „Uno Platform-App“ enthält zwei voreingestellte Optionen (Leer und Standard) für den schnellen Einstieg – entweder mit einer leeren Projektmappe oder mit der Standardkonfiguration, die Verweise auf die Bibliotheken „Uno.Material“ und „Uno.Toolkit“ enthält. Die Standardkonfiguration beinhaltet auch „Uno.Extensions“ für die Abhängigkeitsinjektion, Konfiguration, Navigation und Protokollierung. Darüber hinaus wird MVUX anstelle von MVVM verwendet, was die Option zu einem guten Ausgangspunkt für die schnelle Erstellung praktischer Anwendungen macht.
Wählen Sie der Einfachheit halber die Voreinstellung Leer aus. Klicken Sie anschließend auf die Schaltfläche Erstellen. Warten Sie, bis die Projekte erstellt und ihre Abhängigkeiten wiederhergestellt wurden.
In einem Banner oben im Editor werden Sie u. U. zum Neuladen von Projekten aufgefordert. Klicken Sie auf Projekte neu laden:
Im Projektmappen-Explorer sollte die folgende Standarddateistruktur angezeigt werden:
Hinzufügen von Imageressourcen zum Projekt
Ihre App benötigt einige Bilder, die sie anzeigen kann. Sie können die gleichen Bilder wie im vorherigen Tutorial verwenden.
Erstellen Sie im Projekt UnoSimplePhotos
einen neuen Ordner mit dem Namen Assets
, und kopieren Sie die JPG-Bilddateien in einen Unterordner namens Samples
. Die Struktur des Ordners Assets
sollte nun wie folgt aussehen:
Weitere Informationen zum Erstellen des Ordners Assets
und zum Hinzufügen von Bildern finden Sie in der Uno Platform-Dokumentation unter Assets and image display (Ressourcen und Bildanzeige).
Vorbereiten Ihrer App
Nachdem Sie nun über den funktionalen Ausgangspunkt Ihrer plattformübergreifenden WinUI-Anwendung verfügen, können Sie Code aus dem Desktopprojekt hineinkopieren.
Kopieren der Ansicht
Da Uno Platform die Verwendung der XAML-Variante ermöglicht, mit der Sie bereits vertraut sind, können Sie den im vorherigen Tutorial erstellten Code kopieren und hier einfügen.
Kehren Sie zum Projekt SimplePhotos aus dem vorherigen Tutorial zurück. Suchen Sie im Projektmappen-Explorer nach der Datei MainWindow.xaml
, und öffnen Sie sie. Beachten Sie, dass der Inhalt der Ansicht nicht in einem Page
-Element, sondern in einem Window
-Element definiert ist. Das liegt daran, dass es sich bei dem Desktopprojekt um eine WinUI 3-Anwendung handelt, die Window
-Elemente verwenden kann, um den Inhalt der Ansicht zu definieren:
<Window x:Class="SimplePhotos.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:SimplePhotos"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<Grid.Resources>
<DataTemplate x:Key="ImageGridView_ItemTemplate"
x:DataType="local:ImageFileInfo">
<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="{x:Bind ImageTitle}"
HorizontalAlignment="Center"
Style="{StaticResource SubtitleTextBlockStyle}" />
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center">
<TextBlock Text="{x:Bind ImageFileType}"
HorizontalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}" />
<TextBlock Text="{x:Bind ImageDimensions}"
HorizontalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
Margin="8,0,0,0" />
</StackPanel>
<RatingControl Value="{x:Bind ImageRating}"
IsReadOnly="True"/>
</StackPanel>
</Grid>
</DataTemplate>
<Style x:Key="ImageGridView_ItemContainerStyle"
TargetType="GridViewItem">
<Setter Property="Background"
Value="Gray"/>
<Setter Property="Margin"
Value="8"/>
</Style>
<ItemsPanelTemplate x:Key="ImageGridView_ItemsPanelTemplate">
<ItemsWrapGrid Orientation="Horizontal"
HorizontalAlignment="Center"/>
</ItemsPanelTemplate>
</Grid.Resources>
<GridView x:Name="ImageGridView"
ItemsSource="{x:Bind Images}"
ItemTemplate="{StaticResource ImageGridView_ItemTemplate}"
ItemContainerStyle="{StaticResource ImageGridView_ItemContainerStyle}"
ItemsPanel="{StaticResource ImageGridView_ItemsPanelTemplate}"
ContainerContentChanging="ImageGridView_ContainerContentChanging" />
</Grid>
</Window>
Dank der von Uno Platform bereitgestellten plattformübergreifende Implementierung der im Window
-Element enthaltenen Steuerelemente wie GridView
, Image
und RatingControl
funktioniert die Ansicht mit minimalem Aufwand auf allen unterstützten Plattformen. Kopieren Sie den Inhalt dieses Window
-Elements, und fügen Sie ihn im Uno Platform-Projekt UnoSimplePhotos in das Page
-Element der Datei MainPage.xaml
ein. Der XAML-Code der MainPage
-Ansicht sollte wie folgt aussehen:
<Page x:Class="UnoSimplePhotos.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UnoSimplePhotos"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<Grid.Resources>
<DataTemplate x:Key="ImageGridView_ItemTemplate"
x:DataType="local:ImageFileInfo">
<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="{x:Bind ImageTitle}"
HorizontalAlignment="Center"
Style="{StaticResource SubtitleTextBlockStyle}" />
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center">
<TextBlock Text="{x:Bind ImageFileType}"
HorizontalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}" />
<TextBlock Text="{x:Bind ImageDimensions}"
HorizontalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
Margin="8,0,0,0" />
</StackPanel>
<RatingControl Value="{x:Bind ImageRating}"
IsReadOnly="True"/>
</StackPanel>
</Grid>
</DataTemplate>
<Style x:Key="ImageGridView_ItemContainerStyle"
TargetType="GridViewItem">
<Setter Property="Background"
Value="Gray"/>
<Setter Property="Margin"
Value="8"/>
</Style>
<ItemsPanelTemplate x:Key="ImageGridView_ItemsPanelTemplate">
<ItemsWrapGrid Orientation="Horizontal"
HorizontalAlignment="Center"/>
</ItemsPanelTemplate>
</Grid.Resources>
<GridView x:Name="ImageGridView"
ItemsSource="{x:Bind Images}"
ItemTemplate="{StaticResource ImageGridView_ItemTemplate}"
ItemContainerStyle="{StaticResource ImageGridView_ItemContainerStyle}"
ItemsPanel="{StaticResource ImageGridView_ItemsPanelTemplate}"
ContainerContentChanging="ImageGridView_ContainerContentChanging">
</GridView>
</Grid>
</Page>
Wie Sie wissen, gab es bei der Desktoplösung auch eine Datei namens MainWindow.xaml.cs
mit CodeBehind für die Ansicht. Im Uno Platform-Projekt ist das CodeBehind für die hineinkopierte Ansicht MainPage
in der Datei MainPage.xaml.cs
enthalten.
Um dieses CodeBehind plattformübergreifend zu machen, muss zunächst Folgendes in der Datei MainPage.xaml.cs
platziert werden:
Eigenschaft
Images
: Stellt eine beobachtbare Sammlung von Bilddateien fürGridView
bereit.Inhalt des Konstruktors: Ruft
GetItemsAsync()
auf, um die SammlungImages
mit Elementen aufzufüllen, die Bilddateien darstellen.Entfernen Sie die manuelle Änderung der Eigenschaft
ItemsSource
des SteuerelementsImageGridView
.Methode
ImageGridView_ContainerContentChanging
: Wird als Teil einer Strategie zum progressiven Laden vonGridView
-Elementen verwendet, die in den sichtbaren Bereich gescrollt werden.Methode
ShowImage
: Lädt die Bilddateien inGridView
.Methode
GetItemsAsync
: Ruft die Bildressourcendateien aus dem OrdnerSamples
ab.Methode
LoadImageInfoAsync
: Erstellt einImageFileInfo
-Objekt auf der Grundlage einer erstellten Speicherdatei (StorageFile
).
Nachdem Sie alles eingefügt haben, sollte MainPage.xaml.cs
wie folgt aussehen:
using Microsoft.UI.Xaml.Controls;
using System.Collections.ObjectModel;
using Windows.Storage;
using Windows.Storage.Search;
namespace UnoSimplePhotos;
public sealed partial class MainPage : Page
{
public ObservableCollection<ImageFileInfo> Images { get; }
= new ObservableCollection<ImageFileInfo>();
public MainPage()
{
this.InitializeComponent();
GetItemsAsync();
}
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();
}
}
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));
}
}
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;
}
}
Hinweis
Die Dateien in Ihrem Uno-App-Projekt müssen UnoSimplePhotos
als Namespace verwenden.
Aktuell enthalten die Dateien für die verwendete Hauptansicht alle Funktionen der Desktoplösung. Nach dem Kopieren der Modelldatei ImageFileInfo.cs
erfahren Sie, wie Sie die desktoporientierten Codeblöcke für die Kompatibilität mit mehreren Plattformen ändern.
Kopieren Sie ImageFileInfo
aus dem Desktopprojekt, und fügen Sie das Element in die Datei ImageFileInfo.cs
ein. Nehmen Sie die folgenden Änderungen vor:
Benennen Sie den Namespace von
SimplePhotos
inUnoSimplePhotos
um:// Found towards the top of the file namespace UnoSimplePhotos;
Ändern Sie den Parametertyp der Methode
OnPropertyChanged
so, dass NULL-Werte zulässig sind:// string -> string? protected void OnPropertyChanged([CallerMemberName] string? propertyName = null) ...
Konfigurieren Sie
PropertyChangedEventHandler
so, dass NULL-Werte zulässig sind:// PropertyChangedEventHandler -> PropertyChangedEventHandler? public event PropertyChangedEventHandler? PropertyChanged;
Die Datei sollte nun wie folgt aussehen:
using Microsoft.UI.Xaml.Media.Imaging;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Windows.Storage;
using Windows.Storage.FileProperties;
using Windows.Storage.Streams;
using ThumbnailMode = Windows.Storage.FileProperties.ThumbnailMode;
namespace UnoSimplePhotos;
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));
}
Diese Klasse dient als Modell, um die Bilddateien in GridView
darzustellen. Es sollte nun zwar möglich sein, die App auszuführen, die Bilder werden aber möglicherweise nicht ordnungsgemäß gerendert, oder ihre Eigenschaften werden nicht angezeigt. In den nächsten Abschnitten werden einige Änderungen an den kopierten Dateien vorgenommen, um sie in einem Kontext mit mehreren Plattformen kompatibel zu machen.
Verwenden von Präprozessordirektiven
Im Desktopprojekt aus dem vorherigen Tutorial enthält die Datei MainPage.xaml.cs
eine Methode vom Typ GetItemsAsync
, die Elemente aus einem Speicherordner (StorageFolder
) aufzählt, der den Speicherort des installierten Pakets darstellt. Da dieser Speicherort auf bestimmten Plattformen wie WebAssembly nicht verfügbar ist, muss diese Methode geändert werden, um sie mit allen Plattformen kompatibel zu machen. Wir ändern auch die Klasse ImageFileInfo
entsprechend, um die Kompatibilität zu gewährleisten.
Nehmen Sie zunächst die erforderlichen Änderungen an der Methode GetItemsAsync
vor. Ersetzen Sie die Methode GetItemsAsync
in der Datei MainPage.xaml.cs
durch den folgenden Code:
private async Task GetItemsAsync()
{
#if WINDOWS
StorageFolder appInstalledFolder = Package.Current.InstalledLocation;
StorageFolder picturesFolder = await appInstalledFolder.GetFolderAsync("UnoSimplePhotos\\Assets\\Samples");
var result = picturesFolder.CreateFileQueryWithOptions(new QueryOptions());
IReadOnlyList<StorageFile> imageFiles = await result.GetFilesAsync();
#else
var imageFileNames = Enumerable.Range(1, 20).Select(i => new Uri($"ms-appx:///UnoSimplePhotos/Assets/Samples/{i}.jpg"));
var imageFiles = new List<StorageFile>();
foreach (var file in imageFileNames)
{
imageFiles.Add(await StorageFile.GetFileFromApplicationUriAsync(file));
}
#endif
foreach (StorageFile file in imageFiles)
{
Images.Add(await LoadImageInfoAsync(file));
}
}
Diese Methode verwendet nun eine Präprozessordirektive, um basierend auf der Plattform zu bestimmen, welcher Code ausgeführt werden soll. Unter Windows ruft die Methode den Speicherordner (StorageFolder
) ab, der den Speicherort des installierten Pakets darstellt, und gibt daraus den Ordner Samples
zurück. Auf anderen Plattformen zählt die Methode bis 20 und ruft die Bilddateien aus dem Ordner Samples
ab. Dabei wird ein URI (Uri
) verwendet, um die Bilddatei darzustellen.
Passen Sie als Nächstes die Methode LoadImageInfoAsync
an die Änderungen an, die Sie an der Methode GetItemsAsync
vorgenommen haben. Ersetzen Sie die Methode LoadImageInfoAsync
in der Datei MainPage.xaml.cs
durch den folgenden Code:
public async static Task<ImageFileInfo> LoadImageInfoAsync(StorageFile file)
{
#if WINDOWS
var properties = await file.Properties.GetImagePropertiesAsync();
ImageFileInfo info = new(properties,
file, file.DisplayName, $"{file.FileType} file");
#else
ImageFileInfo info = new(file, file.DisplayName, $"{file.FileType} file");
#endif
return info;
}
Diese Methode verwendet nun ebenfalls eine Präprozessordirektive (ähnlich wie die Methode GetItemsAsync
), um basierend auf der Plattform zu bestimmen, welcher Code ausgeführt werden soll. Unter Windows ruft die Methode die Bildeigenschaften (ImageProperties
) aus der Speicherdatei (StorageFile
) ab und verwendet sie, um ein ImageFileInfo
-Objekt zu erstellen. Auf anderen Plattformen erstellt die Methode ein ImageFileInfo
-Objekt ohne den Parameter ImageProperties
. Später werden Änderungen an der Klasse ImageFileInfo
vorgenommen, um diese Änderung zu berücksichtigen.
Steuerelemente wie GridView
ermöglichen progressives Laden aktualisierter Elementcontainerinhalte, während sie in den sichtbaren Bereich gescrollt werden. Hierzu wird das Ereignis ContainerContentChanging
verwendet. Im Desktopprojekt aus dem vorherigen Tutorial verwendet die Methode ImageGridView_ContainerContentChanging
dieses Ereignis, um die Bilddateien in GridView
zu laden. Da bestimmte Aspekte dieses Ereignisses nicht auf allen Plattformen unterstützt werden, müssen Änderungen an dieser Methode vorgenommen werden, um sie mit ihnen kompatibel zu machen.
Beispielsweise wird die Eigenschaft ContainerContentChangingEventArgs.Phase
derzeit nur unter Windows unterstützt. Die Methode ImageGridView_ContainerContentChanging
muss geändert werden, um diese Änderung zu berücksichtigen. Ersetzen Sie die Methode ImageGridView_ContainerContentChanging
in der Datei MainPage.xaml.cs
durch den folgenden Code:
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;
if (image is not null)
{
image.Source = null;
}
}
#if WINDOWS
if (args.Phase == 0)
{
args.RegisterUpdateCallback(ShowImage);
args.Handled = true;
}
#else
ShowImage(sender, args);
#endif
}
Daraufhin wird der spezialisierte Rückruf nur mit ContainerContentChangingEventArgs.RegisterUpdateCallback()
registriert, wenn es sich bei der Plattform um Windows handelt. Andernfalls wird die Methode ShowImage
direkt aufgerufen. Es sind auch Änderungen an der Methode ShowImage
erforderlich, damit sie mit den Änderungen funktioniert, die an der Methode ImageGridView_ContainerContentChanging
vorgenommen wurden. Ersetzen Sie die Methode ShowImage
in der Datei MainPage.xaml.cs
durch den folgenden Code:
private async void ShowImage(ListViewBase sender, ContainerContentChangingEventArgs args)
{
if (
#if WINDOWS
args.Phase == 1
#else
true
#endif
)
{
// 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;
#if WINDOWS
if (image is not null && item is not null)
{
image.Source = await item.GetImageThumbnailAsync();
}
#else
if (item is not null)
{
await item.GetImageSourceAsync();
}
#endif
}
}
Auch hier stellen wieder Präprozessordirektiven sicher, dass die Eigenschaft ContainerContentChangingEventArgs.Phase
nur auf Plattformen verwendet wird, auf denen sie unterstützt wird. Wir verwenden die zuvor nicht verwendete Methode GetImageSourceAsync()
, um die Bilddateien auf Windows-fremden Plattformen in GridView
zu laden. Als Nächstes wird die Klasse ImageFileInfo
bearbeitet, um die oben vorgenommenen Änderungen zu berücksichtigen.
Erstellen eines separaten Codepfads für andere Plattformen
Aktualisieren Sie ImageFileInfo.cs
, um eine neue Eigenschaft namens ImageSource
einzuschließen, die zum Laden der Bilddatei verwendet wird.
public BitmapImage? ImageSource { get; private set; }
Da Plattformen wie das Web keine erweiterten Bilddateieigenschaften unterstützen (die unter Windows problemlos verfügbar sind), wird eine Konstruktorüberladung hinzugefügt, die keinen Parameter vom Typ ImageProperties
erfordert. Fügen Sie die neue Überladung mithilfe des folgenden Codes nach der bereits vorhandenen Überladung hinzu:
public ImageFileInfo(StorageFile imageFile,
string name,
string type)
{
ImageName = name;
ImageFileType = type;
ImageFile = imageFile;
}
Diese Konstruktorüberladung wird verwendet, um auf Windows-fremden Plattformen ein ImageFileInfo
-Objekt zu erstellen. Infolgedessen ist es sinnvoll, die Eigenschaft ImageProperties
so zu konfigurieren, dass sie NULL-Werte zulässt. Aktualisieren Sie die Eigenschaft ImageProperties
mit dem folgenden Code, damit NULL-Werte für sie zulässig sind:
public ImageProperties? ImageProperties { get; }
Aktualisieren Sie die Methode GetImageSourceAsync
so, dass sie die Eigenschaft ImageSource
verwendet, anstatt nur ein BitmapImage
-Objekt zurückzugeben. Ersetzen Sie die Methode GetImageSourceAsync
in der Datei ImageFileInfo.cs
durch den folgenden Code:
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);
ImageSource = bitmapImage;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ImageSource)));
return bitmapImage;
}
Nehmen Sie die folgenden Änderungen vor, um zu verhindern, dass Sie den Wert von ImageProperties
erhalten, wenn er NULL ist:
Ändern Sie die Eigenschaft
ImageDimensions
so, dass der bedingte NULL-Operator verwendet wird:public string ImageDimensions => $"{ImageProperties?.Width} x {ImageProperties?.Height}";
Ändern Sie die Eigenschaft
ImageTitle
so, dass der bedingte NULL-Operator verwendet wird:public string ImageTitle { get => string.IsNullOrEmpty(ImageProperties?.Title) ? ImageName : ImageProperties?.Title; set { if (ImageProperties is not null) { if (ImageProperties.Title != value) { ImageProperties.Title = value; _ = ImageProperties.SavePropertiesAsync(); OnPropertyChanged(); } } } }
Ändern Sie
ImageRating
so, dass nichtImageProperties
herangezogen wird, indem Sie zu Demonstrationszwecken Bewertungssterne nach dem Zufallsprinzip generieren:public int ImageRating { get => (int)((ImageProperties?.Rating == null || ImageProperties.Rating == 0) ? (uint)Random.Shared.Next(1, 5) : ImageProperties.Rating); set { if (ImageProperties is not null) { if (ImageProperties.Rating != value) { ImageProperties.Rating = (uint)value; _ = ImageProperties.SavePropertiesAsync(); OnPropertyChanged(); } } } }
Aktualisieren Sie den Konstruktor, der eine zufällige ganze Zahl generiert, damit dies nicht mehr geschieht:
public ImageFileInfo(ImageProperties properties, StorageFile imageFile, string name, string type) { ImageProperties = properties; ImageName = name; ImageFileType = type; ImageFile = imageFile; }
Nach diesen Bearbeitungen sollte die Klasse ImageFileInfo
den folgenden Code enthalten. Sie verfügt jetzt über einen neuen getrennten Codepfad für Windows-fremde Plattformen:
using Microsoft.UI.Xaml.Media.Imaging;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Windows.Storage;
using Windows.Storage.FileProperties;
using Windows.Storage.Streams;
using ThumbnailMode = Windows.Storage.FileProperties.ThumbnailMode;
namespace UnoSimplePhotos;
public class ImageFileInfo : INotifyPropertyChanged
{
public BitmapImage? ImageSource { get; private set; }
public ImageFileInfo(ImageProperties properties,
StorageFile imageFile,
string name,
string type)
{
ImageProperties = properties;
ImageName = name;
ImageFileType = type;
ImageFile = imageFile;
}
public ImageFileInfo(StorageFile imageFile,
string name,
string type)
{
ImageName = name;
ImageFileType = type;
ImageFile = imageFile;
}
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);
ImageSource = bitmapImage;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ImageSource)));
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 is not null)
{
if (ImageProperties.Title != value)
{
ImageProperties.Title = value;
_ = ImageProperties.SavePropertiesAsync();
OnPropertyChanged();
}
}
}
}
public int ImageRating
{
get => (int)((ImageProperties?.Rating == null || ImageProperties.Rating == 0) ? (uint)Random.Shared.Next(1, 5) : ImageProperties.Rating);
set
{
if (ImageProperties is not null)
{
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));
}
Diese Klasse vom Typ ImageFileInfo
wird verwendet, um die Bilddateien in GridView
darzustellen. Abschließend ändern wir die Datei MainPage.xaml
, um die Änderungen am Modell zu berücksichtigen.
Verwenden von plattformspezifischem XAML-Markup
Es gibt einige Elemente im Ansichtsmarkup, die nur unter Windows ausgewertet werden dürfen. Fügen Sie dem Page
-Element der Datei MainPage.xaml
einen neuen Namespace hinzu:
...
xmlns:win="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Ersetzen Sie nun in MainPage.xaml
den Eigenschaftensetter ItemsPanel
für das GridView
-Element durch den folgenden Code:
win:ItemsPanel="{StaticResource ImageGridView_ItemsPanelTemplate}"
Wenn Sie dem Eigenschaftsnamen win:
voranstellen, wird die Eigenschaft nur unter Windows festgelegt. Wiederholen Sie dies innerhalb der Ressource ImageGridView_ItemTemplate
. Es sollen nur Elemente geladen werden, die die Eigenschaft ImageDimensions
unter Windows verwenden. Ersetzen Sie das TextBlock
-Element, das die Eigenschaft ImageDimensions
verwendet, durch den folgenden Code:
<win:TextBlock Text="{x:Bind ImageDimensions}"
HorizontalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
Margin="8,0,0,0" />
Die Datei MainPage.xaml
sollte jetzt wie folgt aussehen:
<Page x:Class="UnoSimplePhotos.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UnoSimplePhotos"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:win="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<Grid.Resources>
<DataTemplate x:Key="ImageGridView_ItemTemplate"
x:DataType="local:ImageFileInfo">
<Grid Height="300"
Width="300"
Margin="8">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image x:Name="ItemImage"
Source="{x:Bind ImageSource}"
Stretch="Uniform" />
<StackPanel Orientation="Vertical"
Grid.Row="1">
<TextBlock Text="{x:Bind ImageTitle}"
HorizontalAlignment="Center"
Style="{StaticResource SubtitleTextBlockStyle}" />
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center">
<TextBlock Text="{x:Bind ImageFileType}"
HorizontalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}" />
<win:TextBlock Text="{x:Bind ImageDimensions}"
HorizontalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
Margin="8,0,0,0" />
</StackPanel>
<RatingControl Value="{x:Bind ImageRating}"
IsReadOnly="True" />
</StackPanel>
</Grid>
</DataTemplate>
<Style x:Key="ImageGridView_ItemContainerStyle"
TargetType="GridViewItem">
<Setter Property="Background"
Value="Gray" />
<Setter Property="Margin"
Value="8"/>
</Style>
<ItemsPanelTemplate x:Key="ImageGridView_ItemsPanelTemplate">
<ItemsWrapGrid Orientation="Horizontal"
HorizontalAlignment="Center"/>
</ItemsPanelTemplate>
</Grid.Resources>
<GridView x:Name="ImageGridView"
ItemsSource="{x:Bind Images, Mode=OneWay}"
win:ItemsPanel="{StaticResource ImageGridView_ItemsPanelTemplate}"
ContainerContentChanging="ImageGridView_ContainerContentChanging"
ItemContainerStyle="{StaticResource ImageGridView_ItemContainerStyle}"
ItemTemplate="{StaticResource ImageGridView_ItemTemplate}" />
</Grid>
</Page>
Ausführen der App
Starten Sie das Ziel UnoSimplePhotos.Windows
. Wie Sie sehen, ist diese WinUI-App dem vorherigen Tutorial sehr ähnlich.
Sie können Ihre App jetzt auf jeder der unterstützten Plattformen erstellen und ausführen. Dazu können Sie über die Dropdownliste auf der Debugsymbolleiste eine Zielplattform für die Bereitstellung auswählen:
So führen Sie die WASM-Hauptkomponente (WebAssembly) aus
- Klicken Sie mit der rechten Maustaste auf das Projekt
UnoSimplePhotos.Wasm
, und wählen Sie Als Startprojekt festlegen aus. - Wählen Sie die Schaltfläche
UnoSimplePhotos.Wasm
aus, um die App bereitzustellen. - Auf Wunsch können Sie das Projekt
UnoSimplePhotos.Server
als Alternative hinzufügen und verwenden.
- Klicken Sie mit der rechten Maustaste auf das Projekt
So debuggen Sie für iOS
Klicken Sie mit der rechten Maustaste auf das Projekt
UnoSimplePhotos.Mobile
, und wählen Sie Als Startprojekt festlegen aus.Wählen Sie über die Dropdownliste auf der Debugsymbolleiste ein aktives iOS-Gerät oder den Simulator aus. Hierzu muss eine Koppelung mit einem Mac bestehen.
So debuggen Sie für Mac Catalyst
- Klicken Sie mit der rechten Maustaste auf das Projekt
UnoSimplePhotos.Mobile
, und wählen Sie Als Startprojekt festlegen aus. - Wählen Sie über die Dropdownliste auf der Debugsymbolleiste ein macOS-Remotegerät aus. Hierzu muss eine Koppelung mit einem entsprechenden Gerät bestehen.
- Klicken Sie mit der rechten Maustaste auf das Projekt
So debuggen Sie die Android-Plattform
- Klicken Sie mit der rechten Maustaste auf das Projekt
UnoSimplePhotos.Mobile
, und wählen Sie Als Startprojekt festlegen aus. - Wählen Sie über die Dropdownliste auf der Debugsymbolleiste entweder ein aktives Android-Gerät oder den Emulator aus.
- Wählen Sie im Untermenü „Gerät“ ein aktives Gerät aus.
- Klicken Sie mit der rechten Maustaste auf das Projekt
So debuggen Sie unter Linux mit Skia GTK:
- Klicken Sie mit der rechten Maustaste auf das Projekt
UnoSimplePhotos.Skia.Gtk
, und wählen Sie Als Startprojekt festlegen aus. - Wählen Sie die Schaltfläche
UnoSimplePhotos.Skia.Gtk
aus, um die App bereitzustellen.
- Klicken Sie mit der rechten Maustaste auf das Projekt
Siehe auch
Windows developer