GVKun编程网logo

WPF/Silverlight为什么要使用Canvas.SetLeft()这样的方法?(wpf和silverlight)

12

想了解WPF/Silverlight为什么要使用Canvas.SetLeft()这样的方法?的新动态吗?本文将为您提供详细的信息,我们还将为您解答关于wpf和silverlight的相关问题,此外,我

想了解WPF/Silverlight为什么要使用Canvas.SetLeft()这样的方法?的新动态吗?本文将为您提供详细的信息,我们还将为您解答关于wpf和silverlight的相关问题,此外,我们还将为您介绍关于.net – 为什么我需要MVVM用于WPF / Silverlight,有什么理由不能使用MVC吗?、ArcGIS API for Silverlight开发入门(0):为什么要用Silverlight API?、C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(三十九)向Silverlight移植①、My Silverlight系列(10)—— Silverlight中的InkCanvas的新知识。

本文目录一览:

WPF/Silverlight为什么要使用Canvas.SetLeft()这样的方法?(wpf和silverlight)

WPF/Silverlight为什么要使用Canvas.SetLeft()这样的方法?(wpf和silverlight)

http://www.cnblogs.com/yayx/archive/2008/04/23/1167110.html

看见园子里有兄弟在讨论到关于WPF/SL中的属性的问题,比如:

“为啥flex可以直接对控件的left,top这些属性直接操作,sl还要转一下。这个很难理解,难道是因为dp的好用?相对dp的使用flex的实现简单明了的多了”

正好我最近在研究WPF,在这儿说说我的看法。可能大家大多就是在用WPF或者SL就是做界面,因此首先比较关心界面布局的问题,我就先以WPF的layout为例解释一些用法。

WPF中的布局对象

WPF中不可以给每个界面元素控件都加上"Left"或者"Top"这样的属性吗?其实不是,在一般的布局时,WPF为每个继承自FrameworkElement的组件提供了Margin这个属性,通过这个属性,我们可以轻松的设置四周的边距。再加上HorizontalAlignment和VerticalAlignment属性,我们足以完成一般的元素定位。最常用的,我们在Windows中随便拖一个Button,就会看到生成类似这样的XAML代码:

< Button  Name ="button1"   Margin ="0,28,7"  HorizontalAlignment ="Right"  Width ="53" > button </ Button >

使用Margin属性,渲染时的控件具体位置是如何决定的?我们可以说是“控件自己决定的”,或者说是“WPF统一决定的”。

除此之外,WPF还提供了另一种布局方式,也就是利用布局对象进行布局,这种布局方式最大的特点是,由父控件统一对子对象的布局进行管理。这样的控件有"Border"(单个子控件),"StackPanel"(流布局),"DockPanel"(停靠布局等),还有最常用的"Gird"。相信大家用这些布局控件已经很熟悉了。我们来看一些示例代码,看看WPF是怎样实现"由父控件统一对子对象的布局进行管理"的。以Canvas作为例子

class  Canvas:Panel

{

    
//定义DependencyProperty

    
//public static readonly DependencyProperty LeftProperty = 


    
//public static readonly DependencyProperty TopProperty = 

 


    
protected override Size MeasureOverride(Size availableSize)

    
{

        
//这里计算所有子控件自身的期望尺寸大小

        foreach (UIElement ele in InternalChildren)

        
{

            ele.Measure(availableSize);

        }
            

         
//这里返回自己(父控件)的测量大小,可用多大就多返回多大,并没有根据子控件变化

        return availableSize;

    }

    
protected override Size ArrangeOverride(Size finalSize)

    
{

        
//这里计算每个子控件的位置,也就是对子控件进行布局

        foreach (UIElement ele in InternalChildren)

        
{

            
//这个Rect用来指定子控件相对自己的位置

            Rect itsRect = new Rect(

                
new Point((double)ele.GetValue(LeftProperty), (double)ele.GetValue(TopProperty)), //*这句是关键

                ele.DesiredSize,

                ele.DesiredSize

                );

            ele.Arrange(itsRect);

        }
     

        
return finalSize

    }

}
 


