Silverlight数学引擎(14)——给Shapes命名吧
目前为止,我们画的图形都没有名字,这给人的感觉就是每个图形都是哑巴,虽然会动但是还无法正常地跟我们交流,这节开始我们就给他们取名字,使其能够表现自己。
要命名并且显示在坐标系上,理所当然需要TextBlock了,但是我们以前创建的图形都是基于Shape的,这下就有点小麻烦了,如果在WPF中,我们还可以很方便地继承Shape去画自己的图形,但是在Silverlight中我还没有找到相关的资料可以做到这一点。在此我们只能给原有的CoordinateBase类做点小手术了:
我们在该类中增加两个虚方法:
protected virtual void ShapeAttached(){} protected virtual void ShapeDetached(){}
该方法在Shape被添加到坐标系或者从坐标系删除的时候调用,你一定猜到它是干什么的了吧,呵呵,下面就是我们文本类的定义,为了与CoordinateBase一致,同时也为了能够和其他图形一个编辑样式,我们创建的Shape是Rectangle,同时增加了一个文本框TextBlock,这个文本框怎么与坐标系关联呢,自然就用到上面的两个方法了,以下是代码:
public class LabelBase : CoordinateBase, IMovable { public LabelBase() { TextBlock = new TextBlock {FontSize = 15, FontWeight = FontWeights.Bold}; } protected TextBlock TextBlock; public override Shape CreateShape() { return new Rectangle {Fill = Brushes.Transparent, Stroke = Brushes.Black, StrokeThickness = 0, Tag = this}; } protected override void ShapeAttached() { CS.Children.Add(TextBlock); Canvas.SetZIndex(TextBlock, ZIndex); } protected override void ShapeDetached() { CS.Children.Remove(TextBlock); } }
为什么叫LabelBase呢,本节是给图形命名,名称自然要用文本显示,该文本是与图形关联的,例如文本“A”就关联一个点,但是我们知道几何题的话除了关联文本还有独立文本,比如题目本身,而且这也是我们以后必然要用到了,不如在此一并解决了哈!关联文本也是文本,我们只需要在LabelBase上稍加扩展即可。
关联文本有哪些属性呢?我们来分析一下:
- 必须关联一个坐标系上图形(如PointShape,LineShape等)
- 必须停靠在关联图形周围,当图形移动时,它要跟着移动。
- 允许被拖拽,但是不能便宜关联图形太远。
这几点说白了就是依赖,非常类似我们前面设计的图形PointOnCircle,属于半自由图形,我们就照葫芦画瓢来设计一个类ShapeLabel:
public sealed class ShapeLabel : LabelBase { public ShapeLabel(ICoordinate host) { Host = host; RegisterDependencies(host); } private ICoordinate Host; private double Radians; public double Offset = -0.3; private const double MaxOffset = 1; public override void Move(LogicalPoint newPosition) { Radians = newPosition.GetRadians(Host.Center); Offset = newPosition.Distance(Host.Center); if (Offset > MaxOffset) Offset = MaxOffset; UpdateVisual(); } public override void UpdateVisual() { Center.SetPosition(Host.Center.X + Offset * Math.Cos(Radians), Host.Center.Y + Offset * Math.Sin(Radians)); base.UpdateVisual(); } }
从Move()和UpdateVisual()可以看出,ShapeLabel可以由鼠标拖动,但移动范围仅限于以它的Host的Center为中心,半径为MaxOffset的圆内。
OK,有了ShapeLabel,当然需要配套的行为来创建它了,所以LabelCreattor就顺利成章地诞生了,在LabelCreattor中我们实现了以下的功能:
- 当鼠标点击处没有Shape或者是LabelBase的时候,创建一个自由文本框(LabelBase)
- 当鼠标点击处的图形是一个点的话,创建该点的ShapeLabel并自动给点命名。
- 当鼠标点击处的图形不是一个点的话,创建该图形的关联文本ShapeLabel。
其中“自动给点命名”这个功能需要我们画点心思去实现,因为点一般是从A、B、C….A1、B1、C1(1是下标)…这样去命名的,为了使用这种方式,我们要知道下标是怎么实现的,好在园子里已经有人把这个整理出来了:
http://www.cnblogs.com/OnlyVersion/archive/2012/11/03/2752950.html
以下是自动命名的方法:
private static string[] labelBottom = ",\x2081,\x2082,\x2083,\x2084,\x2085,\x2086,\x2087,\x2088,\x2089".Split(',');//下标0-9,注:0为空 public static string GeneratePointName(CoordinateSystem CS) { //A-Z string name = "A"; for (int i = 0; i <= 9; i++) { for (char c = 'A'; c <= 'Z'; c++) { name = c + labelBottom[i]; if (CS.Shapes.Where(s => s.Name == name).FirstOrDefault() == null) return name; } } return name; }
此外,我们还需要做一些常规的工作,例如需要增加一个新的掩框类型,工具栏中也要新增一些控件允许文本的编辑等等。具体就不介绍了,看看效果吧:
http://www.diyuexi.com/webpages/query/ShareRes.aspx
怎么样,功能算是实现了,可是用起来总感觉有点不自然,主要是有些细节工作没有去调整,比如点名的初始便宜位置啦,拖拽时候的相对位置啊,都没有去调整,而且我们现在演示的点似乎大了点,像是一个圆了,更有甚者,当两条线重合了,选一个作为半径画圆时就不知道选的是哪个了……,看来任重而道远啊!不过这些都会在慢慢地调试和重构中去优化,练习不经意的重构和不自觉的优化吧,代码会变得越来越完美。
下节再继续介绍一下图形命名以及如何将所画的图存成文本文件再根据文本文件加载出图形,可能又是一个挑战哦!