Windows Community Toolkit 3.0 – InfiniteCanvas

概述

Infinitecanvas 是一個(gè) Canvas 控件,它支持無(wú)限畫(huà)布的滾動(dòng),支持 Ink,文本,格式文本,畫(huà)布縮放操作,撤銷(xiāo)重做操作,導(dǎo)入和導(dǎo)出數(shù)據(jù)。

這是一個(gè)非常實(shí)用的控件,在“來(lái)畫(huà)視頻” UWP 應(yīng)用的繪畫(huà)功能中,也用到了這個(gè)控件,它對(duì)不同畫(huà)筆的選擇,橡皮擦,直尺和圓形尺,文字輸入和字體選擇等都提供了很便捷的支持,而且支持導(dǎo)入和導(dǎo)出數(shù)據(jù),可以很方便的創(chuàng)作繪畫(huà)作品,并能導(dǎo)出和分享給別人。?

下面是 windows Community Toolkit Sample App 的示例截圖和 code/doc 地址:

Windows Community Toolkit 3.0 – InfiniteCanvas

Windows Community Toolkit Doc – InfiniteCanvas

Windows Community Toolkit Source Code –?InfiniteCanvas

Namespace:?microsoft.Toolkit.Uwp.ui.Controls;?Nuget:?Microsoft.Toolkit.Uwp.UI.Controls;

開(kāi)發(fā)過(guò)程

代碼結(jié)構(gòu)分析

首先來(lái)看 InfiniteCanvas 的代碼結(jié)構(gòu),組成如下:

Commands – InfiniteCanvas 對(duì)應(yīng)的所有命令,包括 redo/undo,Ink,Text 等,如上面示例圖中 Toolbar 上所示;Controls –?InfiniteCanvas 的主要控件都在這個(gè)文件夾里;Drawables – Text 和 Ink 主要的繪制功能在這個(gè)文件夾里;JsonConverters – 序列化和反序列化的主要功能,以及自定義的 Ink 屬性等;InfiniteCanvas.Events.cs –?InfiniteCanvas 的主要事件處理邏輯;InfiniteCanvas.TextBox.cs –?InfiniteCanvas 添加文字的文本框控件處理邏輯;InfiniteCanvas.cs –?InfiniteCanvas 控件的主要處理邏輯;InfiniteCanvas.xaml –?InfiniteCanvas 控件的 XAML 樣式文件;

Windows Community Toolkit 3.0 – InfiniteCanvas

InfiniteCanvas 整體類結(jié)構(gòu)很清晰,每個(gè)類的功能也很明確,下面我們選取幾個(gè)重要的類來(lái)做分析。

1.??InfiniteCanvasCreateInkCommand

Command 文件夾的每一個(gè)類都比較簡(jiǎn)單,我們找一個(gè)創(chuàng)建 Ink 命令看一下;該類實(shí)現(xiàn)了 IInfiniteCanvasCommand 這個(gè)接口,實(shí)現(xiàn)了 Execute() 和 Undo() 兩個(gè)方法;Command 初始化也很簡(jiǎn)單,創(chuàng)建一個(gè) InkDrawable 對(duì)象,初始化 drawableList 對(duì)象,執(zhí)行創(chuàng)建時(shí)加入該 drawable,撤銷(xiāo)時(shí)把它從 drawableList 中去掉。

代碼語(yǔ)言:JavaScript代碼運(yùn)行次數(shù):0運(yùn)行復(fù)制

internal class InfiniteCanvasCreateInkCommand : IInfiniteCanvasCommand{    private readonly List<idrawable> _drawableList;    private readonly InkDrawable _drawable;    public InfiniteCanvasCreateInkCommand(List<idrawable> drawableList, IReadOnlyList<inkstroke> strokes)    {        _drawable = new InkDrawable(strokes);        _drawableList = drawableList;    }    public void Execute()    {        _drawableList.Add(_drawable);    }    public void Undo()    {        _drawableList.Remove(_drawable);    }}</inkstroke></idrawable></idrawable>

2.?InfiniteCanvasTextBox

用于 InfiniteCanvas 的文本框控件,從下圖的結(jié)構(gòu)中可以看到完整的文本框?qū)傩远x方法,包括設(shè)置文字,設(shè)置編輯區(qū)域尺寸,文字變化的處理,光標(biāo)位置的限制等。

