重新想象 Windows 8 Store Apps (51) - 输入: 涂鸦板

本文涉及的产品
票证核验,票证核验 50次/账号
小语种识别,小语种识别 200次/月
教育场景识别,教育场景识别 200次/月
简介: 原文:重新想象 Windows 8 Store Apps (51) - 输入: 涂鸦板[源码下载] 重新想象 Windows 8 Store Apps (51) - 输入: 涂鸦板 作者:webabcd介绍重新想象 Windows 8 Store Apps 之 涂鸦板 通过 Pointer...
原文: 重新想象 Windows 8 Store Apps (51) - 输入: 涂鸦板

[源码下载]


重新想象 Windows 8 Store Apps (51) - 输入: 涂鸦板



作者:webabcd


介绍
重新想象 Windows 8 Store Apps 之 涂鸦板

  • 通过 Pointer 相关事件实现一个具有基本功能的涂鸦板
  • 通过 InkManager 实现一个功能完善的涂鸦板



示例
1、演示如何通过 Pointer 相关事件实现一个只有基本功能的涂鸦板
Input/Ink/Simple.xaml

<Page
    x:Class="XamlDemo.Input.Ink.Simple"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:XamlDemo.Input.Ink"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="Transparent">
        <StackPanel Margin="120 0 0 0">

            <Button Name="btnClear" Content="清除" Click="btnClear_Click_1" />

            <Canvas Name="canvas" Background="Blue" Width="800" Height="480" HorizontalAlignment="Left" Margin="0 10 0 0" />

        </StackPanel>
    </Grid>
</Page>

Input/Ink/Simple.xaml.cs

/*
 * 通过 Pointer 相关事件实现一个只有基本功能的涂鸦板
 */

using System;
using System.Collections.Generic;
using Windows.Foundation;
using Windows.UI;
using Windows.UI.Input;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;

namespace XamlDemo.Input.Ink
{
    public sealed partial class Simple : Page
    {
        // 用于保存触摸点(PointerId - Point)
        private Dictionary<uint, Point?> _dicPoint;

        public Simple()
        {
            this.InitializeComponent();

            canvas.PointerPressed += canvas_PointerPressed;
            canvas.PointerMoved += canvas_PointerMoved;
            canvas.PointerReleased += canvas_PointerReleased;
            canvas.PointerExited += canvas_PointerExited;

            _dicPoint = new Dictionary<uint, Point?>();
        }

        void canvas_PointerPressed(object sender, PointerRoutedEventArgs e)
        {
            // 指针按下后,保存此触摸点
            PointerPoint pointerPoint = e.GetCurrentPoint(canvas);
            _dicPoint[pointerPoint.PointerId] = pointerPoint.Position;
        }

        void canvas_PointerMoved(object sender, PointerRoutedEventArgs e)
        {
            PointerPoint pointerPoint = e.GetCurrentPoint(canvas);

            if (_dicPoint.ContainsKey(pointerPoint.PointerId) && _dicPoint[pointerPoint.PointerId].HasValue)
            {
                Point currentPoint = pointerPoint.Position;
                Point previousPoint = _dicPoint[pointerPoint.PointerId].Value;

                // 如果指针移动过程中,两个点间的距离超过 4 则在两点间绘制一条直线,以完成涂鸦
                if (ComputeDistance(currentPoint, previousPoint) > 4)
                {
                    Line line = new Line()
                    {
                        X1 = previousPoint.X,
                        Y1 = previousPoint.Y,
                        X2 = currentPoint.X,
                        Y2 = currentPoint.Y,
                        StrokeThickness = 5,
                        Stroke = new SolidColorBrush(Colors.Orange),
                        StrokeEndLineCap = PenLineCap.Round
                    };

                    _dicPoint[pointerPoint.PointerId] = currentPoint;
                    canvas.Children.Add(line);
                }
            }
        }

        void canvas_PointerReleased(object sender, PointerRoutedEventArgs e)
        {
            // 指针释放后,从字典中删除此 PointerId 的数据
            PointerPoint pointerPoint = e.GetCurrentPoint(canvas);
            if (_dicPoint.ContainsKey(pointerPoint.PointerId))
                _dicPoint.Remove(pointerPoint.PointerId);
        }