详细的布局过程可以参考一些书籍(强烈推荐很经典的《Application = Code + Markup:WPF程序设计指南》),这里不多说,注意ArrangeOverride方法中的new Point((double)ele.GetValue(LeftProperty),(double)ele.GetValue(TopProperty)),这一句使用GetValue方法获得了每个子控件的Left和Top属性,然后据此对子控件进行定位。

我不熟悉Flex,就和以前其他布局方式对比一下,比如Winform,Winform时代有个老大帮我们布局。老大说了,我们每个能参与布局的对象都有Left,Top,或者还有Position来指定Relative还是Absolute这样(我确实想到css去了),现在不一样了,因为“权力下放”,WPF把布局的权利直接下放给了布局控件,这就麻烦了,Canvas需要的是Left属性,Top属性,Grid可能需要Row属性,Column属性,DockPanel可能需要Dock属性,那怎么办呢?于是我们就使用Attached Property,把属性定义在各个“小老大”下面,下面参与布局的“小兄弟”就使用Attached Property的方式来设置和读取这些属性的值。

也许你问这种“权力下放”有什么好处,如果你从WPF自带的一些布局控件还体会不到好处的话,我推荐一个链接,大家可以看看各式各样的炫酷的自定义Layout:

Why WPF Rocks (Custom Layout Panel Showcase)

其实,如果你坚定的想为每个“小兄弟”建立一个Left和Top的话,你完全可以自己实现类似如下的代码:

class  MyFrameworkElement : FrameworkElement

{

    
public int Left get; }

    
public int Top get; }

    
//


}
 


class  MyCanvas : Canvas

{

    
protected override Size ArrangeOverride(Size finalSize)

    
{

        
foreach (UIElement ele in InternalChildren)

        
{

            MyFrameworkElement mele 
= ele as MyFrameworkElement;

            
if (mele != null)

            
{

                Rect itsRect 
= new Rect(

                    
new Point(mele.Left, mele.Top),

                    ele.DesiredSize,

                    ele.DesiredSize

                    );

                ele.Arrange(itsRect);

            }

        }

        
return base.ArrangeOverride(finalSize);

    }

}
 


不知你感觉如何,反正我不喜欢这样的实现。MyFrameworkElement和MyCanvas被强耦合在一起了,我不知道DockPanel控件是否还需要Left和Top属性,但是这两个属性确实存在,难道我需要定义MyFrameworkElementForCanvas,MyFrameworkElementForDockPanel...?太恐怖了

不过插一句,WPF中并没有所谓的特定的“布局对象”,其实每一个视觉对象(Visual)都可以为自己的子对象(们)安排布局。

形成模式

抛开布局不谈,从设计模式和面向对象的设计来说,这种设置属性值的方式可以算一种很有意义的模式,他很漂亮的分离了属性(Left),拥有属性的对象(Element),定义属性的对象(Canvas),属性值四者之间的耦合关系。DependencyProperty和AttachedProperty不仅在layout system中有很大的用处,在数据绑定,动画支持,资源,风格等都有很好的应用。

当然,任何复杂的设计都是有代价的,这种方式最大的代价也许是性能,虽然在值得存储上WPF进行了很多优化(可以参考我的另一篇即将发布的Post),但性能损失肯定存在。另一方面,复杂的设计必定会增加用户(我们程序员)使用的复杂度,也就是我们必须使用GetValue或者是SetValue方法去获取值了。不过在使用复杂度上面,WPF进行了一些官方的规范处理,他希望普通的DependencyProperty都能被封装成名为"CLR Wapper"的get/set方法,AttachedProperty封装成类似Canvas.SetLeft(v,150) 这样的语法,于是,便出现了Canvas.SetLeft()这样的方法。。

另外Silverlight本身还只是Beta版本,所以最好别拿Silverlight说事儿。呵呵,我还是很喜欢WPF。