Windows Community Toolkit 3.0 – InfiniteCanvas

來(lái)看一下判斷光標(biāo)能夠下移一行的 CannotGoDown() 方法,按照換行符來(lái)切割文字行,如果只有一行則不可下移;當(dāng)前選擇的結(jié)束,在最后一行時(shí),也不可下移,其他情況都可以下移;

代碼語(yǔ)言:javascript代碼運(yùn)行次數(shù):0運(yùn)行復(fù)制

internal bool CannotGoDown(){    var lines = _editZone.Text.Split('r');    if (lines.Count() == 1)    {        return true;    }    var lastLine = lines.ElementAt(lines.Length - 1);    if ((_editZone.Text.Length - lastLine.Length) <p>3.?InfiniteCanvasVirtualDrawingSurface</p><p>用于渲染 Ink 和 Text 的虛擬 drawing surface,它有幾個(gè)部分類組成:</p><figure class=""><img src="https://img.php.cn/upload/article/001/503/042/174693481031747.jpg" alt="Windows Community Toolkit 3.0 - InfiniteCanvas"></figure><p>主要的處理邏輯,是利用 Commands 來(lái)操作 Ink 和 Text 的渲染執(zhí)行和撤銷(xiāo)操作,計(jì)算渲染的尺寸空間,組織渲染的內(nèi)容。</p><p>4.?InkDrawable 和 TextDrawable</p><p>Ink 和 Text 可繪畫(huà)元素的繪畫(huà)邏輯,分別來(lái)看一下兩個(gè)類的組成:</p><figure class=""><img src="https://img.php.cn/upload/article/001/503/042/174693481057252.jpg" alt="Windows Community Toolkit 3.0 - InfiniteCanvas"></figure><figure class=""><img src="https://img.php.cn/upload/article/001/503/042/174693481061442.jpg" alt="Windows Community Toolkit 3.0 - InfiniteCanvas"></figure><p>我們看一下兩個(gè)類的 Draw 方法:</p><p>Draw() - InkDrawable:</p><p>獲得 Strokes 中每個(gè)線條的每個(gè)點(diǎn),加入到集合中,從點(diǎn)集合創(chuàng)建線條,最后生成新的 Stroke 列表;遍歷完成后,把新的 Stroke 列表用于 drawingSession 的 DrawInk 方法來(lái)實(shí)現(xiàn)繪制;?</p>代碼語(yǔ)言:javascript<i class="icon-code"></i>代碼運(yùn)行次數(shù):<!-- -->0<svg xmlns="http://www.w3.org/2000/svg" width="16"    style="max-width:90%" viewbox="0 0 16 16" fill="none"><path d="M6.66666 10.9999L10.6667 7.99992L6.66666 4.99992V10.9999ZM7.99999 1.33325C4.31999 1.33325 1.33333 4.31992 1.33333 7.99992C1.33333 11.6799 4.31999 14.6666 7.99999 14.6666C11.68 14.6666 14.6667 11.6799 14.6667 7.99992C14.6667 4.31992 11.68 1.33325 7.99999 1.33325ZM7.99999 13.3333C5.05999 13.3333 2.66666 10.9399 2.66666 7.99992C2.66666 5.05992 5.05999 2.66659 7.99999 2.66659C10.94 2.66659 13.3333 5.05992 13.3333 7.99992C13.3333 10.9399 10.94 13.3333 7.99999 13.3333Z" fill="currentcolor"></path></svg>運(yùn)行<svg width="16" height="16" viewbox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M4.5 15.5V3.5H14.5V15.5H4.5ZM12.5 5.5H6.5V13.5H12.5V5.5ZM9.5 2.5H3.5V12.5H1.5V0.5H11.5V2.5H9.5Z" fill="currentcolor"></path></svg>復(fù)制<pre class="prism-token token line-numbers javascript">public void Draw(CanvasDrawingSession drawingSession, Rect sessionBounds){    var finalStrokeList = new List<inkstroke>(Strokes.Count);    foreach (var stroke in Strokes)    {        var points = stroke.GetInkPoints();        var finalPointList = new List<inkpoint>(points.Count);        foreach (var point in points)        {            finalPointList.Add(MapPointToToSessionBounds(point, sessionBounds));        }        StrokeBuilder.SetDefaultDrawingAttributes(stroke.DrawingAttributes);        var newStroke = StrokeBuilder.CreateStrokeFromInkPoints(finalPointList, stroke.PointTransform);        finalStrokeList.Add(newStroke);    }    drawingSession.DrawInk(finalStrokeList);}</inkpoint></inkstroke>

Draw() – TextDrawble:

設(shè)置 Canvas 中文本的格式,使用文本和格式設(shè)置的 textLayout 來(lái)用于 drawingSession 的 DrawTextLayout 方法實(shí)現(xiàn)繪制;根據(jù)字體大小設(shè)置橫向偏移 HorizontalMarginBasedOnFont,固定的縱向偏移 verticalMargin;

代碼語(yǔ)言:javascript代碼運(yùn)行次數(shù):0運(yùn)行復(fù)制

public void Draw(CanvasDrawingSession drawingSession, Rect sessionBounds){    const int verticalMargin = 3;    CanvasTextFormat format = new CanvasTextFormat    {        FontSize = FontSize,        WordWrapping = CanvasWordWrapping.NoWrap,        FontWeight = IsBold ? FontWeights.Bold : FontWeights.Normal,        FontStyle = IsItalic ? FontStyle.Italic : FontStyle.Normal    };    CanvasTextLayout textLayout = new CanvasTextLayout(drawingSession, Text, format, 0.0f, 0.0f);    drawingSession.DrawTextLayout(textLayout, (float)(Bounds.X - sessionBounds.X + HorizontalMarginBasedOnFont), (float)(Bounds.Y - sessionBounds.Y + verticalMargin), TextColor);}?

5.?InfiniteCanvas

InfiniteCanvas 是主要邏輯處理,由?InfiniteCanvas.cs,InfiniteCanvas.Events.cs,InfiniteCanvas.TextBox.cs 三個(gè)部分類組成。

其中?InfiniteCanvas.cs 這個(gè)類中主要是實(shí)現(xiàn)?OnApplyTemplate(),DependencyProperty 處理,控件的定義,事件注冊(cè),Canvas 的基礎(chǔ)事件處理等,InfiniteCanvas 中實(shí)現(xiàn)了一個(gè) InkCanvas 所以可以實(shí)現(xiàn)各種筆觸的筆跡繪制;InfiniteCanvas.Events.cs 主要是 Canvas 中的各種按鈕點(diǎn)擊等事件處理;InfiniteCanvas.TextBox.cs 主要是 Canvas 中 TextBox 控件對(duì)應(yīng)的控件定義和事件處理;

調(diào)用示例

InfiniteCanvas 控件的調(diào)用非常簡(jiǎn)單,下面看看 XAML 中的調(diào)用:

代碼語(yǔ)言:javascript代碼運(yùn)行次數(shù):0運(yùn)行復(fù)制

<page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:ignorable="d"><grid><infinitecanvas name="canvas" istoolbarvisible="True"></infinitecanvas></grid></page>

總結(jié)

到這里我們就把 Windows Community Toolkit 3.0 中的?InfiniteCanvas?的源代碼實(shí)現(xiàn)過(guò)程講解完成了,希望能對(duì)大家更好的理解和使用這個(gè)功能有所幫助。InfiniteCanvas 控件在繪畫(huà)類場(chǎng)景中有非常多的應(yīng)用,控件默認(rèn)實(shí)現(xiàn)了多種筆觸的繪畫(huà),橡皮,文字,redo undo 等重要功能,開(kāi)發(fā)者也可以根據(jù)?InfiniteCanvas 的實(shí)現(xiàn)自定義 Toolbar 的樣式和更多的繪畫(huà)筆觸,不同的筆畫(huà)保存方式等。

最后,再跟大家安利一下 WindowsCommunityToolkit 的官方微博:https://weibo.com/u/6506046490,?大家可以通過(guò)微博關(guān)注最新動(dòng)態(tài)。

衷心感謝 WindowsCommunityToolkit 的作者們杰出的工作,感謝每一位貢獻(xiàn)者,Thank you so much, ALL WindowsCommunityToolkit AUTHORS !!!

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點(diǎn)贊14 分享