        void canvas_PointerExited(object sender, PointerRoutedEventArgs e)
        {
            // 指针离开相当于指针释放
            canvas_PointerReleased(sender, e);
        }

        // 清除涂鸦
        private void btnClear_Click_1(object sender, RoutedEventArgs e)
        {
            canvas.Children.Clear();
            _dicPoint.Clear();
        }

        // 计算两个点(Point)之间的距离
        private double ComputeDistance(Point point1, Point point2)
        {
            return Math.Sqrt(Math.Pow(point1.X - point2.X, 2) + Math.Pow(point1.Y - point2.Y, 2));
        }
    }
}


2、演示如何通过 InkManager 实现一个功能完善的涂鸦板
Input/Ink/InkRenderer.cs

/*
 * 开发一个 InkRenderer 类,用于呈现 InkManager 中的内容
 * 
 * 注:InkManager 用于 ink 的管理,但并不负责呈现
 * 
 * 
 * InkStroke - ink 画笔
 *     DrawingAttributes - 画笔的相关属性,一个 InkDrawingAttributes 类型的对象
 *         Color - 颜色
 *         FitToCurve - 使用贝塞尔曲线则为 true,使用直线则为 false,默认值为 true
 *         PenTip - 笔尖的形状(Circle, Rectangle)
 *         Size - 笔尖的尺寸
 *         IgnorePressure - 是否忽略笔尖在触摸屏上的压力
 *     BoundingRect - ink 画笔的边界框
 *     Recognized - 该画笔是否已被文字识别
 *     Selected - 该画笔是否被选中
 *     Clone() - 克隆这个画笔,并返回克隆后的新的 InkStroke 对象
 *     GetRenderingSegments() - 返回 ink 的线段集合,即 InkStrokeRenderingSegment 集合
 *     
 * InkStrokeRenderingSegment - ink 的线段
 *     BezierControlPoint1 - 贝塞尔曲线的第一个控制点的位置
 *     BezierControlPoint2 - 贝塞尔曲线的第二个控制点的位置
 *     Position - 贝塞尔曲线的终点的位置
 */

using System.Collections.Generic;
using Windows.UI;
using Windows.UI.Input;
using Windows.UI.Input.Inking;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;

namespace XamlDemo.Input.Ink
{
    public class InkRenderer
    {
        /// <summary>
        /// 呈现 ink 的容器
        /// </summary>
        private readonly Panel _container = null;

        /// <summary>
        /// ink 的实时路径(PointerId - Path)
        /// </summary>
        private readonly Dictionary<uint, Path> _dicLivePath = null;
        /// <summary>
        /// ink 的实时路径中所包含的 PolyLineSegment(PointerId - PolyLineSegment)
        /// </summary>
        private readonly Dictionary<uint, PolyLineSegment> _dicLivePolyLine = null;

        /// <summary>
        /// InkStroke 所对应三次贝塞尔曲线,用于保存全部涂鸦曲线(包括未被选中的和被选中的)
        /// </summary>
        private readonly Dictionary<InkStroke, Path> _dicInkPath = null;
        /// <summary>
        /// InkStroke 所对应三次贝塞尔曲线,用于保存被选中的涂鸦曲线
        /// </summary>
        private readonly Dictionary<InkStroke, Path> _dicSelectionPath = null;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="panel">用于呈现 ink 的容器</param>
        public InkRenderer(Panel panel)
        {
            _container = panel;

            _dicLivePath = new Dictionary<uint, Path>();
            _dicLivePolyLine = new Dictionary<uint, PolyLineSegment>();

            _dicInkPath = new Dictionary<InkStroke, Path>();
            _dicSelectionPath = new Dictionary<InkStroke, Path>();
        }

