Руководство. Создание простого средства просмотра фотографий, предназначенных для нескольких платформ
После создания начального простого приложения средства просмотра фотографий WinUI 3 вы можете задуматься о том, как получить доступ к большему числу пользователей, не перезаписав приложение. В этом руководстве используется uno Platform для расширения доступа к существующему приложению WinUI C# 3, позволяющему повторно использовать бизнес-логику и уровень пользовательского интерфейса в собственном мобильном, веб-приложении и настольном компьютере. С минимальными изменениями в простом приложении просмотра фотографий мы можем запустить идеальную копию приложения, перенесенного на эти платформы.
Необходимые компоненты
Настройка компьютера разработки (см. статью "Начало работы с WinUI")
ASP.NET и рабочую нагрузку веб-разработки (для разработки WebAssembly)
Разработка многоплатформенного пользовательского интерфейса приложений .NET (для разработки iOS, Android, Mac Catalyst)
Установленная разработка классических приложений .NET (для разработки Gtk, Wpf и Linux Framebuffer)
Завершение работы среды
Откройте командную строку, Терминал Windows, если она установлена, или в командной строке или Windows PowerShell из меню "Пуск".
Установите или обновите
uno-check
средство:Используйте следующую команду:
dotnet tool install -g uno.check
Чтобы обновить средство, если вы уже установили старую версию, выполните следующие действия.
dotnet tool update -g uno.check
Запустите средство с помощью следующей команды:
uno-check
Следуйте инструкциям, указанным средством. Так как она должна изменить систему, вам может потребоваться разрешение с повышенными привилегиями.
Установка шаблонов решений Uno Platform
Запустите Visual Studio, а затем щелкните Continue without code
. Щелкните Extensions
в>Manage Extensions
строке меню.
В диспетчере расширений разверните узел Online и найдите Uno
его, установите Uno Platform
расширение или скачайте и установите его из Visual Studio Marketplace, а затем перезапустите Visual Studio.
Создание приложения
Теперь, когда мы готовы создать мультиплатформенное приложение, мы рассмотрим, как создать новое приложение Uno Platform. Мы скопируйм код из проекта SimplePhotos WinUI 3 предыдущего руководства в проект с несколькими платформами. Это возможно, так как Uno Platform позволяет повторно использовать существующую базу кода. Для функций, зависящих от API ОС, предоставляемых каждой платформой, их можно легко работать с течением времени. Этот подход особенно полезен, если у вас есть существующее приложение, которое вы хотите перенести на другие платформы.
В ближайшее время вы сможете получить преимущества этого подхода, так как вы можете нацелиться на более платформы с знакомым вкусом XAML и базой кода, у вас уже есть.
Откройте Visual Studio и создайте проект с помощьюFile
>>New
Project
:
Найдите uno и выберите шаблон проекта приложения Uno Platform App:
Создайте новое решение C# с помощью типа приложения Uno Platform App на начальной странице Visual Studio. Чтобы избежать конфликтов с кодом из предыдущего руководства, мы предоставим этому решению другое имя UnoSimplePhotos. Укажите имя проекта, имя решения и каталог. В этом примере проект UnoSimplePhotos
с несколькими платформами принадлежит UnoSimplePhotos
решению, которое будет жить в C:\Projects:
Теперь вы выберете базовый шаблон, чтобы сделать приложение простой коллекции фотографий мультиплатформенным.
Шаблон приложения Uno Platform поставляется с двумя предварительно настроенными параметрами, которые позволяют быстро приступить к работе с пустым решением или конфигурацией по умолчанию , которая включает ссылки на библиотеки Uno.Material и Uno.Toolkit. Конфигурация по умолчанию также включает Uno.Extensions, которая используется для внедрения зависимостей, настройки, навигации и ведения журнала. Кроме того, он использует MVUX вместо MVVM, что делает его отличной отправной точкой для быстрого создания реальных приложений.
Чтобы упростить работу, выберите пустой предустановку. Затем нажмите кнопку "Создать ". Дождитесь создания проектов и их зависимостей.
Баннер в верхней части редактора может попросить перезагрузить проекты, щелкните " Перезагрузить проекты":
В Обозреватель решений должна появиться следующая структура файлов по умолчанию:
Добавление ресурсов изображений в проект
Вашему приложению потребуется отобразить некоторые изображения. Вы можете использовать те же изображения из предыдущего руководства.
UnoSimplePhotos
В проекте создайте новую папку с именем Assets
и скопируйте файлы изображений JPG в вложенную папкуSamples
. Теперь Assets
структура папок должна выглядеть следующим образом:
Дополнительные сведения о создании Assets
папки и добавлении в нее изображений см. в документации по uno Platform о ресурсах и изображении.
Подготовка приложения
Теперь, когда вы создали функциональную начальную точку мультиплатформенного приложения WinUI, вы можете скопировать код в него из классического проекта.
Копирование представления
Так как Uno Platform позволяет использовать уже знакомый вами вкус XAML, можно скопировать тот же код, который вы создали в предыдущем руководстве.
Вернитесь к проекту SimplePhotos из предыдущего руководства. В Обозреватель решений найдите файл с именем MainWindow.xaml
и откройте его. Обратите внимание, что содержимое представления определяется в элементе Window
, а не в элементе Page
. Это связано с тем, что классический проект является приложением WinUI 3, которое может использовать Window
элементы для определения содержимого представления:
<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>
Разоложите мультиплатформенную реализацию элементов управления, найденных в Window
элементе, например Image
GridView
, и RatingControl
убедитесь, что представление будет работать на всех поддерживаемых платформах только с тривиальным объемом усилий. Скопируйте его содержимое Window
и вставьте их Page
в элемент MainPage.xaml
файла в проекте UnoSimplePhotos Uno Platform. Представление MainPage
XAML должно выглядеть следующим образом:
<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>
Вы можете вспомнить, что в классическом решении также есть MainWindow.xaml.cs
файл, содержащий код, который соответствует представлению. В проекте Uno Platform код MainPage
для представления, скопированного в файл, содержится в MainPage.xaml.cs
файле.
Чтобы перенести этот код за несколькими платформами, сначала необходимо переместить следующий код в MainPage.xaml.cs
файл:
Images
свойство: предоставляетGridView
наблюдаемую коллекцию файлов изображенийСодержимое конструктора: вызовы
GetItemsAsync()
для заполненияImages
коллекции элементами, представляющими файлы изображенийУдаление ручного
ImageGridView
изменения свойства элемента управленияItemsSource
ImageGridView_ContainerContentChanging
метод. Используется в рамках стратегии для постепенной загрузкиGridView
элементов по мере прокрутки в представлениеShowImage
метод. Загружает файлы изображений в файлGridView
GetItemsAsync
метод. Возвращает файлы ресурсов образа изSamples
папкиLoadImageInfoAsync
метод: создаетImageFileInfo
объект из созданного объектаStorageFile
После перемещения всего, MainPage.xaml.cs
теперь должен выглядеть следующим образом:
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;
}
}
Примечание.
Файлы в проекте приложения Uno должны использоваться UnoSimplePhotos
в качестве пространства имен.
До сих пор файлы для основного представления, с которыми мы работаем, содержат все возможности классического решения. После копирования файла модели мы научимся ImageFileInfo.cs
изменять классические блоки кода для совместимости с несколькими платформами.
Скопируйте ImageFileInfo
из классического проекта и вставьте его в ImageFileInfo.cs
файл. Внесите следующие изменения:
Переименуйте пространство имен вместо
UnoSimplePhotos
SimplePhotos
:// Found towards the top of the file namespace UnoSimplePhotos;
Измените тип
OnPropertyChanged
параметра метода, чтобы иметь значение NULL:// string -> string? protected void OnPropertyChanged([CallerMemberName] string? propertyName = null) ...
Сделайте значение NULL допустимым
PropertyChangedEventHandler
:// PropertyChangedEventHandler -> PropertyChangedEventHandler? public event PropertyChangedEventHandler? PropertyChanged;
Сложенный файл должен выглядеть следующим образом:
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));
}
Этот класс будет служить моделью для представления файлов изображений в объекте GridView
. Хотя на этом этапе приложение должно быть технически возможно, он не может правильно отображать изображения или отображать их свойства. В следующих разделах мы введем набор изменений в эти скопированные файлы, чтобы сделать их совместимыми в контексте с несколькими платформами.
Использование директив препроцессора
В классическом проекте из предыдущего руководства MainPage.xaml.cs
файл содержит GetItemsAsync
метод, перечисляющий элементы из установленного StorageFolder
расположения пакета. Так как это расположение недоступно на определенных платформах, таких как WebAssembly, необходимо внести изменения в этот метод, чтобы сделать его совместимым со всеми платформами. Соответственно, мы внодем некоторые изменения в ImageFileInfo
класс, чтобы обеспечить совместимость.
Сначала внесите необходимые изменения в GetItemsAsync
метод. Замените GetItemsAsync
метод в MainPage.xaml.cs
файле следующим кодом:
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));
}
}
Этот метод теперь использует директиву препроцессора, чтобы определить, какой код будет выполняться на основе платформы. В Windows метод получает StorageFolder
представление установленного расположения пакета и возвращает папку из нее Samples
. На других платформах метод подсчитывает до 20, получая файлы изображений из Samples
папки с помощью Uri
файла изображения для представления файла изображения.
Затем настройте LoadImageInfoAsync
метод, чтобы вместить изменения, внесенные GetItemsAsync
в метод. Замените LoadImageInfoAsync
метод в MainPage.xaml.cs
файле следующим кодом:
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;
}
Как и в GetItemsAsync
случае с методом, этот метод теперь использует директиву препроцессора для определения кода, выполняемого на основе платформы. В Windows метод получает ImageProperties
из StorageFile
него и использует его для создания ImageFileInfo
объекта. На других платформах метод создает ImageFileInfo
объект без ImageProperties
параметра. Позже изменения будут внесены в ImageFileInfo
класс для удовлетворения этого изменения.
Элементы управления, такие как GridView
возможность постепенной загрузки обновленного содержимого контейнера элементов по мере прокрутки в окно просмотра. Это делается с помощью ContainerContentChanging
события. В классическом проекте из предыдущего руководства ImageGridView_ContainerContentChanging
метод использует это событие для загрузки файлов изображений в файл GridView
. Так как некоторые аспекты этого события не поддерживаются на всех платформах, нам потребуется внести изменения в этот метод, чтобы сделать его совместимым с ними.
Например, ContainerContentChangingEventArgs.Phase
свойство в настоящее время не поддерживается на платформах, отличных от Windows. Для удовлетворения этого изменения необходимо внести изменения в ImageGridView_ContainerContentChanging
метод. Замените ImageGridView_ContainerContentChanging
метод в MainPage.xaml.cs
файле следующим кодом:
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
}
Специализированный обратный вызов теперь зарегистрирован только в ContainerContentChangingEventArgs.RegisterUpdateCallback()
том случае, если платформа является Windows. ShowImage
В противном случае метод вызывается напрямую. Мы также должны внести изменения в ShowImage
метод, чтобы работать вместе с изменениями, внесенными в ImageGridView_ContainerContentChanging
метод. Замените ShowImage
метод в MainPage.xaml.cs
файле следующим кодом:
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
}
}
Опять же, директивы препроцессора гарантируют, что ContainerContentChangingEventArgs.Phase
свойство используется только на платформах, где она поддерживается. Мы используем ранее неиспользуемый GetImageSourceAsync()
метод для загрузки файлов изображений на GridView
платформы, отличные от Windows. На этом этапе мы будем учесть изменения, внесенные выше, изменив ImageFileInfo
класс.
Создание отдельного пути кода для других платформ
Обновите ImageFileInfo.cs
новое свойство, которое ImageSource
будет использоваться для загрузки файла образа.
public BitmapImage? ImageSource { get; private set; }
Так как платформы, такие как Интернет, не поддерживают расширенные свойства файлов изображений, которые легко доступны в Windows, мы добавим перегрузку конструктора, которая не требует типизированного ImageProperties
параметра. Добавьте новую перегрузку после существующего с помощью следующего кода:
public ImageFileInfo(StorageFile imageFile,
string name,
string type)
{
ImageName = name;
ImageFileType = type;
ImageFile = imageFile;
}
Эта перегрузка конструктора используется для создания ImageFileInfo
объекта на платформах, отличных от Windows. Так как мы сделали это, имеет смысл сделать ImageProperties
свойство nullable. ImageProperties
Обновите свойство, чтобы иметь значение NULL, используя следующий код:
public ImageProperties? ImageProperties { get; }
GetImageSourceAsync
Обновите метод, чтобы использовать ImageSource
свойство вместо возврата BitmapImage
только объекта. Замените GetImageSourceAsync
метод в ImageFileInfo.cs
файле следующим кодом:
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;
}
Чтобы предотвратить получение значения ImageProperties
, когда оно равно NULL, внесите следующие изменения:
Измените
ImageDimensions
свойство, чтобы использовать условный оператор NULL:public string ImageDimensions => $"{ImageProperties?.Width} x {ImageProperties?.Height}";
Измените
ImageTitle
свойство, чтобы использовать условный оператор NULL: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(); } } } }
Изменение
ImageRating
, чтобы не полагатьсяImageProperties
, создав случайный рейтинг звезд для демонстрационных целей: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 ImageFileInfo(ImageProperties properties, StorageFile imageFile, string name, string type) { ImageProperties = properties; ImageName = name; ImageFileType = type; ImageFile = imageFile; }
При этих изменениях ImageFileInfo
класс должен содержать следующий код. Теперь он имеет недавно разделенный путь кода для платформ, отличных от Windows:
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));
}
Этот ImageFileInfo
класс используется для представления файлов изображений в файле GridView
. Наконец, мы внодим изменения MainPage.xaml
в файл, чтобы вместить изменения в модель.
Использование разметки XAML для конкретной платформы
В разметке представления есть несколько элементов, которые должны оцениваться только в Windows. Добавьте новое пространство имен в Page
элемент MainPage.xaml
файла следующим образом:
...
xmlns:win="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Теперь замените MainPage.xaml
ItemsPanel
метод задания свойств в GridView
элементе следующим кодом:
win:ItemsPanel="{StaticResource ImageGridView_ItemsPanelTemplate}"
Предустановка имени win:
свойства гарантирует, что свойство задано только в Windows. Повторите это в ресурсе ImageGridView_ItemTemplate
. Мы хотим загрузить только элементы, использующие ImageDimensions
свойство в Windows. Замените TextBlock
элемент, использующий ImageDimensions
свойство следующим кодом:
<win:TextBlock Text="{x:Bind ImageDimensions}"
HorizontalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
Margin="8,0,0,0" />
Теперь MainPage.xaml
файл должен выглядеть следующим образом:
<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>
Запуск приложения
Запустите целевой UnoSimplePhotos.Windows
объект. Обратите внимание, что это приложение WinUI очень похоже на предыдущее руководство.
Теперь вы можете создать и запустить приложение на любой из поддерживаемых платформ. Для этого можно использовать раскрывающийся список панели инструментов отладки для выбора целевой платформы для развертывания:
Чтобы запустить голову WebAssembly (Wasm):
- Щелкните проект правой
UnoSimplePhotos.Wasm
кнопкой мыши, выберите "Задать в качестве запускаемого проекта" - Нажмите кнопку
UnoSimplePhotos.Wasm
для развертывания приложения - При желании можно добавить и использовать
UnoSimplePhotos.Server
проект в качестве альтернативы
- Щелкните проект правой
Отладка для iOS:
Щелкните проект правой
UnoSimplePhotos.Mobile
кнопкой мыши и выберите "Задать в качестве запускаемого проекта"В раскрывающемся списке панели инструментов отладки выберите активное устройство iOS или симулятор. Для работы вам потребуется связаться с Mac.
Отладка для Mac Catalyst:
- Щелкните проект правой
UnoSimplePhotos.Mobile
кнопкой мыши и выберите "Задать в качестве запускаемого проекта" - В раскрывающемся списке панели инструментов отладки выберите удаленное устройство macOS. Для работы вам потребуется связаться с одним из них.
- Щелкните проект правой
Отладка платформы Android :
- Щелкните проект правой
UnoSimplePhotos.Mobile
кнопкой мыши и выберите "Задать в качестве запускаемого проекта" - В раскрывающемся списке панели инструментов отладки выберите активное устройство Android или эмулятор
- Выберите активное устройство в подмене "Устройство"
- Щелкните проект правой
Чтобы выполнить отладку в Linux с помощью Skia GTK, выполните следующую команду:
- Щелкните проект правой
UnoSimplePhotos.Skia.Gtk
кнопкой мыши и выберите "Задать в качестве запускаемого проекта" - Нажмите кнопку
UnoSimplePhotos.Skia.Gtk
для развертывания приложения
- Щелкните проект правой
См. также
Windows developer