Windows Community Toolkit 3.0 - InfiniteCanvas

概述git

InfiniteCanvas 是一個 Canvas 控件,它支持無限畫布的滾動,支持 Ink,文本,格式文本,畫布縮放操做,撤銷重作操做,導入和導出數據。github

這是一個很是實用的控件,在「來畫視頻」 UWP 應用的繪畫功能中,也用到了這個控件,它對不一樣畫筆的選擇,橡皮擦,直尺和圓形尺,文字輸入和字體選擇等都提供了很便捷的支持,並且支持導入和導出數據,能夠很方便的創做繪畫做品,並能導出和分享給別人。 express

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

Windows Community Toolkit Doc - InfiniteCanvaswindows

Windows Community Toolkit Source Code - InfiniteCanvassession

Namespace: Microsoft.Toolkit.Uwp.UI.Controls; Nuget: Microsoft.Toolkit.Uwp.UI.Controls;app

 

開發過程字體

代碼結構分析ui

首先來看 InfiniteCanvas 的代碼結構,組成以下:spa

  • Commands - InfiniteCanvas 對應的全部命令,包括 redo/undo,Ink,Text 等,如上面示例圖中 Toolbar 上所示;
  • Controls - InfiniteCanvas 的主要控件都在這個文件夾裏;
  • Drawables - Text 和 Ink 主要的繪製功能在這個文件夾裏;
  • JsonConverters - 序列化和反序列化的主要功能,以及自定義的 Ink 屬性等;
  • InfiniteCanvas.Events.cs - InfiniteCanvas 的主要事件處理邏輯;
  • InfiniteCanvas.TextBox.cs - InfiniteCanvas 添加文字的文本框控件處理邏輯;
  • InfiniteCanvas.cs - InfiniteCanvas 控件的主要處理邏輯;
  • InfiniteCanvas.xaml - InfiniteCanvas 控件的 XAML 樣式文件;

 

InfiniteCanvas 總體類結構很清晰,每一個類的功能也很明確,下面咱們選取幾個重要的類來作分析。

1.  InfiniteCanvasCreateInkCommand

Command 文件夾的每個類都比較簡單,咱們找一個建立 Ink 命令看一下;該類實現了 IInfiniteCanvasCommand 這個接口,實現了 Execute() 和 Undo() 兩個方法;Command 初始化也很簡單,建立一個 InkDrawable 對象,初始化 drawableList 對象,執行建立時加入該 drawable,撤銷時把它從 drawableList 中去掉。  

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);
    }
}

2. InfiniteCanvasTextBox

用於 InfiniteCanvas 的文本框控件,從下圖的結構中能夠看到完整的文本框屬性定義方法,包括設置文字,設置編輯區域尺寸,文字變化的處理,光標位置的限制等。

來看一下判斷光標可以下移一行的 CannotGoDown() 方法,按照換行符來切割文字行,若是隻有一行則不可下移;當前選擇的結束,在最後一行時,也不可下移,其餘狀況均可如下移;

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) <= (_editZone.SelectionStart + _editZone.SelectionLength))
    {
        return true;
    }

    return false;
}

3. InfiniteCanvasVirtualDrawingSurface

用於渲染 Ink 和 Text 的虛擬 drawing surface,它有幾個部分類組成:

主要的處理邏輯,是利用 Commands 來操做 Ink 和 Text 的渲染執行和撤銷操做,計算渲染的尺寸空間,組織渲染的內容。

4. InkDrawable 和 TextDrawable

Ink 和 Text 可繪畫元素的繪畫邏輯,分別來看一下兩個類的組成:

 

咱們看一下兩個類的 Draw 方法:

Draw() - InkDrawable:

得到 Strokes 中每一個線條的每一個點,加入到集合中,從點集合建立線條,最後生成新的 Stroke 列表;遍歷完成後,把新的 Stroke 列表用於 drawingSession 的 DrawInk 方法來實現繪製; 

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);
}

Draw() - TextDrawble:

設置 Canvas 中文本的格式,使用文本和格式設置的 textLayout 來用於 drawingSession 的 DrawTextLayout 方法實現繪製;根據字體大小設置橫向偏移 HorizontalMarginBasedOnFont,固定的縱向偏移 verticalMargin;

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 三個部分類組成。

  • 其中 InfiniteCanvas.cs 這個類中主要是實現 OnApplyTemplate(),DependencyProperty 處理,控件的定義,事件註冊,Canvas 的基礎事件處理等,InfiniteCanvas 中實現了一個 InkCanvas 因此能夠實現各類筆觸的筆跡繪製;
  • InfiniteCanvas.Events.cs 主要是 Canvas 中的各類按鈕點擊等事件處理;
  • InfiniteCanvas.TextBox.cs 主要是 Canvas 中 TextBox 控件對應的控件定義和事件處理;

 

調用示例

InfiniteCanvas 控件的調用很是簡單,下面看看 XAML 中的調用:

<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>
    <controls:InfiniteCanvas Name="canvas" IsToolbarVisible="True"/>
  </Grid>
</Page>

 

總結

到這裏咱們就把 Windows Community Toolkit 3.0 中的 InfiniteCanvas 的源代碼實現過程講解完成了,但願能對你們更好的理解和使用這個功能有所幫助。InfiniteCanvas 控件在繪畫類場景中有很是多的應用,控件默認實現了多種筆觸的繪畫,橡皮,文字,redo undo 等重要功能,開發者也能夠根據 InfiniteCanvas 的實現自定義 Toolbar 的樣式和更多的繪畫筆觸,不一樣的筆畫保存方式等。

最後,再跟你們安利一下 WindowsCommunityToolkit 的官方微博:https://weibo.com/u/6506046490你們能夠經過微博關注最新動態。

衷心感謝 WindowsCommunityToolkit 的做者們傑出的工做,感謝每一位貢獻者,Thank you so much, ALL WindowsCommunityToolkit AUTHORS !!!

相關文章
相關標籤/搜索