        /// <summary>
        /// 开始实时绘制 ink
        /// </summary>
        /// <param name="pointerPoint">当前的触摸点</param>
        /// <param name="inkDrawingAttributes">ink 的 InkDrawingAttributes 数据</param>
        public void EnterLiveRendering(PointerPoint pointerPoint, InkDrawingAttributes inkDrawingAttributes)
        {
            uint pointerId = pointerPoint.PointerId;

            var polyLine = new PolyLineSegment();
            polyLine.Points.Add(pointerPoint.Position);
            var figure = new PathFigure();
            figure.StartPoint = pointerPoint.Position;
            figure.Segments.Add(polyLine);
            var geometry = new PathGeometry();
            geometry.Figures.Add(figure);
            var path = new Path();
            path.Data = geometry;

            path.Stroke = new SolidColorBrush(inkDrawingAttributes.Color);
            path.StrokeThickness = inkDrawingAttributes.Size.Width;
            path.StrokeLineJoin = PenLineJoin.Round;
            path.StrokeStartLineCap = PenLineCap.Round;

            _dicLivePolyLine.Add(pointerId, polyLine);
            _dicLivePath.Add(pointerId, path);

            _container.Children.Add(path);
        }

        /// <summary>
        /// 更新 ink 的实时绘制
        /// </summary>
        /// <param name="pointerPoint"></param>
        public void UpdateLiveRender(PointerPoint pointerPoint)
        {
            uint pointerId = pointerPoint.PointerId;

            _dicLivePolyLine[pointerId].Points.Add(pointerPoint.Position);
        }

        /// <summary>
        /// 停止此次 ink 的实时绘制
        /// </summary>
        /// <param name="pointerPoint">涂鸦的停止点</param>
        public void ExitLiveRendering(PointerPoint pointerPoint)
        {
            uint pointerId = pointerPoint.PointerId;

            _container.Children.Remove(_dicLivePath[pointerId]);

            _dicLivePolyLine.Remove(pointerId);
            _dicLivePath.Remove(pointerId);
        }

        /// <summary>
        /// 将 InkStroke 绘制成一条三次贝塞尔曲线
        /// </summary>
        /// <param name="inkStroke">InkStroke 对象</param>
        public void UpdateInkRender(InkStroke inkStroke)
        {
            Path bezierPath = InkStroke2BezierPath(inkStroke);
            _dicInkPath.Add(inkStroke, bezierPath);
            _container.Children.Add(bezierPath);
        }

        /// <summary>
        /// 将每一个 InkStroke 都绘制成一条三次贝塞尔曲线
        /// </summary>
        /// <param name="inkStrokes">InkStroke 集合</param>
        public void UpdateInkRender(IEnumerable<InkStroke> inkStrokes)
        {
            foreach (InkStroke inkStroke in inkStrokes)
            {
                UpdateInkRender(inkStroke);
            }
        }

        /// <summary>
        /// 将被选中的 InkStroke 以被选中的样式绘制出来
        /// </summary>
        public void UpdateSelectionRender()
        {
            foreach (var inkPath in _dicInkPath)
            {
                Path selectionPath = null;
                bool selectionPathExists = _dicSelectionPath.TryGetValue(inkPath.Key, out selectionPath);

                // 如果 InkStroke 是被选中的状态,但是并没有呈现出选中状态,则呈现这个选中状态
                if (inkPath.Key.Selected && !selectionPathExists)
                {
                    inkPath.Value.StrokeThickness = inkPath.Key.DrawingAttributes.Size.Width + 2;

                    selectionPath = InkStroke2BezierPath(inkPath.Key);
                    selectionPath.Stroke = new SolidColorBrush(Colors.White);
                    selectionPath.StrokeThickness = inkPath.Key.DrawingAttributes.Size.Width;
                    _dicSelectionPath.Add(inkPath.Key, selectionPath);

                    _container.Children.Add(selectionPath);
                }
                // 如果 InkStroke 是未被选中的状态,但是却呈现出选中状态,则去掉这个选中状态的呈现
                else if (selectionPathExists)
                {
                    inkPath.Value.StrokeThickness = inkPath.Key.DrawingAttributes.Size.Width;
                    _dicSelectionPath.Remove(inkPath.Key);
                    _container.Children.Remove(selectionPath);
                }
            }
        }

