基于InkCanvas实现的桌面涂鸦工具-[ WPF开发 ]
首先简单的介绍下InkCanvas,简单的来说,InkCanvas就是在WPF中实现允许使用墨迹的布局控件。实际上,InkCanvas有着更多层面上的应用,它的主要目的是(通过鼠标或者和指示笔)捕捉笔迹。InkCanvas从技术上说不是一个控件,因为它直接从FrameworkElement继承而来,但是它的行为和控件非常像(但不能用一个新的模板来改变它的样式)。
默认模式下,InkCanvas允许在它的表面上进行简单的书写和画图。当使用指示笔时,笔尖用来写、笔端用来擦。每一个笔画被捕捉为一个System.Windows.Ink.Stroke对象,保存在InkCanvas的Strokes集合中。但是InkCanvas也支持在Children集合(一个内容属性)中保留任意数量的UIElement元素。这样很容易通过墨水(ink)来注释任何东西。如下面的代码,我们可以很容易的生成一个InkCanvas画板。
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="InkCanvasSample" Width="220" Height="87">
<InkCanvas>
<InkCanvas.DefaultDrawingAttributes>
<DrawingAttributes Color="Red" />
</InkCanvas.DefaultDrawingAttributes>
<Image Source="E:\Cnblogs\wwf.png"/>
</InkCanvas>
</Window>
InkCanvas 支持几种模式,它们能利用EditingMode属性被独立地应用到指示笔尖(或鼠标),并通过EditingModeInverted属性来应用于指示笔的末端(back end)。只读的ActiveEditingMode属性可以告诉你哪一个属性当前正在被使用。所有这3个属性都是InkCanvasEditingMode类型的,它有以下几种值:
1、Ink(EditingMode的默认值) —— 通过鼠标或者指示笔来绘制笔画。
2、InkAndGesture —— 和Ink一样,但同样可以识别用户的手势。手势的列表(Up、Down、Circle、ScratchOut和Tap)保存在System.Windows.Ink.ApplicationGesture枚举类型中。
3、GestureOnly —— 只识别手势,不会绘制用户输入的笔画。
4、EraseByStroke (EditingModeInverted的默认值)—— 当笔画被触及时将笔画擦掉。
5、EraseByPoint —— 只擦掉直接碰及到的笔画部分(就像传统的铅笔橡皮)。
6、Select —— 当被触及时,选择笔画或者任何UIElement,使它们能被删除、移动或者在InkCanvas范围内被调整尺寸。
7、None —— 对于鼠标或者指示笔不做任何响应。
一些普通元素与墨水没有任何关系,如果在这些元素上使用Select模式将非常有趣,因为它自动会提供一个“穷人”的运行时设计界面用来排列控件。InkCanvas还定义了15种事件,其中包括改变编辑模式、改变/移动/调整选择、收集或者擦除笔画,以及执行手势。当然,在应用程序中使用墨水比在人脸上画胡子还是要复杂些!你经常要对一个笔画集合做手写识别,如果输入的是字符你就可以分析出它。WPF拥有内建的手势识别功能,但没有手写识别引擎。
概述
程序截图如下:
大概的一个思路就是主要有两个操作窗口,一个是主窗口,可以进行新建涂鸦、调整画笔彩色、画笔形状等等,另外一个就是涂鸦的画板了。首先先截图,然后设置Inkcanvas的Background属性为该图片,然后就可以在上面进行画图了。主要要处理的细节问题在于:
- 截图的问题
- 取得窗口坐标的问题
- 窗口之间传值的问题
代码解释
首先是截图的问题。查MSDN,我找不到WPF里有现在的API可以调用,只能通过winForm来实现截图,代码如下:02 {
03 System.Drawing.Rectangle rc = SystemInformation.WorkingArea;
04 var bitmap = new Bitmap(rc.Width, rc.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
05
06 using (Graphics g = Graphics.FromImage(bitmap))
07 {
08 g.CopyFromScreen(rc.X, rc.Y, 0, 0, rc.Size, CopyPixelOperation.SourceCopy);
09 }
10
11 return bitmap;
12 }
13
14 public BitmapSource ToBitmapSource(Bitmap bmp)
15 {
16 BitmapSource returnSource;
17
18 try
19 {
20 returnSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bmp.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
21 }
22 catch
23 {
24 returnSource = null;
25 }
26 return returnSource;
27
28 }
29 //截取整个屏幕作为画布,并开始画画
30 private void pencil_Click(object sender, RoutedEventArgs e)
31 {
32 //初始化画布
33 PainterWindow pw = new PainterWindow();
34 System.Drawing.Rectangle rc = SystemInformation.WorkingArea;
35 pw.Width = rc.Width;
36 pw.Height = rc.Height;
37 pw.ink.Width = rc.Width;
38 pw.ink.Height = rc.Height;
39 //截图
40 this.Hide();
41 Bitmap bt = GetScreenSnapshot();
42 BitmapSource bs = ToBitmapSource(bt);
43
44 System.Windows.Controls.Image img = new System.Windows.Controls.Image();
45 img.Source = bs;
46 pw.ink.Background = new ImageBrush(bs);
47 pw.Show();
48 this.Show();
49
50 }
看到上面的代码,通过System.Drawing.Rectangle rc = SystemInformation.WorkingArea;来获取当前系统工作窗口(不包括任务栏)的信息,包括分辨率的大小,各个点的坐标信息,获取这些信息后,新建一个bitmap,通过winForm里的CopyFromScreen函数我们把屏幕绘画在新建的那个bitmap上,然后获得bitmap的地址设置为BitmapSource,并设置ink的Background为ImageBrush(bs)即可。
下面是响应颜色选择的代码:
02 {
03 ColorDialog cd = new ColorDialog();
04 //取得颜色
05 if (cd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
06 {
07 //要去了解这两种颜色的不同之处
08 inkDA.Color = System.Windows.Media.Color.FromArgb(cd.Color.A, cd.Color.R, cd.Color.G, cd.Color.B);
09 inkDA.Height = 3;
10 inkDA.Width = 3;
11 inkDA.FitToCurve = true;
12 //修改颜色
13 foreach (Window win in System.Windows.Application.Current.Windows)
14 {
15 if (win.Title == "PainterWindow")
16 {
17 PainterWindow pw = win as PainterWindow;
18 pw.ink.DefaultDrawingAttributes = inkDA;
19 pw.Show();
20 }
21 }
22
23 }
24
25
26 }
要注意的是System.Widows.Media.Color与InkCanvas定义的颜色的不同,这中间需要通过一个函数来转换。
其实想着能把它完善好的,像形状的自定义,界面的细节调整,等。最近有太多的事情要做,所以先搁置下来,待时间过后再回来看自己的代码,熟悉下。
相关资料
一些关于InkCancas的资料
@:卿之 → http://www.cnblogs.com/wpdev
©:博文是本人当时的学习笔记及知识整理,由于自身局限错误在所难免,敬请斧正.
©:本文版权属于博客园和本人,版权基于署名 2.5 中国大陆许可协议发布,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接和署名卿之(包含链接),不得删节,否则保留追究法律责任的权利。

浙公网安备 33010602011771号