总结

这一篇Post首先从WPF的布局系统说起,介绍了布局系统中是如何应用AttachedProperty的,然后总结的阐述了为什么会出现Canvas.SetLeft()这样的语法。 以上是我个人的一些看法,如果有什么不对希望大家多多指正~

.net – 为什么我需要MVVM用于WPF / Silverlight,有什么理由不能使用MVC吗?

.net – 为什么我需要MVVM用于WPF / Silverlight,有什么理由不能使用MVC吗?

也许我疯了,但我得到了MVC.这说得通. MVVM似乎是一种让MVC变得更难的方法,我错了(如果是这样,为什么?)?

解决方法

我认为如果你获得MVC,那么MVVM实际上是相同的 – 增加了一个部分(viewmodel).在我看来,MVVM更有用的原因与新的DataBinding流程有关. viewmodel提供了有关数据绑定的有用抽象和清晰度,以及如何呈现或转换绑定.这就是我的想法.

MVC中缺少的部分实际上是MVVM模式,在我看来它只是MVVM的隐藏部分.如果你看一下大多数MVVM框架,大多数提供某种消息或通知系统,它真正提供了MVC缺少的控制器方面.

可以将viewmodel视为View的代码隐藏,View作为View的Designer,Model作为模型,Notification / Messaging系统作为Controller,我们有一个真正等效的模式.

这对我来说也很难理解(我也来自MVC模式,但现在喜欢MVVM模式).

ArcGIS API for Silverlight开发入门(0):为什么要用Silverlight API?

ArcGIS API for Silverlight开发入门(0):为什么要用Silverlight API?