        /// <summary>
        /// 清除全部呈现
        /// </summary>
        public void Clear()
        {
            foreach (var path in _dicInkPath.Values)
            {
                _container.Children.Remove(path);
            }
            foreach (var path in _dicSelectionPath.Values)
            {
                _container.Children.Remove(path);
            }
            foreach (var path in _dicLivePath.Values)
            {
                _container.Children.Remove(path);
            }

            _dicInkPath.Clear();
            _dicSelectionPath.Clear();
            _dicLivePath.Clear();
            _dicLivePolyLine.Clear();
        }



        /// <summary>
        /// 如果 InkStroke 是一个贝塞尔曲线画笔,则可将其转换成 Path(一条贝塞尔曲线)
        /// </summary>
        /// <param name="inkStroke">InkStroke 对象</param>
        /// <returns>转换后的 Path 对象</returns>
        public static Path InkStroke2BezierPath(InkStroke inkStroke)
        {
            var figure = new PathFigure();
            var segments = inkStroke.GetRenderingSegments().GetEnumerator();
            segments.MoveNext();

            figure.StartPoint = segments.Current.Position;

            while (segments.MoveNext())
            {
                var bs = new BezierSegment();
                bs.Point1 = segments.Current.BezierControlPoint1;
                bs.Point2 = segments.Current.BezierControlPoint2;
                bs.Point3 = segments.Current.Position;
                figure.Segments.Add(bs);
            }

            var geometry = new PathGeometry();
            geometry.Figures.Add(figure);
            var path = new Path();
            path.Data = geometry;

            path.Stroke = new SolidColorBrush(inkStroke.DrawingAttributes.Color);
            path.StrokeThickness = inkStroke.DrawingAttributes.Size.Width;
            path.StrokeLineJoin = PenLineJoin.Round;
            path.StrokeStartLineCap = PenLineCap.Round;

            return path;
        }
    }
}

Input/Ink/Demo.xaml

<Page
    x:Class="XamlDemo.Input.Ink.Demo"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:XamlDemo.Input.Ink"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="Transparent">
        <StackPanel Margin="120 0 0 0">

            <TextBlock Name="lblMsg" FontSize="14.667" />
            
            <StackPanel Orientation="Horizontal" Margin="0 10 0 0">
                <RadioButton Name="radInk" GroupName="mode" Content="涂鸦模式" Checked="radInk_Checked_1" IsChecked="True" />
                <RadioButton Name="radSelect" GroupName="mode" Content="选择模式" Checked="radSelect_Checked_1" Margin="10 0 0 0" />
                <RadioButton Name="radErase" GroupName="mode" Content="删除模式" Checked="radErase_Checked_1" Margin="10 0 0 0" />
            </StackPanel>

            <StackPanel Orientation="Horizontal" Margin="0 10 0 0">
                <Button Name="btnClearAll" Content="清除全部内容" Click="btnClearAll_Click_1" />
                <Button Name="btnClearSelectedInk" Content="清除被选中的内容" Click="btnClearSelectedInk_Click_1" Margin="10 0 0 0" />
                <Button Name="btnClearSelectedState" Content="清除选中状态" Click="btnClearSelectedState_Click_1" Margin="10 0 0 0" />
                <Button Name="btnMoveSelectedInk" Content="移动被选中的内容" Click="btnMoveSelectedInk_Click_1" Margin="10 0 0 0" />
            </StackPanel>
            
            <StackPanel Orientation="Horizontal" Margin="0 10 0 0">
                <Button Name="btnCut" Content="剪切" Click="btnCut_Click_1" />
                <Button Name="btnCopy" Content="复制" Click="btnCopy_Click_1" Margin="10 0 0 0" />
                <Button Name="btnPaste" Content="粘贴" Click="btnPaste_Click_1" Margin="10 0 0 0" />
            </StackPanel>

            <StackPanel Orientation="Horizontal" Margin="0 10 0 0">
                <Button Name="btnSave" Content="保存到文件" Click="btnSave_Click_1" />
                <Button Name="btnLoad" Content="从文件加载" Click="btnLoad_Click_1" Margin="10 0 0 0" />
                <Button Name="btnRecognize" Content="文字识别" Click="btnRecognize_Click_1" Margin="10 0 0 0" />
            </StackPanel>

            <Canvas x:Name="canvas" Background="Blue" Width="800" Height="360" Margin="0 10 0 0" HorizontalAlignment="Left"
                    PointerPressed="canvas_PointerPressed_1"
                    PointerMoved="canvas_PointerMoved_1"
                    PointerReleased="canvas_PointerReleased_1" />

        </StackPanel>
    </Grid>
