我想在 WinUI 3 中创建一个名为 ImageViewer 的用户控件,它将显示、遍历、添加和删除照片。
我的用户控件包含以下属性:
由于 ImageViewer 是一个 UserControl,它继承自类 UserControl。为了在按下 Previous/Next/Add/Delete 按钮时能够更改照片,Photos Collection 和 Current* Properties 必须能够通知 UI 某些内容已更改(即当前照片已被删除,因此显示最后一张一)。 但是,我的理解是,一个类必须继承自 ObservableRecipient,从而对其数据(例如 Photos、CurrentPhotoIndex)使用 OnPropertyChanged 方法。我的想法是 Photos 集合不能在代码隐藏 (ImageViewer.xaml.cs) 中,而是在 ViewModel (ImageViewerViewModel) 中,然后我就可以遍历/添加/删除照片了。
我的问题是在示例中(我从中获得灵感)没有使用 ViewModel,我不知道如何将 DependencyProperty 数据设置为直接来自 ViewModel。
我的问题:
我希望我的问题提出得很好。如果有人需要更多信息,我将非常乐意提供。
ImageViewer.xaml
<!-- Copyright (c) Microsoft Corporation and Contributors. -->
<!-- Licensed under the MIT License. -->
<UserControl
x:Class="MyApp.Desktop.UserControls.ImageViewer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyApp.Desktop.UserControls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:dxe="using:DevExpress.WinUI.Editors"
mc:Ignorable="d">
<Grid Padding="12">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal"
Grid.Row="0">
<Grid>
<Image Source="{x:Bind CurrentPhotoPath, Mode=TwoWay}"
Stretch="Uniform"/>
<Button Click="BtnPrevPhoto_Click"
VerticalAlignment="Center" HorizontalAlignment="Left"
Style="{StaticResource PreviousButtonStyle}"/>
<Button Click="BtnNextPhoto_Click"
VerticalAlignment="Center" HorizontalAlignment="Right"
Style="{StaticResource NextButtonStyle}"/>
</Grid>
<StackPanel Orientation="Vertical"
VerticalAlignment="Center"
Margin="{StaticResource SmallLeftMargin}"
Grid.Column="1">
<Button Click="BtnAddPhoto_Click"
Margin="{StaticResource XXSmallBottomMargin}">
<FontIcon Glyph="" Style="{StaticResource SmallFontIconStyle}"/>
</Button>
<Button Click="BtnDeletePhoto_Click"
Margin="{StaticResource XXSmallTopMargin}">
<FontIcon Glyph="" Style="{StaticResource SmallFontIconStyle}"/>
</Button>
</StackPanel>
</StackPanel>
<dxe:TextEdit x:Uid="/Products/TxtPhotoDescription"
Text="{x:Bind CurrentPhotoDescription, Mode=TwoWay}"
IsReadOnly="{x:Bind IsReadOnly, Mode=OneWay}"
Style="{StaticResource FormSmallTextEditStyle}"
Margin="{StaticResource SmallTopMargin}"
HorizontalAlignment="Stretch"
Grid.Row="1"/>
</Grid>
</UserControl>
ImageViewer.xaml.cs
// Copyright (c) Microsoft Corporation and Contributors.
// Licensed under the MIT License.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using CommunityToolkit.Mvvm.ComponentModel;
using DevExpress.Mvvm.Native;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using MyApp.Desktop.Helpers;
using MyApp.Desktop.ViewModels.UserControls;
using MyApp.Models.FileStorage;
using MyApp.Models.Products;
namespace ThemelioApp.Desktop.UserControls;
public sealed partial class ImageViewer : UserControl
{
public static readonly DependencyProperty PhotosProperty = DependencyProperty.Register(
"Photos", typeof(ICollection<Product_PhotoModel>), typeof(ImageViewer), new PropertyMetadata(null));
public ICollection<Product_PhotoModel> Photos
{
get => (ICollection<Product_PhotoModel>)GetValue(PhotosProperty);
set => SetValue(PhotosProperty, value);
}
public static readonly DependencyProperty PhotosToDeleteProperty = DependencyProperty.Register(
"PhotosToDelete", typeof(ICollection<long>), typeof(ImageViewer), new PropertyMetadata(null));
public ICollection<long> PhotosToDelete
{
get => (ICollection<long>)GetValue(PhotosToDeleteProperty);
set => SetValue(PhotosToDeleteProperty, value);
}
public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register(
"IsReadOnlyProperty", typeof(bool), typeof(ImageViewer), new PropertyMetadata(null));
public bool IsReadOnly
{
get => (bool)GetValue(IsReadOnlyProperty);
set => SetValue(IsReadOnlyProperty, value);
}
public int CurrentPhotoIndex
{
get; set;
}
private readonly string _noPhotoPath;
public string CurrentPhotoPath
{
get; set;
}
public string CurrentPhotoDescription
{
get; set;
}
public ImageViewerViewModel ViewModel
{
get;
}
public ImageViewer()
{
InitializeComponent();
ViewModel = App.GetService<ImageViewerViewModel>();
Photos = new ObservableCollection<Product_PhotoModel>();
PhotosToDelete = new ObservableCollection<long>();
CurrentPhotoIndex = 0;
_noPhotoPath = new Uri("ms-appx:///assets/Images/NoImage128x128.png").LocalPath;
CurrentPhotoPath = Photos.Count == 0 ? _noPhotoPath : Photos.FirstOrDefault().Photo.LocalFilePath;
CurrentPhotoDescription = "";
}
private async void BtnAddPhoto_Click(object sender, RoutedEventArgs e)
{
var file = await FileStorageHelper.PickFile(FileStorageHelper.PickFileType.image, true);
if (file == null)
{
return;
}
AddPhotoLocal(file.Path);
}
private async void BtnDeletePhoto_Click(object sender, RoutedEventArgs e)
{
RemovePhotoLocal();
}
private void BtnNextPhoto_Click(object sender, RoutedEventArgs e)
{
Next();
}
private void BtnPrevPhoto_Click(object sender, RoutedEventArgs e)
{
Previous();
}
public void Next()
{
CurrentPhotoIndex++;
UpdateCurrentPhoto();
}
public void Previous()
{
CurrentPhotoIndex--;
UpdateCurrentPhoto();
}
public void AddPhotoLocal(string path)
{
Photos.Add(
new Product_PhotoModel()
{
Photo = new FileStorageDetailedModel()
{
Description = "",
LocalFilePath = path
}
});
}
public void RemovePhotoLocal()
{
PhotosToDelete ??= new List<long>();
var toDelete = Photos.ElementAt(CurrentPhotoIndex);
if (toDelete.Id > 0)
{
PhotosToDelete.Add(toDelete.Photo.Id);
}
Photos.Remove(toDelete);
UpdateCurrentPhoto();
}
public void UpdateCurrentPhoto()
{
if (CurrentPhotoIndex > Photos?.Count - 1)
{
CurrentPhotoIndex = (int)(Photos?.Count - 1);
}
else if (CurrentPhotoIndex < 0)
{
CurrentPhotoIndex = 0;
}
CurrentPhotoPath = Photos.ElementAt(CurrentPhotoIndex).Photo.LocalFilePath;
CurrentPhotoDescription = Photos.ElementAt(CurrentPhotoIndex).Photo.Description;
}
}
ImageViewerViewModel.cs(目前是空的,因为我无法让它工作)
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.UI.Xaml;
using MyApp.Desktop.UserControls;
using MyApp.Models.FileStorage;
using MyApp.Models.Products;
namespace MyApp.Desktop.ViewModels.UserControls;
public class ImageViewerViewModel : ObservableRecipient
{
public ImageViewerViewModel()
{
}
}