你用上3G手机了吗?你可能会说,我就是喜欢用nokia1100,ABCDEFG跟我都没关系。但你不能否认3G是一种趋势,最终我们每个人都会被包裹在3G网络中。1100也不是一成不变,没准哪天为了打击犯罪,会在你的1100上强制装上GPS。GIS工作既然建立在计算机的基础上,当然也得随着IT行业与时俱进。
       看看现在计算机应用的趋势吧。云(计算),这个东西可讲不清楚,因为云嘛,飘忽不定的。不过可以这样来看它,以后计算机网络上就有一坨(或者几坨)万能的云,有什么需求云都可以满足我们,包括各种资源或者计算工作,就不需要在自己的机器上安装任何软件了(甚至操作系统都可以由天边那朵云来提供给你)。更具体点,SaaS(Software as a Service),各种的网页邮件系统, google docs (一个在线的office)都是SaaS。收发邮件登陆一个网页就行,而不需要在自己机器上安装一个软件。这就是计算机应用的一个趋势,把所有东西都做到网上。再来看看网上的趋势:RIA(Rich InternetApplication)。RIA简单来讲就是一个网页(网络应用),在完成基本功能的同时,会让你觉得很漂亮,操作起来很舒服,效果很炫,而不是打开后立刻就想关掉它。其实大受欢迎的开心网(各种插件)和twitter,甚至QQ空间等,都有RIA的身影。
       好了,ArcGIS之所以在行业领先,特点之一就是它能紧跟计算机发展的趋势。ArcGIS Online就是那朵天边的浮云;JavaScript API,Flex API,Siverlight API就是ArcGIS自己RIA的三驾马车。
       这里还得插一句,我觉得ArcGIS Server的主角本来是ADF,通过它我们可以完成一个无所不能的ServerGIS。但在大多数情况下,GIS都是作为特定的业务嵌入在一些MIS中的,相比购买ADF这辆悍马来说,还是直接驾驭三套车跑的更轻快一些。
       现在看看我们的主角。其实是ArcGIS API forSilverlight/WPF(以下专注Siverlight部分),那么Silverlight和WPF的关系如何呢?Silverlight原来叫WPF/E,E就是Everywhere,从命名可以看出它们的关系:Silverlight是WPF的一个子集。WPF是.NETFramework 3.0的组成部分之一,微软视其为下一代用户界面,总之很高档就是了(在Vista和Windows7中看到的大量与XP不同的界面,就是WPF的身影);Silverlight可以看做是WPF在浏览器里的一个外挂,用于向网络用户展示自己的强大能力,由于受限于网络环境,所以是WPF的一部分核心功能。Siverlight的设计初衷是跨平台,跨浏览器的。
       如果这些还是比较抽象,那么可以造一个排比句来进一步说明。之前先肯定一点,Flash现在在网络中的的主导地位。开始造句。Adobe有Flash,微软有Silverlight;Adobe有AIR,微软有WPF;Flex有mxml,Silverlight有xaml;Adobe有ActionScript,微软有Code-Behind(C#/VB.NET)或者JavaScript;Adobe有CS(包括Dreamweaver,Flash,Fireworks,Photoshop,Illustrator),微软有ExpressionStudio(包括Blend,Web,Design,Media,Encoder)。现在,能够看来Siverlight到底是何方神圣了吧?
       最后再来说说ArcGIS这三驾马车(JavaScript API,Flex API,SiverlightAPI)。国外有人说,随着Siverlight API的推出,与Flex API一起,将会使JavaScriptAPI慢慢退出历史舞台,因为前两者就是为RIA而生的。但其实也不然,随着Google和Mozilla工程师的推进,他们能够 使JavaScript的执行速度提高非常多 ,Chrome就是例子。在这种背景下,一些非常cool的程序员会让古老的JavaScript获得重生。到底哪匹马跑得更快?别回答这种问题,赶紧挑一匹自己的马儿,快马扬鞭吧~~
       在今年的ESRI开发用户大会上,一阵鼓声过后,ESRI隆重推出了ArcGIS API for Silverlight/WPF(beta)。接下来我将把自己在学习Silverlight API中的一些经历和大家分享,与大伙共同进步。

C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(三十九)向Silverlight移植①

C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(三十九)向Silverlight移植①

    深蓝Silverlight-MMORPG游戏引擎单机部分即将开源;这两节里,我将为大家讲解本教程示例游戏从WPFSilverlight移植的一些关键性优化与性能提升技巧,以及新增加的内容等等。接下来的时间里,我将接着前面的教程,通过Silverlight平台继续为大家演义,目标只有一个:誓将Silverlight游戏引擎完美到底!

一、主要改进:

1Silverlight3.0上的右键实现:

//注册右键事件

HtmlPage.Document.AttachEvent("oncontextmenu", Game_MouseRightButtonDown);

//鼠标右键事件

private void Game_MouseRightButtonDown(object sender, HtmlEventArgs e) {

        e.PreventDefault(); //取消右键弹出菜单

……逻辑部分

}

通过上述方法还必须配合<param name="Windowless" value="true" />System.Windows.Interop.Settings.Windowless = true才能实现右键功能。另外需要特别说明的是,此方法并非官方所提供的解决方案,而是第三方间接的实现方式。因此,在使用前,您必须解为Silverlight解禁右键将付出的代价:①Windowless = true将降低程序整体性能;②无法使用输入法;③无法被所有的浏览器所兼容,例如在Google Chrome中,虽然可以激发出右键功能,但是取消不了弹出右键菜单。综上,在Silverlight3.0中,您还是得谨慎再谨慎的考虑是否使用右键。

 

2)撤消精灵及其他所有控件中的x,y,z坐标定位用关联属性,取而代之的是一个名为Coordinate的关联属性,其完整定义如下:

/// <summary>

/// 获取或设置控件坐标(关联属性)

/// </summary>

public Point Coordinate {

    get { return (Point)GetValue(CoordinateProperty); }

    set { SetValue(CoordinateProperty, value); }

}

public static readonly DependencyProperty CoordinateProperty = DependencyProperty.Register(

    "Coordinate",

     typeof(Point),

     typeof(QXSprite),

     new PropertyMetadata(ChangeCoordinateProperty)

);

