Silverlight数学引擎(13)——掩框与菜单

上节我们实现了属性编辑器,并且遗留了两个主要问题,这节我们实现两个新的功能(掩框和右键菜单)来Fix这两个问题,并且新增一些新的特性。

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

在我们的坐标系中,只有EllipseLine两种图形,所以我们实现创建两个静态掩框,分别对应这两种图形,这种方式只能适用于单选的情况,对于多选应该重新设计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,名不正言不顺嘛,更别说去作图了,是不是,那么下节我们给它们命名吧!

 

posted @ 2012-12-07 13:01  地月银光  阅读(1236)  评论(2)    收藏  举报