</Page>

Input/Ink/Demo.xaml.cs

/*
 * 通过 InkManager 实现一个功能完善的涂鸦板
 * 
 * 注:InkManager 用于 ink 的管理,但并不负责呈现,需要自己开发呈现 ink 的类,请参见 InkRenderer.cs
 * 
 * 
 * InkManager - ink 的管理类
 *     包括 ink 的输入模式,复制/粘贴,移动,保存到文件,从文件加载,文字识别等功能
 *     详细说明请参见本 Demo 中的相关代码的注释
 * 
 * InkStroke - ink 画笔
 *     DrawingAttributes - 画笔的相关属性,一个 InkDrawingAttributes 类型的对象
 *         Color - 颜色
 *         FitToCurve - 使用贝塞尔曲线则为 true,使用直线则为 false,默认值为 true
 *         PenTip - 笔尖的形状(Circle, Rectangle)
 *         Size - 笔尖的尺寸
 *         IgnorePressure - 是否忽略笔尖在触摸屏上的压力
 *     BoundingRect - ink 画笔的边界框
 *     Recognized - 该画笔是否已被文字识别
 *     Selected - 该画笔是否被选中
 *     Clone() - 克隆这个画笔,并返回克隆后的新的 InkStroke 对象
 *     GetRenderingSegments() - 返回 ink 的线段集合,即 InkStrokeRenderingSegment 集合
 *     
 * InkStrokeRenderingSegment - ink 的线段
 *     BezierControlPoint1 - 贝塞尔曲线的第一个控制点的位置
 *     BezierControlPoint2 - 贝塞尔曲线的第二个控制点的位置
 *     Position - 贝塞尔曲线的终点的位置
 */

using System;
using System.Linq;
using System.Collections.Generic;
using Windows.Foundation;
using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.UI;
using Windows.UI.Input;
using Windows.UI.Input.Inking;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using XamlDemo.Common;

namespace XamlDemo.Input.Ink
{
    public sealed partial class Demo : Page
    {
        // 自己开发的用于呈现 ink 的类
        private InkRenderer _renderer = null;

        private InkManager _inkManager = null;
        private InkDrawingAttributes _inkDrawingAttributes = null; // 涂鸦 ink 的样式
        private InkDrawingAttributes _inkDrawingAttributesForSelect = null; // 选择模式时,用于呈现用户的选择路径的 ink 的样式

        private int _pointerId = -1; // 当前指针的 pointerId

        public Demo()
        {
            this.InitializeComponent();

            _inkDrawingAttributes = new InkDrawingAttributes();
            _inkDrawingAttributes.Color = Colors.Orange;
            _inkDrawingAttributes.Size = new Size(5, 5);
            _inkDrawingAttributes.IgnorePressure = true;
            _inkDrawingAttributes.FitToCurve = true;

            _inkDrawingAttributesForSelect = new InkDrawingAttributes();
            _inkDrawingAttributesForSelect.Color = Colors.White;
            _inkDrawingAttributesForSelect.Size = new Size(1, 1);
            _inkDrawingAttributesForSelect.PenTip = PenTipShape.Circle;
           

            _inkManager = new InkManager();
            // InkManager 一共有三种模式:Inking - 涂鸦;Erasing - 擦除;Selecting - 选择
            _inkManager.Mode = InkManipulationMode.Inking;
            // 设置 InkManager 的默认的画笔属性
            _inkManager.SetDefaultDrawingAttributes(_inkDrawingAttributes);
            // 获取 InkManager 所支持的文字识别的种类
            var inkRecognizers = _inkManager.GetRecognizers();
            var inkRecognizer = inkRecognizers.SingleOrDefault(p => p.Name.Equals("Microsoft 中文(简体)手写识别器"));
            if (inkRecognizer != null)
            {
                // 设置 InkManager 的默认的文字识别器为“Microsoft 中文(简体)手写识别器”
                _inkManager.SetDefaultRecognizer(inkRecognizer);
            }

            _renderer = new InkRenderer(canvas);
        }

