初探迷雾洞窟,InkPresenter 初探

简介:
InkPresenter 是 System.Windows.Controls 命名空间下的一个类,其主要是 实现 在界面上显示一个可以显示墨迹笔画的矩形图画。
InkPresenter 是派生自Canvas 他可以显示一个或多个UI元素和 StrokeCollection.
InkPresenter 有两个重要属性,一个是Background可以设置图画的背景,一个是Children ,Children中的值将被呈现在InkPresenter图画上。
使用InkPresenter可以实现像绘图板的效果,非常好玩,我简单实现了一个写字板的简单功能。
功能如下:在界面上显示出一个画布,可以使用钢笔、标记笔、荧光笔在上面 写字画画什么的,
而且还有两个橡皮擦 一个是点橡皮擦,一个是线条橡皮擦。效果就如下图这样:
InkPresenter 初探初探迷雾洞窟
要实现上面的功能首先要简单了解下面几个类:
StylusPoint:表示在用户使用触笔或鼠标绘制墨迹笔画时收集的单个点。
StylusPointCollection:表示StylusPoint的集合。
Stroke:StylusPointCollection连续的点就可以表示为表示单个墨迹笔画,就是Stroke。
StrokeCollection:表示一组Stroke。
下面就是实现过程:
1、首先绘制界面,最重要的是标记,其他还有一些按钮和Textbox来触发事件、显示信息,xaml如下:
InkPresenter 初探初探迷雾洞窟InkPresenter 初探初探迷雾洞窟View Code
2、实现在界面上绘制线条,
基本过程是,当鼠标左键按下的时候 记录下墨迹的点,并使用这些点 初始化一条笔画,然后将笔画绘制到InkPresenter 上,
当鼠标移动的时候,将所有经过的点加入到上面的笔画里。
当失去鼠标捕获的时候,将笔画对像清空,等待下一次鼠标按下。
如下:
鼠标按下事件 MouseLeftButtonDown:
View Code private void myInkPresenter_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { myInkPresenter.CaptureMouse(); StylusPointCollection stylusPointCollection = new StylusPointCollection(); stylusPointCollection.Add(e.StylusDevice.GetStylusPoints(myInkPresenter)); newStroke = new Stroke(stylusPointCollection); //newStroke.DrawingAttributes = myDrawingAttributes; //这里要注意不能使用上面的方法直接将DrawingAttributes赋给Stroke这样会使整个InkPresenter上的线都变成该样式 //我 在网上查到有一个DrawingAttributesChanged事件,应该是这个事件产生的影响 //所以使用下面的 复制的方法就可以了 newStroke.DrawingAttributes =CloneDrawingAttributes(myDrawingAttributes); //开始想的 当使用点橡皮的时候 直接在InkPresenter 上涂上背景色不就像擦除了吗,像下面这样 //单上当你使用点橡皮 将一条线切开的时候 就会发现 再使用线橡皮擦的时候会出现问题的,所以下面是不合适的 //应该使用将线条切开的方式 //if (flag == "PointErase") //{ // newStroke.DrawingAttributes.Color = Color.FromArgb(255,255,255, 255); // newStroke.DrawingAttributes.OutlineColor = Color.FromArgb(255, 255, 255, 255); //} myInkPresenter.Strokes.Add(newStroke); }
上面的事件处理函数中有 Stroke对象有一个DrawingAttributes属性,该属性是对Stroke对象外观的描述,需要注意。
MouseMove事件中,将经过的点加到笔画中,并且使用StylusPoint的X、Y、PressureFactor获取操作的一些信息:
View Code private void myInkPresenter_MouseMove(object sender, MouseEventArgs e) { if (newStroke != null) { newStroke.StylusPoints.Add(e.StylusDevice.GetStylusPoints(myInkPresenter)); stylusPointsInfo.Text ="X坐标: "+ e.StylusDevice.GetStylusPoints(myInkPresenter)[0].X.ToString() + " Y坐标: "+e.StylusDevice.GetStylusPoints(myInkPresenter)[0].Y.ToString() +" 力度: "+e.StylusDevice.GetStylusPoints(myInkPresenter)[0].PressureFactor.ToString(); } }
LostMouseCapture事件将 全局的笔画对象清空就行了。
这样就实现了线条的绘制了。
3、实现画笔的切换
画笔的切换主要就是修改Stroke的DrawingAttributes属性就可以了,我这里定义了三个按钮,单击时候修改他:
View Code private void penButton_Click(object sender, RoutedEventArgs e) { myDrawingAttributes.Width = 1; myDrawingAttributes.Height = Double.Epsilon - 1; myDrawingAttributes.Color = Colors.Black; myDrawingAttributes.OutlineColor = Colors.Black; this.myInkPresenter.Cursor = Cursors.Stylus; } private void markerButton_Click(object sender, RoutedEventArgs e) { myDrawingAttributes.Width = 10; myDrawingAttributes.Height = 4; myDrawingAttributes.Color = Colors.Blue; myDrawingAttributes.OutlineColor = Colors.Blue; this.myInkPresenter.Cursor = Cursors.Stylus; } private void highlighterButton_Click(object sender, RoutedEventArgs e) { myDrawingAttributes.Width = 25; myDrawingAttributes.Height = 5; myDrawingAttributes.Color = Colors.Yellow; myDrawingAttributes.OutlineColor = Colors.Yellow; this.myInkPresenter.Cursor = Cursors.Stylus; }
4、橡皮擦功能
这个比较复杂 我设置了三种情况,为了区分画笔,我定义了一个 flag 用来标记是否是橡皮擦
第一个是全部清空,只需将InkPresenter的Strokes Clear以下就Ok了
第二个是线条橡皮擦,主要原理是:在橡皮状态下当鼠标在InkPresenter中移动的时候如果与已经存在的笔画相交就将该笔画删除 判断是否相交是通过HitTest方法实现的,比较简单。在LeftMouseDown的时候 初始化一个StylusPointCollection对象erasePoints,在鼠标移动的时候记录下所有经过的点 并与已存在笔画进行HitTest 将相交的画线删除。
View Code erasePoints.Add(e.StylusDevice.GetStylusPoints(myInkPresenter)); StrokeCollection hitStrokes = myInkPresenter.Strokes.HitTest(erasePoints); if (hitStrokes.Count > 0) { foreach (Stroke hitStroke in hitStrokes) { myInkPresenter.Strokes.Remove(hitStroke); } }
第三个是点橡皮擦,这个是最复杂的一个了,初我想的是橡皮擦擦的时候 将移动的轨迹重新用背景色画下不就行了?
试了才知道这种方法可以,但是不太好,因为你再次选择笔画橡皮擦的时候即使线条已经被切断,还会将整条线擦除,因为整条线条根本就没改变。 基于此可以使用 当由某点经过画线时候将改点删除,并将改线切成两段,这样即实现了功能也不会出现上面的问题了。
过程是:在鼠标按下的时候,这里需要注意的是在鼠标按下的这个点也可能需要处理,因为可能正好按在了某条线上,所以可以先将这个点保存起来,让他也让后面的处理过程处理;在鼠标移动的时候不断收集这些经过的点,并进行HitTest检测,若两条线相交了,就将这条线段从改点截成两段显示。这里定义了一个ProcessPointErase方法,该方法线从被截这条线的起始点开始直到与橡皮相交的点 构成一条线,然后从被截这条线的末尾开始到与橡皮相交的点 构成一条线,然后将原来的线移除,将这两条线绘制到界面上,ProcessPointErase方法 如下:
View Code private void ProcessPointErase(Stroke stroke, StylusPointCollection pointErasePoints) { Stroke splitStroke1, splitStroke2, hitTestStroke; splitStroke1 = new Stroke(); hitTestStroke = new Stroke(); hitTestStroke.StylusPoints.Add(stroke.StylusPoints); hitTestStroke.DrawingAttributes = stroke.DrawingAttributes; //通过以 stroke 上的第一个点为起始,并且将 stroke 上的每个触笔接触点添加到 splitStroke1, //直到到达与 pointErasePoints 相交的触笔接触点,确定相交。 while (true) { StylusPoint sp=hitTestStroke.StylusPoints[0]; hitTestStroke.StylusPoints.RemoveAt(0); if (!hitTestStroke.HitTest(pointErasePoints)) { break; } splitStroke1.StylusPoints.Add(sp); } //通过以 stroke 上的最后一个点为起始,并且将每个触笔接触点添加到 splitStroke2, //直到到达与输入 pointErasePoints 相交的触笔接触点 splitStroke2 = new Stroke(); hitTestStroke = new Stroke(); hitTestStroke.StylusPoints.Add(stroke.StylusPoints); hitTestStroke.DrawingAttributes = stroke.DrawingAttributes; while (true) { StylusPoint sp = hitTestStroke.StylusPoints[hitTestStroke.StylusPoints.Count-1]; hitTestStroke.StylusPoints.RemoveAt(hitTestStroke.StylusPoints.Count - 1); if (!hitTestStroke.HitTest(pointErasePoints)) { break; } splitStroke2.StylusPoints.Insert(0,sp); } //将原有的线删除,将拆分的两条线 绘制到屏幕 if (splitStroke1.StylusPoints.Count > 1) { splitStroke1.DrawingAttributes = stroke.DrawingAttributes; myInkPresenter.Strokes.Add(splitStroke1); } if (splitStroke2.StylusPoints.Count > 1) { splitStroke2.DrawingAttributes = stroke.DrawingAttributes; myInkPresenter.Strokes.Add(splitStroke2); } myInkPresenter.Strokes.Remove(stroke); }
程序代码在最下面。
不明白的话可以去MSDN上看看 http://msdn.microsoft.com/zh-cn/library/dd233088(v=VS.95).aspx 我就是参考着做的。
这样整个程序基本上就好了,然后就可以写写画画了,当然了放在Windows Phone上也很好玩,几乎不用改代码,看下图:
InkPresenter 初探初探迷雾洞窟
程序代码:InkPresenter.rar
Tags:  梦幻西游初探建邺 传统语文教育初探 干部学研究初探 qq三国初探景门 初探迷雾洞窟

延伸阅读

最新评论

发表评论