private static void ChangeCoordinateProperty(DependencyObject d, DependencyPropertyChangedEventArgs e) {

     QXSprite obj = (QXSprite)d;

     if (obj.Visibility == Visibility.Visible) {

         Point p = (Point)e.NewValue;

         obj.SetValue(Canvas.LeftProperty, p.X - obj.CenterX);

         obj.SetValue(Canvas.TopProperty, p.Y - obj.CenterY);

         obj.SetValue(Canvas.ZIndexProperty, Convert.ToInt32(p.Y));

      }

}

Coordinate的类型为Point,因此,我将原先精灵移动用的DoubleAnimation动画类型替换成了PointAnimation;这样,不论是在代码结构还是性能上均得到很大的优化。更改控件坐标时,只需修改它的Coordinate = new Point(x,y)即可,系统会判断该关联属性的值是否发生改变而激发ChangeCoordinateProperty方法,从而更新该控件最终在画面中的LeftPropertyTopPropertyZIndexProperty。没错,关联属性就是这么强大。

3A*移动的优化。我已留下接口,根据不同的参数设置,可以启动不同效率、不同路径长短、不同精确度的A*寻路,这里我给大家推荐两种现成的方案,第一种程序默认A*寻路方案,此方案找到的路径最精确,但性能消耗最高;另一种方案可以实现最高效的寻路,但得到的路径并非最短:

PathFinderFast pathFinderFast = new PathFinderFast(varyObstruction) {

    HeavyDiagonals = false,

    HeuristicEstimate = 100,

};

我在Silverlight引擎中封装的A*寻路DLL,是根据教程第七节的老外A*改编而成。因此,您完全可以将之作为一个调试器,调试不同的搭配方案,然后将参数赋予pathFinderFast里:

例如上图,我通过模拟测试,发现最终找到路径所消耗的时间为0.0071秒,假如我已对此设置所产生的路径长度与性能感到满意,那么接下来要做的就是将此方案的配置记录下来: Diagonals = true ; Heavy Diagonals = true ; Henuristic = 5 ; Formula = Max(DX,DY) ; Use Tie Breaker = false ; Search Limit = 40000 ; 寻路对象使用的是FastPathFinder

OK,最后来在Silverlight引擎中,我就可以这样来启动A*寻路:

PathFinderFast pathFinderFast = new PathFinderFast(varyObstruction) {

    Diagonals = true,

    HeavyDiagonals = true,

    HeuristicEstimate = 5,

    Formula = HeuristicFormula.MaxDXDY,

    TieBreaker = false,

    SearchLimit = 40000,

};

嘿嘿,其实使用A*是可以如此简单的,不是吗?

二、主要优化:

1)  地图切片实现了最优化加载方法。即不需要额外做多余判断,也无需每次对切片容器进行Clear。只需按从8的顺序对这9个切片重新赋值Source即可,性能真的很优哦:

private void ChangeMapSection() {

……

        countSection = 0;

        for (int x = startSectionX; x <= endSectionX; x++) {

           for (int y = startSectionY; y <= endSectionY; y++) {

              mapSection[countSection].Source =

 Super.GetImage(string.Format("/Image/Map/{0}/Surface/{1}_{2}.jpg", mapCode, x, y));

              Canvas.SetLeft(mapSection[countSection], x * mapSectionWidth);

              Canvas.SetTop(mapSection[countSection], y * mapSectionHeight);

              countSection++;

        }

……

}

2)改进了 “托盘式”主位地图移动模式。首先我想向一些朋友道歉,一时找不到是哪篇文章后面评论中有提到对一个Canvas进行移动而不是遍历所有精灵,这样可以提升逻辑方面的性能;我当时有测试过,为什么一直坚持不行,因为我没转过弯,主角和其他所有对象是完全可以放在一个Canvas里的,这也意味着它们的ZIndex顺序照样可以很好的处理,同时实现“托盘式”地图移动模式。最终在QQ群里“内Cool超人”的感化下,我才得以觉醒。这样,虽然画面性并无提升,但是,配合上Coordinate坐标关联属性的回调方法使用,可以去掉循环遍历地图上所有对象位置,在逻辑上大大的提升了性能。