        private void canvas_PointerPressed_1(object sender, PointerRoutedEventArgs e)
        {
            PointerPoint pointerPoint = e.GetCurrentPoint(canvas);
            _pointerId = (int)pointerPoint.PointerId;

            switch (_inkManager.Mode)
            {
                case InkManipulationMode.Erasing: // 擦除模式
                    break;
                case InkManipulationMode.Inking: // 涂鸦模式
                    _renderer.EnterLiveRendering(pointerPoint, _inkDrawingAttributes);
                    break;
                case InkManipulationMode.Selecting: // 选择模式
                    _renderer.EnterLiveRendering(pointerPoint, _inkDrawingAttributesForSelect);
                    break;
                default:
                    break;
            }

            // 通知 InkManager 指针已按下
            _inkManager.ProcessPointerDown(pointerPoint);
        }

        private void canvas_PointerMoved_1(object sender, PointerRoutedEventArgs e)
        {
            PointerPoint pointerPoint = e.GetCurrentPoint(canvas);

            if (_pointerId == (int)pointerPoint.PointerId)
            {
                switch (_inkManager.Mode)
                {
                    case InkManipulationMode.Erasing:
                        // 通知 InkManager 指针移动中,然后会返回一个 Rect 对象,其代表被 Erasing 的矩形范围
                        var erasingRect = (Rect)_inkManager.ProcessPointerUpdate(e.GetCurrentPoint(canvas));
                        if (erasingRect.Height != 0 && erasingRect.Width != 0)
                        {
                            _renderer.Clear();
                            // 通过 InkManager.GetStrokes() 获取 InkManager 包含的全部 InkStroke 对象集合
                            _renderer.UpdateInkRender(_inkManager.GetStrokes());
                        }
                        break;
                    case InkManipulationMode.Inking:
                    case InkManipulationMode.Selecting:
                        if (_inkManager.Mode == InkManipulationMode.Inking || _inkManager.Mode == InkManipulationMode.Selecting)
                        {
                            var intermediatePoints = e.GetIntermediatePoints(canvas);
                            for (int i = intermediatePoints.Count - 1; i >= 0; i--)
                            {
                                // 通知 InkManager 指针移动中
                                _inkManager.ProcessPointerUpdate(intermediatePoints[i]);
                            }

                            _renderer.UpdateLiveRender(pointerPoint);
                        }
                        break;
                    default:
                        break;
                }
            }
        }

        private void canvas_PointerReleased_1(object sender, PointerRoutedEventArgs e)
        {
            PointerPoint pointerPoint = e.GetCurrentPoint(canvas);

            if (_pointerId == (int)pointerPoint.PointerId)
            {
                _pointerId = -1;

                // 通知 InkManager 指针释放了,返回值:当 Inking 或 Selecting 时返回值为用户此次操作的矩形范围;当 Erasing 时返回值为 0,0,0,0
                var rect = _inkManager.ProcessPointerUp(pointerPoint);
                // var inkManagerRect = _inkManager.BoundingRect; // InkManager 所管理 InkStroke 集合的矩形范围(矩形边框)

                switch (_inkManager.Mode)
                {
                    case InkManipulationMode.Inking:
                        _renderer.ExitLiveRendering(pointerPoint);
                        _renderer.UpdateInkRender(_inkManager.GetStrokes()[_inkManager.GetStrokes().Count - 1]);
                        break;
                    case InkManipulationMode.Selecting:
                        _renderer.ExitLiveRendering(pointerPoint);
                        _renderer.UpdateSelectionRender();
                        break;
                    default:
                        break;
                }
            }
        }

