Silverlight数学引擎(13)——掩框与菜单
上节我们实现了属性编辑器,并且遗留了两个主要问题,这节我们实现两个新的功能(掩框和右键菜单)来Fix这两个问题,并且新增一些新的特性。
第一个主要问题是【选择】的功能(请注意我将【拽】改成了【选】,因为我们增加了选择以进行属性编辑,以前的拖拽只能选中可移动对象所以不适用了),当我们选中了一个对象,除了属性编辑器有反应外,对象本身无任何反应,如果对象很多,当我们眨了一下眼可能就不记得选中的是哪个对象了,就像在下围棋,不记得刚才下的是哪个子儿,岂不悲乎?解决这个问题,方式不要太多了,例如可以改变对象的样式等,但是这种方式可能与我们的样式编辑器相冲突,所以我们采取另外一种,通过掩框(MaskFrame)实现,其原理是在对象下层放一个形状一样,大小稍微大一点,且颜色鲜亮一点的控件,当对象被选中,则将该MaskFrame移到控件底部,效果如下图所示,我们为【返回】按钮添加了一个掩框:

在我们的坐标系中,只有Ellipse和Line两种图形,所以我们实现创建两个静态掩框,分别对应这两种图形,这种方式只能适用于单选的情况,对于多选应该重新设计Shape控件(例如继承Canvas),这里就先只考虑单选了:
private readonly Ellipse MaskFrameOfEllipse = new Ellipse{Stroke = Brushes.Yellow,Opacity = 0.8}; private readonly Line MaskFrameOfLine = new Line{Stroke = Brushes.Yellow,Opacity = 0.8}; public ICoordinate MaskShape { set { if (value == null) { if (Children.Contains(MaskFrameOfEllipse)) Children.Remove(MaskFrameOfEllipse); else if (Children.Contains(MaskFrameOfLine)) Children.Remove(MaskFrameOfLine); } else { if (value.Shape is Ellipse) { if (Children.Contains(MaskFrameOfLine)) Children.Remove(MaskFrameOfLine); if (!Children.Contains(MaskFrameOfEllipse)) Children.Add(MaskFrameOfEllipse); SetZIndex(MaskFrameOfEllipse, value.ZIndex - 1); MaskFrameOfEllipse.Width = MaskFrameOfEllipse.Height = value.Shape.Width + 5; MaskFrameOfEllipse.StrokeThickness = value.Shape.StrokeThickness + 6; MaskFrameOfEllipse.CenterTo(value.Center.ToPhysical(this).Coordinate); } if (value.Shape is Line) { if (Children.Contains(MaskFrameOfEllipse)) Children.Remove(MaskFrameOfEllipse); if (!Children.Contains(MaskFrameOfLine)) Children.Add(MaskFrameOfLine); var line = (value.Shape as Line); SetZIndex(MaskFrameOfLine, value.ZIndex - 1); MaskFrameOfLine.StrokeThickness = line.StrokeThickness + 10; MaskFrameOfLine.X1 = line.X1; MaskFrameOfLine.Y1 = line.Y1; MaskFrameOfLine.X2 = line.X2; MaskFrameOfLine.Y2 = line.Y2; } } } }
通过将选中对象赋给掩框来实现高亮效果,用法如下,简单吧:
public ICoordinate SeletedItem { get { return _seletedItem; } set { _seletedItem = value; MaskShape = value; } }
OK,来看第二个问题:当图形被设成透明的后,就看不到了,如果要想再去修改样式就没办法了,而且如果图形画错了也删不掉。这些就由我们功能强大的右键菜单来处理吧,我们来建立几个菜单命令,对应相应的功能:
public enum ShapeMenuCommand { 选择, 清空, 删除, 取消隐藏 } private void ExcuteShapeMenuCommand(object obj) { var cmd = (ShapeMenuCommand)obj; switch (cmd) { case ShapeMenuCommand.选择: { this.Behavior = BehaviorBase.Drag; } break; case ShapeMenuCommand.删除: { if(SeletedItem!=null) SeletedItem.CS = null; } break; case ShapeMenuCommand.清空: { while(Shapes.Count>0) Shapes.Last().CS = null; } break; case ShapeMenuCommand.取消隐藏: { Shapes.Where(s => s.Shape.Opacity < 0.1).ToList().ForEach(s => s.Shape.Opacity = 1); } break; } }
大家可以看到上面代码很简答,删除只要将CS=null就可以,其实我是对代码进行了一些重构的,并在CoordinateSystem中建立了Shapes的缓存以优化性能,复杂的处理和缓存同步都封装在CS=null这个简单的操作中。
如何实现一个菜单呢?Silverlight有现成的ContextMenu 弹出菜单了,不过需要添加System.Windows.Controls.dll 和 System.Windows.Controls.Input.Toolkit.dll 两个引用,实现的方式网上一大把,这里就不介绍了。System.Windows.Controls.Input.Toolkit.dll这个包还是挺大的,里面包含了很多控件,想想如果我们只要一个小小的弹出菜单就要动用如此大家伙,不太划算啊。毕竟我们的整个xap包也没有有Toolkit.dll 这么大的,加了这个引用势必影响页面加载速度。这里我们通过StackPanel实现一个轻量级的弹出菜单,而且可以灵活控制,因为StackPanel可以自动垂直排版嘛,用起来很简单,我们只需要按照ShapeMenuCommand枚举建立一些TextBlock放在StackPanel中就可以了,原理就是这样,很简单:
public class PopMenu : StackPanel { public PopMenu() { this.Width = 150; this.Height = 200; this.Background = Brushes.LightSkyBlue; this.Orientation = Orientation.Vertical; VerticalAlignment = VerticalAlignment.Top; } public void InitCtl<T>(Action<object> action, Panel parent) { this.action = action; var list = DataTypeExtensions.GetEnumValues<T>(); this.Width = 30 + 10 * list.Max(t => t.ToString().Length); this.Height = 22 * list.Count; foreach (object obj in list) this.AddMenuItem(obj); parent.Children.Add(this); } }
使用也很简单,在坐标系中使用就是把它当做一个普通的控件加进去就可以,然后在鼠标右键事件中使用它,值得注意的是菜单应该在所有的图形之上,设置它的ZIndex就能达到目的,用法如下:
private void ShowPopMenu(MouseButtonEventArgs e) { try { if (PopMenu.ShapeMenu == null) { PopMenu.ShapeMenu = new PopMenu(); PopMenu.ShapeMenu.InitCtl<ShapeMenuCommand>(ExcuteShapeMenuCommand, this); } PopMenu.ShapeMenu.Show(e); e.Handled = true; } catch (Exception ex) { MessageBox.Show(ex.ToString()); } }
好了,一切就绪,看看效果吧:

http://www.diyuexi.com/MathEngineTestPage.aspx
至此为止,我们还有一项极其重要的功能没有完成,那就是给图形增加名称,例如点的名字叫A、B、C...,圆叫O1、O2,名不正言不顺嘛,更别说去作图了,是不是,那么下节我们给它们命名吧!
浙公网安备 33010602011771号