3)隐藏远离画面窗口的精灵对象。这是基于Web游戏所必须做的处理,它将大大减少不必要元素的呈现及逻辑运算:

……

//隐藏及显示区域范围内精灵

      if ((Math.Abs(sprite.Coordinate.X - Leader.Coordinate.X) > this.ActualWidth / 2) || (Math.Abs(sprite.Coordinate.Y - Leader.Coordinate.Y) > this.ActualHeight / 2)) {

           sprite.Visibility = Visibility.Collapsed;

           sprite.Timer.Stop();

      }else {

             if (!sprite.Timer.IsEnabled) {

                  sprite.Visibility = Visibility.Visible;

                  sprite.Timer.Start();

             }

      ……

}

……

在间隔0.5秒的辅助计时器事件中进行类似如上判断,当某个精灵超出了主角可视范围,即在我们屏幕窗口所能看到的区域以外,则将之隐藏掉,并停止它的切帧动作,否则反之。这对提升游戏整体性能起着决定性关键作用。如果是网络版,我们则可以拓展出2级范围,其中1级范围即为上述范围;而2级范围则为:当某个已被隐藏的精灵远离主角到了更遥远的地方,则我们将之移除掉,从而减少逻辑且实现不必要资源的及时释放与回收。

4)改进了时时障碍物系统。整个游戏有两个障碍物数组(可以记录0-255代表障碍物,除外的所有其他字节均代表无障碍。这里我使用1标识无任何对象可通行区域,10-19用来标识传送点。如果以后需要加入新的地形效果拓展,那么同样可以使用类似设定:例如20用来标识可通行水域,21标识可通行沙漠等等;这样,现当主角在这些区域中移动时,会发出相应的脚步声,使游戏效果更为逼真)。动态障碍物系统实现代码如下,首先定义一个固定数组和一个动态数组:

byte[,] fixedObstruction, varyObstruction;

fixedObstruction是地图加载后永远不变的地图信息描述载体,它记录了地图中肯定无法通过的地形及传送点的位置等等。varyObstruction是时时的动态地图信息,会根据所有精灵时时的位置来填充障碍物。

在每次A*移动时,我们通过先去掉精灵脚底的障碍物区域(HoldWidthHoldHeight),然后启动A*寻路,找到路径后再补回精灵的脚底障碍物区域:

……

SetSpriteObstruction(sprite, 1);

AStarMove(sprite, GetSpriteEdge(enemy));

sprite.UseAStarMove = true;

SetSpriteObstruction(sprite, 0);

……

其中SetSpriteObstruction方法为:

/// <summary>

/// 设置精灵占位障碍物对应值

/// </summary>

private void SetSpriteObstruction(QXSprite sprite, byte sign) {

        int x = (int)(sprite.Coordinate.X / gridSizeX);

        int y = (int)(sprite.Coordinate.Y / gridSizeY);

        for (int m = x - sprite.HoldWidth; m <= x + sprite.HoldWidth; m++) {

            for (int n = y - sprite.HoldHeight; n <= y + sprite.HoldHeight; n++) {

                if (fixedObstruction[m, n] != 0) {

                    varyObstruction[m, n] = sign;

                }

            }

        }

}

WPF/Silverlight
作者: 深蓝色右手
出处: http://alamiye010.cnblogs.com/
教程目录及源码下载: 点击进入( 欢迎加入WPF/Silverlight小组 WPF/Silverlight博客团队)
本文版权归作者和博客园共有,欢迎转载。但未经作者同意必须保留此段声明,且在文章页面显著位置给出原文连接,否则保留追究法律责任的权利。

原文链接: http://www.cnblogs.com/alamiye010/archive/2009/11/03/1595141.html

My Silverlight系列(10)—— Silverlight中的InkCanvas