        private async void btnSave_Click_1(object sender, RoutedEventArgs e)
        {
            if (_inkManager.GetStrokes().Count > 0)
            {
                if (Helper.EnsureUnsnapped())
                {
                    var savePicker = new FileSavePicker();
                    savePicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
                    savePicker.FileTypeChoices.Add("GIF", new List<string> { ".gif" });

                    StorageFile file = await savePicker.PickSaveFileAsync();
                    if (null != file)
                    {
                        using (var stream = await file.OpenAsync(FileAccessMode.ReadWrite))
                        {
                            // 将 InkManager 中的 InkStroke 对象集合保存为文件
                            await _inkManager.SaveAsync(stream);
                        }
                    }
                }
            }
        }

        private async void btnLoad_Click_1(object sender, RoutedEventArgs e)
        {
            if (Helper.EnsureUnsnapped())
            {
                var openPicker = new Windows.Storage.Pickers.FileOpenPicker();
                openPicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary;
                openPicker.FileTypeFilter.Add(".gif");
                StorageFile file = await openPicker.PickSingleFileAsync();
                if (null != file)
                {
                    using (var stream = await file.OpenSequentialReadAsync())
                    {
                        // 加载一个文件,从中获取 InkStroke 对象集合,并将其交由 InkManager 管理
                        await _inkManager.LoadAsync(stream);
                    }

                    _renderer.Clear();
                    _renderer.UpdateInkRender(_inkManager.GetStrokes());
                }
            }
        }

        private void radInk_Checked_1(object sender, RoutedEventArgs e)
        {
            if (_inkManager != null)
                _inkManager.Mode = InkManipulationMode.Inking; // 涂鸦模式
        }

        private void radSelect_Checked_1(object sender, RoutedEventArgs e)
        {
            if (_inkManager != null)
                _inkManager.Mode = InkManipulationMode.Selecting; // 选择模式
        }

        private void radErase_Checked_1(object sender, RoutedEventArgs e)
        {
            if (_inkManager != null)
                _inkManager.Mode = InkManipulationMode.Erasing; // 擦除模式
        }

        private void btnCut_Click_1(object sender, RoutedEventArgs e)
        {
            // 将 InkManager 中的被选中的 InkStroke 对象集合复制到剪切板
            _inkManager.CopySelectedToClipboard();
            // 将 InkManager 中的被选中的 InkStroke 对象集合删除掉
            _inkManager.DeleteSelected();

            _renderer.Clear();
            _renderer.UpdateInkRender(_inkManager.GetStrokes());
        }

        private void btnCopy_Click_1(object sender, RoutedEventArgs e)
        {
            // 将 InkManager 中的被选中的 InkStroke 对象集合复制到剪切板
            _inkManager.CopySelectedToClipboard();
        }

        private void btnPaste_Click_1(object sender, RoutedEventArgs e)
        {
            // 剪切板中的数据是否是 InkStroke 对象集合
            if (_inkManager.CanPasteFromClipboard())
            {
                // 将剪切板中的 InkStroke 对象集合粘贴到指定位置,并将此 InkStroke 对象集合交由 InkManager 管理
                _inkManager.PasteFromClipboard(new Point(10, 10));

                _renderer.Clear();
                _renderer.UpdateInkRender(_inkManager.GetStrokes());
                _renderer.UpdateSelectionRender();
            }
        }

        private void btnClearAll_Click_1(object sender, RoutedEventArgs e)
        {
            // 通过 InkManager.GetStrokes() 获取 InkManager 包含的全部 InkStroke 对象集合
            foreach (var inkStroke in _inkManager.GetStrokes())
            {
                // InkStroke 是否被选中
                inkStroke.Selected = true;
            }
            // 将 InkManager 中的被选中的 InkStroke 对象集合删除掉
            _inkManager.DeleteSelected();

            _renderer.Clear();
        }

        private void btnClearSelectedInk_Click_1(object sender, RoutedEventArgs e)
        {
            // 将 InkManager 中的被选中的 InkStroke 对象集合删除掉
            _inkManager.DeleteSelected();

            _renderer.Clear();
            _renderer.UpdateInkRender(_inkManager.GetStrokes());
        }

        private void btnClearSelectedState_Click_1(object sender, RoutedEventArgs e)
        {
            foreach (var inkStroke in _inkManager.GetStrokes())
            {
                // InkStroke 是否被选中
                inkStroke.Selected = false;
            }

            _renderer.Clear();
            _renderer.UpdateInkRender(_inkManager.GetStrokes());
        }

