[UWP]使用Picker構建應用內圖片公共裁剪組件

在上一篇博文《[UWP]如何實現UWP平臺最佳圖片裁剪控件》中我講解了編寫ImageCropper控件的過程及知識分享。在那篇文章裏,我大言不慚的稱其爲UWP平臺最佳圖片裁剪控件(主要是沒有別的相似控件來充當對手🤣)。其實寫博客時的那個版本連交互動畫都尚未,遠遠稱不上「最佳」。html

不過,這一個月來ImageCropper通過數次迭代,它已經具備很是棒的體驗了!另外還有一件使人興奮的事,它即將加入WindowsCommunityToolkit中(PR已經接近尾聲)!這將讓更多UWP開發者能夠體驗它。git

今天這篇博文我來說下ImageCropper的一個特殊應用實例。github

先來思考一下這個問題:express

若是咱們的應用只有一個界面須要調用圖片剪裁,那很好,直接在這個頁面嵌入ImageCropper控件就能夠輕鬆完成了。可是,若是這個應用有多處須要裁剪圖片的地方(譬如說上傳圖片、修剪照片、裁剪頭像...),咱們是否能夠爲這些須要裁剪圖片的地方構建一個公共的可調用組件呢?windows

事實上,咱們能夠藉助HHChaosToolkit中的Picker組件來完成這一想法。框架

以前我寫過一系列關於Picker的文章,不瞭解Picker的話建議能夠抽時間閱讀:async

那麼,咱們今天使用Picker和ImageCropper來爲應用構建一個公共裁剪組件。ide

先看下效果圖:佈局

ImageCropper

如何實現的呢?動畫

項目結構

建立一個空白UWP應用項目,並引入了HHChaosToolkit.UWP以及ImageCropper.UWP這兩個Nuget包。

Nuget

我在HHChaosToolkit項目首頁寫過:

HHChaosToolkit是一套適用於MVVM框架下使用的彈窗層組件庫。

HHChaosToolkit其中包含了Picker彈窗組件、SubWindows子窗口組件以及輕量級MVVM框架。咱們在這個例子中使用到的是它的Picker以及MVVM部分。

ImageCropper則是這個例子中主要依賴的功能組件。

項目結構以下圖:

Solution

其中包含了兩個頁面,ImageCropperPickerPage便是這個例子中咱們要構建的圖片公共裁剪組件,MainPage中包含了調用使用公共裁剪組件的示例,邏輯代碼均在其對應的ViewModel中。

圖片裁剪Picker

ImageCropperPicker就是咱們想要的圖片公共裁剪組件的Picker實現,它能夠在任意地方調用(不管是在View仍是ViewModel的代碼中),且不會中斷當前的操做,其調用原理能夠查看我以前的博文《[UWP]使用Popup構建UWP Picker》。

這個頁面中包含了ImageCropper控件,用於實現圖片裁剪功能。

ImageCropper

界面佈局以下:

<Page
    x:Class="ImageCropperPicker.Views.ImageCropperPickerPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:cropper="using:ImageCropper.UWP"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="using:ImageCropperPicker.Views"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    DataContext="{Binding ImageCropperPickerViewModel, Source={StaticResource Locator}}"
    RequestedTheme="Dark"
    mc:Ignorable="d">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <cropper:ImageCropper
            x:Name="ImageCropper"
            Padding="80"
            AspectRatio="{Binding AspectRatio}"
            Background="Transparent"
            CircularCrop="{Binding CircularCrop}"
            Mask="#af000000"
            SourceImage="{Binding SourceImage}" />
        <Grid Grid.Column="1" Background="#af000000">
            <StackPanel Padding="55" VerticalAlignment="Center">
                <AppBarButton
                    Margin="0,15"
                    Click="Button_Click"
                    Icon="Accept"
                    Label="OK" />
                <AppBarButton
                    Command="{Binding ExitCommand}"
                    Foreground="White"
                    Icon="Cancel"
                    Label="Cancel" />
            </StackPanel>
        </Grid>
    </Grid>
</Page>

邏輯層代碼中,我使用ImageCropperConfig類來接受調用方傳來的參數,並在打開Picker時讀取參數,初始化Picker。

ImageCropperConfig定義以下:

public class ImageCropperConfig
    {
        public StorageFile ImageFile { get; set; }
        public double AspectRatio { get; set; } = -1;
        public bool CircularCrop { get; set; }
    }

ImageCropperPicker的ViewModel代碼:

using HHChaosToolkit.UWP.Mvvm;
using ImageCropperPicker.Models;
using System;
using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Xaml.Navigation;

namespace ImageCropperPicker.ViewModels
{
    public class ImageCropperPickerViewModel : ObjectPickerBase<WriteableBitmap>
    {
        private WriteableBitmap _sourceImage;
        public WriteableBitmap SourceImage
        {
            get => _sourceImage;
            set => Set(ref _sourceImage, value);
        }
        private double _aspectRatio;
        public double AspectRatio
        {
            get => _aspectRatio;
            set => Set(ref _aspectRatio, value);
        }
        private bool _circularCrop;
        public bool CircularCrop
        {
            get => _circularCrop;
            set => Set(ref _circularCrop, value);
        }
        public async override void OnNavigatedTo(NavigationEventArgs e)
        {
            if (e.Parameter is ImageCropperConfig config)
            {
                var writeableBitmap = new WriteableBitmap(1, 1);
                using (var stream = await config.ImageFile.OpenReadAsync())
                {
                    await writeableBitmap.SetSourceAsync(stream);
                }

                SourceImage = writeableBitmap;
                AspectRatio = config.AspectRatio;
                CircularCrop = config.CircularCrop;
            }
            base.OnNavigatedTo(e);
        }
    }
}

在這裏,完成裁剪圖片的操做我放在了界面上OK按鍵的後臺代碼中(實際上仍是調用的Picker中的方法):

private async void Button_Click(object sender, RoutedEventArgs e)
        {
            var img = await ImageCropper.GetCroppedBitmapAsync();
            ViewModel?.SetResult(img);
        }

應用內調用

調用方法很是的簡單,直接使用HHChaosToolkit中的ObjectPickerService來啓動Picker,而且等待Picker返回結果便可。

比較特別的是,在這裏我經過使用PickerOpenOption讓裁剪圖片Picker能夠覆蓋到整個應用界面,UI表現看起來更佳。

調用代碼:

private async Task<ImageSource> CropImage(ImageCropperConfig config)
        {
            var startOption = new PickerOpenOption
            {
                VerticalAlignment = VerticalAlignment.Stretch,
                HorizontalAlignment = HorizontalAlignment.Stretch,
            };
            var ret = await ViewModelLocator.Current.ObjectPickerService.PickSingleObjectAsync<WriteableBitmap>(
                typeof(ImageCropperPickerViewModel).FullName, config, startOption);
            if (!ret.Canceled)
            {
                return ret.Result;
            }
            return null;
        }

是否是看起來很簡單?是的,優雅的調用方式就應該這麼簡單!

結尾

正如我以前所說,Picker組件在不少地方均可以派上用處,咱們幾乎能夠用它來實現一切UWP自定義彈出框,很是歡迎有相似需求的開發者們試用!

這個示例項目開源在Github中,感興趣的能夠自行Clone編譯,同時項目中的代碼請隨意享用,喜歡的話請不要忘記Star!

本篇博客到此結束!謝謝你們閱讀!

相關文章
相關標籤/搜索