My Silverlight系列(10)—— Silverlight中的InkCanvas

有许多人喜欢手写板或者涂鸦板之类的东西,而并不怎么喜欢输入法,因此Microsoft专门有Ink这个东西用于处理鼠标画图。不得不说这个东西功能十分的强大,也让许多用户使用起来非常方便,用微软开发出来的很多Ink与Bitmap结合的API,即使不会用Photoshop的人也能轻松打开一张图片,然后在自己喜欢的地方写上一段话或者签个名什么的。这个功能,Winform上面有,WPF上面也有,当然Silverlight上也有,只不过作为起步不久的Silverlight版Ink,功能尚不够强大,目前能够开放给我们使用的,只有InkPresenter这一个控件。

也许开发WPF的人都没怎么听说过这个控件而只听说过InkCanvas——那是一个在WPF上对Ink功能封装得非常完善的控件,我们可以使用它进行画图和橡皮擦等一系列的事情。当然,如果你去研究过这个控件,你就可以发现其实它其实是通过DataBinding在InkPresenter上进行了进一步的封装。由于WPF与Silverlight不同的继承结构,恐怕在Silverlight上很难照搬WPF上那一套,即我们不能对其进行一对一的Port,所以如果要在Silverlight上实现一个InkCanvas,就要另辟蹊径。

如果你使用过WPF的InkCanvas控件,你将会发现它支持EraseByPoint,EraseBystroke,Ink三种模式,而且支持复制、粘贴,而且可以轻松地扩展出撤销与重做两个功能。但是后面的一系列功能,不是InkCanvas的核心功能,只要前三者得以实现,那么这个InkCanvas就可以正常的运作了。那么,我们首先从这三种模式中用于画图的Ink模式说起。

InkCanvas的核心,其实在于它内部的InkPresenter,在Silverlight中InkPresenter仅仅是Canvas的子类,只不过它多了strokes这么一个属性用于存储和展示画上去的所有stroke。因此,它把如何生成一个stroke的问题完全留给了我们。先来看一下stroke的定义:

复制代码

  //  Summary:

    
     Represents a collection of points that correspond to a stylus-down, move,

    
     and stylus-up sequence.

     public   sealed   class  stroke : DependencyObject

    
{

        
// Summary:

        
     Initializes a new instance of the System.Windows.Ink.stroke class.

        public stroke();

        

        
     Initializes a new instance of the System.Windows.Ink.stroke class with the

        
     specified System.Windows.Input.StylusPointCollection.

        
 Parameters:

        
   stylusPoints:

        
     A System.Windows.Input.StylusPointCollection that represents the System.Windows.Ink.stroke.public stroke(StylusPointCollection stylusPoints);


        
     Gets or sets the properties of the stroke, such as System.Windows.Ink.DrawingAttributes.Height,

        
     System.Windows.Ink.DrawingAttributes.Width, System.Windows.Ink.DrawingAttributes.Color,0)">     or System.Windows.Ink.DrawingAttributes.OutlineColor.

        
 Returns:

        
     The System.Windows.Ink.DrawingAttributes of the stroke.

        public DrawingAttributes DrawingAttributes getset; }

        
     Gets or sets the stylus points of the System.Windows.Ink.stroke.

        
     The System.Windows.Input.StylusPointCollection that contains the stylus points

        
     that represent the current System.Windows.Ink.stroke.

        public StylusPointCollection StylusPoints set; }


        
     Retrieves the bounding Box for the System.Windows.Ink.stroke object.

        
     A System.Windows.Rect structure defining the bounding Box for the System.Windows.Ink.stroke

        
     object.public Rect GetBounds();

        
     Indicates whether a specified System.Windows.Input.StylusPointCollection

        
     intersects with a System.Windows.Ink.stroke object.

        
   stylusPointCollection:

        
     The System.Windows.Input.StylusPointCollection used to check for intersection

        
     with the System.Windows.Ink.stroke object.

        
     true if the specified System.Windows.Input.StylusPointCollection intersects

        
     with the System.Windows.Ink.stroke object; otherwise, false.public bool HitTest(StylusPointCollection stylusPointCollection);

    }