        private void btnMoveSelectedInk_Click_1(object sender, RoutedEventArgs e)
        {
            // 将 InkManager 中的被选中的 InkStroke 对象集合移动指定的距离(本例为向下移动 5 个像素,向右移动 50 个像素)
            _inkManager.MoveSelected(new Point(50, 5));

            _renderer.Clear();
            _renderer.UpdateInkRender(_inkManager.GetStrokes());
            _renderer.UpdateSelectionRender();
        }

        private async void btnRecognize_Click_1(object sender, RoutedEventArgs e)
        {
            // 对 InkManager 中的内容做文字识别
            //     InkRecognitionTarget.All - 识别全部; 
            //     InkRecognitionTarget.Selected - 识别选中的 InkStroke 集合; 
            //     InkRecognitionTarget.Recent - 识别最近的 InkStroke
            var recognitionResults = await _inkManager.RecognizeAsync(InkRecognitionTarget.All);
            // 将文字识别的结果更新到 InkManager
            _inkManager.UpdateRecognitionResults(recognitionResults);
            // _inkManager.GetRecognitionResults(); 获取 InkManager 中的文字识别的结果

            string result = "文字识别的结果:";
            foreach (InkRecognitionResult recognitionResult in recognitionResults)
            {
                // 获取此文字识别的所有可能的结果集合,集合的第一条数据为最匹配的结果
                result += " " + recognitionResult.GetTextCandidates()[0];
            }

            lblMsg.Text = result;
        }
    }
}



OK
[源码下载]

目录
相关文章
|
2月前
|
Linux C++ Windows
【Azure 应用服务】Azure App Service(Windows)环境中如何让.NET应用调用SAP NetWeaver RFC函数
【Azure 应用服务】Azure App Service(Windows)环境中如何让.NET应用调用SAP NetWeaver RFC函数
【Azure 应用服务】Azure App Service(Windows)环境中如何让.NET应用调用SAP NetWeaver RFC函数
|
2月前
|
Java 应用服务中间件 开发工具
[App Service for Windows]通过 KUDU 查看 Tomcat 配置信息
[App Service for Windows]通过 KUDU 查看 Tomcat 配置信息
|
2月前
|
Java 应用服务中间件 Windows
【App Service for Windows】为 App Service 配置自定义 Tomcat 环境
【App Service for Windows】为 App Service 配置自定义 Tomcat 环境
|
2月前
|
PHP Windows
【Azure App Service for Windows】 PHP应用出现500 : The page cannot be displayed because an internal server error has occurred. 错误
【Azure App Service for Windows】 PHP应用出现500 : The page cannot be displayed because an internal server error has occurred. 错误
|
2月前
|
PHP 开发工具 git
【Azure 应用服务】在 App Service for Windows 中自定义 PHP 版本的方法
【Azure 应用服务】在 App Service for Windows 中自定义 PHP 版本的方法
|
2月前
|
网络安全 API 数据安全/隐私保护
【Azure App Service】.NET代码实验App Service应用中获取TLS/SSL 证书 (App Service Windows)
【Azure App Service】.NET代码实验App Service应用中获取TLS/SSL 证书 (App Service Windows)
|
2月前
|
Shell PHP Windows
【Azure App Service】Web Job 报错 UNC paths are not supported. Defaulting to Windows directory.
【Azure App Service】Web Job 报错 UNC paths are not supported. Defaulting to Windows directory.
|
2月前
|
存储 Linux Windows
【应用服务 App Service】App Service For Windows 如何挂载Storage Account File Share 示例
【应用服务 App Service】App Service For Windows 如何挂载Storage Account File Share 示例
|
2月前
|
应用服务中间件 nginx Windows
【Azure 应用服务】在App Service for Windows中实现反向代理
【Azure 应用服务】在App Service for Windows中实现反向代理
|
2月前
|
网络协议 Linux 网络安全
【Azure 应用服务】更便捷的方式抓取Azure App Service for Windows的网络包
【Azure 应用服务】更便捷的方式抓取Azure App Service for Windows的网络包