复制代码

其中DrawingAttributes这个属性是用于描述画笔的颜色的,而StylusPoints描述了stroke内点的集合。学过数学的人都知道,线是由点组成的,因此只要我们找到了应该插入到这个stroke中所有的点,那么生成一个新的stroke不在话下。所幸MouseEventArgs中,有一个StylusDevice只读属性,而它的一个公共方法public StylusPointCollection GetStylusPoints(UIElement relativeto)可以在鼠标事件触发的时候,得到这些“点”的集合。我们只需要为InkPresenter加上MouseLeftButtonDown,MouseMove,MouseLeftButtonUp三个handler,那么我们就可以在鼠标进行轨迹上把那些点加到线上,并将这条线加入到InkPresenter这个“面”里。代码比较多,最后我会把工程放在下面,就不一段一段的贴了。

其实这个Ink模式,不算什么难点,而后面这个EraseBystroke也相对简单,最笨的方法就是遍历InkPresenter内所有的stroke,然后一一检验它是否与我们的"Eraser"有交叉,如果有,则将它Remove。但是,最后这个EraseByPoint可没那么容易了,因为当橡皮将一条线拦腰截断的时候,不但要把擦掉的部分去掉,还要把余下的两段保留在strokes这个strokeCollection中,这才能达到一分为二的效果。我最初在实现这个功能的时候,由于设计的算法时间复杂度居高不下,造成如果相交的线过多,或者橡皮拖动太快,就会出现卡死的现象。在与微软silverlight开发小组的stefan swick交流之后,他决定实现这一功能,并且将其做成一个Custom Control。昨天他告诉我他把这个东西做好了,要我去他的Blog上下载。今天我仔细研究了他的算法,发现这个算法与我的算法有一个最大的不同之处就是:我在将一条线一分为二的过程中,完全是按照从前向后的顺序,将每个点一一挎贝并缓存,从前向后判断这个点是否被橡皮擦中,如果被擦中的话,马上生成一个新的stroke,把旧的加入strokes内,并对新的stroke进行上述相同的操作。而stefan的算法则分为了两个部分,首先从前向后把前面没有被擦中的点取出来存到一个新的stroke中,然后停止,再从后往前寻找后面的点,将没有被擦中的点加入到一个新的stroke中,直到遇到被擦中的点停止。这样的话,可以保证一个stroke可以被一分为二。

经过我的测试,执行并没有什么问题。但是由于我们向stroke中插入点,完全依赖于MouseMove事件,如果我们的鼠标移动速度过快,那么被插入的这些本就离散的点,它们之前的间隔会变得更大。这在Ink模式下不会有什么问题,但是在EraseByPoint模式下,就会因被去掉的点附近没有其他的点,而一次性擦掉很大的一段,这是由于我们在插入点和擦除的时候没有做任何的优化造成的,希望这个问题能得到解决。

大家可以到http://blogs.msdn.com/swick/archive/2008/11/30/erasing-ink-in-silverlight-2.aspx去看stefan的原文,那里提供工程原件的下载,我就不再多此一举把它上传到博客园来浪费空间了。至于上面提到的问题,如果大家有什么优化的方式和算法,希望可以告诉我们,谢谢!

---------------------------------------------------------------

今天的关于WPF/Silverlight为什么要使用Canvas.SetLeft()这样的方法?wpf和silverlight的分享已经结束,谢谢您的关注,如果想了解更多关于.net – 为什么我需要MVVM用于WPF / Silverlight,有什么理由不能使用MVC吗?、ArcGIS API for Silverlight开发入门(0):为什么要用Silverlight API?、C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(三十九)向Silverlight移植①、My Silverlight系列(10)—— Silverlight中的InkCanvas的相关知识,请在本站进行查询。

本文标签: