首页 > 代码库 > Silverlight 雷达图和一种特殊泡泡画法

Silverlight 雷达图和一种特殊泡泡画法

原文:Silverlight 雷达图和一种特殊泡泡画法

<iframe style="background-image: url(‘http://pic002.cnblogs.com/images/2012/161229/2012062115575565.jpg‘);" src="http://www.jm47.com/os/app/jmchart/" scrolling="yes" width="90%" height="250"></iframe>

 

自上次发了雷达图,也没怎么说一下。

这次又做了一种图,继续共享一下,就是以一个点为中心,周围绕着几个点,用一个箭头与中心相连并带有某些信息。圆 和中心都可以响应鼠标事件。

我一向不会讲解所做的东西。所以大部分我直接上源码的。

简单讲解一下:

代码结构分为4部分,

1.

首先是画布,其实就是一个canvas因为现在只有二种图,

一个雷达画布 RadarCanvas(比较特殊),和一个二维坐标画布 CoorCanvas,都派生自ChartCanvas;

2.就是轴

坐标轴CoorAxis 和RadarAxis 都派生自IAxis,其实里面什么也没有。就是一个存一些值,比较这个轴的颜色,它的起始与终结坐标。雷达轴的角度偏移等,。

 

3.model

存值的类了,暂时有 clm泡泡图的点击事件参数 CLMArrowClickEventArg,DataPoint图坐标点,图点点击事件参数ItemClickEventArg,字段映射ItemMapping

 

4.图形

现有CLMBubbleSeries,它是一个特殊的泡泡图,我们项目中用到的。这里就不说它有什么用。只讲实现。

RadarSeries,它就是雷达图形

它们都继承自ISeries

 

核心就是讲Series怎么画的

 

首先看它们的基类:

View Code
/// <summary>    /// 图表线或图接口    /// </summary>    public abstract class ISeries : Common.IBaseControl    {        public ISeries(ChartCanvas canvas)        {            Canvas = canvas;                        Points = new System.Collections.ObjectModel.ObservableCollection<Model.DataPoint>();            ItemMappings = new System.Collections.ObjectModel.ObservableCollection<Model.ItemMapping>();        }        /// <summary>        /// 动画执行时间        /// </summary>        protected const int AnimateDurtion = 1000;        /// <summary>        /// 项单击事件        /// </summary>        public EventHandler<Model.ItemClickEventArg> ItemClick;        /// <summary>        /// 画布        /// </summary>        public ChartCanvas Canvas { get; set; }        /// <summary>        /// 当前颜色        /// </summary>        public Brush Stroke        {            get;            set;        }        /// <summary>        /// 填充色        /// </summary>        public Brush Fill        {            get;            set;        }        /// <summary>        /// 图例名        /// </summary>        public string LegendLabel        {            get;            set;        }        /// <summary>        /// 当前线的label格式        /// /// #Y=当前值,#YName=当前Y轴名称,#C{列名}=表示绑定当前数据对象的指定列值        /// </summary>        public string ItemTooltipFormat { get; set; }        /// <summary>        /// 当前绑定的对象        /// </summary>        public object DataContext { get; set; }        /// <summary>        /// 当前索引        /// </summary>        public int Index { get; set; }        /// <summary>        /// 图点        /// </summary>        public System.Collections.ObjectModel.ObservableCollection<Model.DataPoint> Points { get; set; }        /// <summary>        /// 当前图型属性映射        /// </summary>        public System.Collections.ObjectModel.ObservableCollection<Model.ItemMapping> ItemMappings        {            get;            internal set;        }        /// <summary>        /// 获取对象的属性的值        /// </summary>        /// <param name="name"></param>        /// <returns></returns>        public object GetValue(string name)        {            var mapping = GetMapping(name);            if (mapping != null) name = mapping.MemberName;            var obj = Common.Helper.GetPropertyName(DataContext, name);            return obj;        }        /// <summary>        /// 获取对象的值        /// </summary>        /// <param name="name">属性名</param>        /// <returns></returns>        public double GetNumberValue(string name)        {            double value = http://www.mamicode.com/0;            var obj = GetValue(name);            if (obj != null)            {                if (Silverlight.Common.Data.TypeHelper.IsNumber(obj.GetType()))                {                    if (!double.TryParse(obj.ToString(), out value))                    {                        value = Index + 1;                    }                }                else                {                    value = Index + 1;                }            }            return value;        }        /// <summary>        /// 获取指定的字段映射        /// </summary>        /// <param name="dm"></param>        /// <returns></returns>        public Model.ItemMapping GetMapping(Model.ItemMapping.EnumDataMember dm)        {            foreach (var m in ItemMappings)            {                if (m.DataMember == dm) return m;            }            return null;        }        /// <summary>        /// 获取指定的字段映射        /// </summary>        /// <param name="dm"></param>        /// <returns></returns>        public Model.ItemMapping GetMapping(string name)        {            foreach (var m in ItemMappings)            {                if (name.Equals(m.OldName, StringComparison.OrdinalIgnoreCase) ||                    name.Equals(m.MemberName, StringComparison.OrdinalIgnoreCase) ||                    name.Equals(m.DisplayName, StringComparison.OrdinalIgnoreCase))                     return m;            }            return null;        }        /// <summary>        /// 获取指定的字段映射        /// </summary>        /// <param name="dm"></param>        /// <returns></returns>        public System.Collections.Generic.IEnumerable<Model.ItemMapping> GetMappings(Model.ItemMapping.EnumDataMember dm)        {            var ms = (from m in ItemMappings                      where m.DataMember == dm                      select m).ToArray<Model.ItemMapping>();            return ms;        }        /// <summary>        /// 当前动画        /// </summary>        protected Storyboard storyboard;        /// <summary>        /// 展现        /// </summary>        public virtual void Draw()        {            var ps = CreatePath();            foreach (var p in ps)            {                Canvas.AddChild(p);            }            if (storyboard != null && Canvas.IsAnimate)            {                storyboard.Begin();            }        }        System.Collections.Generic.List<Shape> shaps=new System.Collections.Generic.List<Shape>();        /// <summary>        /// 当前线条        /// </summary>        public System.Collections.Generic.List<Shape> Shaps        {            get { return shaps; }            protected set { shaps = value; }        }        /// <summary>        /// 生成图形        /// </summary>        /// <returns></returns>        public virtual System.Collections.Generic.IEnumerable<Shape> CreatePath()        {            return Shaps;        }        /// <summary>        /// 生成图例        /// </summary>        /// <returns></returns>        internal virtual StackPanel CreateLegend()        {            if (!string.IsNullOrWhiteSpace(LegendLabel))            {                var panel = new StackPanel();                panel.Orientation = Orientation.Horizontal;                var colorarea = new Rectangle();                colorarea.Width = 20;                colorarea.Height = 10;                colorarea.Fill = this.Fill;                colorarea.Stroke = this.Stroke;                panel.Margin = new Thickness(2);                panel.Children.Add(colorarea);                var text = new TextBlock();                text.Margin = new Thickness(2);                var dic=new System.Collections.Generic.Dictionary<string,string>();                foreach (var m in ItemMappings)                {                    if (!dic.ContainsKey("YName") && !string.IsNullOrWhiteSpace(m.DisplayName))                    {                        dic.Add("YName", m.DisplayName??m.MemberName);                    }                }                text.Text = Common.Helper.DserLabelName(LegendLabel,dic ,                        (string name) =>                        {                            return GetValue(name);                        });                text.Foreground = new SolidColorBrush(Colors.Black);                panel.Children.Add(text);                return panel;            }            return null;        }        /// <summary>        /// 添加点的小圆圈,方便鼠标点中。并加提示        /// </summary>        /// <param name="center"></param>        /// <param name="rotate"></param>        protected Ellipse AddPoint(Point center, double rotate,object tooltip,Model.DataPoint p)        {            var circle = Common.Helper.CreateEllipse(center, rotate);            circle.Stroke = this.Stroke;            circle.Fill = this.Fill;            ToolTipService.SetToolTip(circle, tooltip);            if (this.ItemClick != null) {                circle.Cursor = Cursors.Hand;                circle.MouseLeftButtonUp += (sender, e) => {                    var arg = new Model.ItemClickEventArg() {                      Data=this.DataContext,                      Item=p                    };                    ItemClick(circle,arg);                };            }            Canvas.AddChild(circle);            System.Windows.Controls.Canvas.SetZIndex(circle, Common.BaseParams.TooltipZIndex);            return circle;        }        /// <summary>        /// 生成提示信息        /// #Y=当前值,#YName=当前Y轴名称,#C{列名}=表示绑定当前数据对象的指定列值        /// </summary>        /// <returns></returns>        protected string CreateTooltip(string yName)        {            if (!string.IsNullOrWhiteSpace(this.ItemTooltipFormat))            {                var yvalue =http://www.mamicode.com/ GetValue(yName);                var tmp = Common.Helper.DserLabelName(this.ItemTooltipFormat,                    new System.Collections.Generic.Dictionary<string, string>() { { "YName", yName }, { "Y", yvalue=http://www.mamicode.com/=null?"":yvalue.ToString() } },                     (string name) =>                    {                        return GetValue(name);                    });                return tmp;            }            return this.ItemTooltipFormat;        }        public void Show()        {            throw new NotImplementedException();        }        public void Hide()        {            throw new NotImplementedException();        }    }

嗯。没有很多东西,都是一些基础操作,和几个接口。

 

下面就可以看泡泡图CLMBubbleSeries:

其构造函数:

 public CLMBubbleSeries(CoorCanvas canvas)            : base(canvas) {                this.Stroke = new SolidColorBrush(Color.FromArgb(255, 51, 153, 255));                this.Fill = new SolidColorBrush(Color.FromArgb(255, 188, 222, 255));        }

初始化它的颜色。

最重要的是二个函数

View Code
/// <summary>        /// 生成当前图形        /// </summary>        /// <returns></returns>        public override System.Collections.Generic.IEnumerable<Shape> CreatePath()        {            if (storyboard != null) storyboard.Stop();            if (Canvas.IsAnimate) this.storyboard = new Storyboard();            this.Shaps.Clear();            if (DataContext == null) return base.CreatePath();            var data = http://www.mamicode.com/DataContext as System.Collections.ICollection;            var circlesize = data.Count > 20 ? circleSize / data.Count * 20 : circleSize;            var center=new Point() { X = this.Canvas.Width / 2, Y = centerSize * 2.3 };            var left = Canvas.Margin.Left + circlesize * 2;            if (left <= circlesize / 2) left = circlesize + 2;            var bottom = (center.Y + circlesize + centerSize);            var maxbottom = Canvas.Height - Canvas.Margin.Bottom - circlesize - 4;            //距离中心距离            var radiacenter = Math.Min(center.X - left, maxbottom);            var circleIndex = -1;                        //小圆个数            var circlecount = data.Count;            var rotatestep = 3.78 / circlecount;//每个小圆的角度            var mapping = GetMapping(Model.ItemMapping.EnumDataMember.Y);            if (mapping == null) throw new Exception("至少需要指定一个Y轴字段映射");            //与中心点关联设置            var links = GetMappings(Model.ItemMapping.EnumDataMember.CLMLink);            var tocentername="";            //画泡泡            foreach (var m in data)            {                if (m != null)                {                    var item = new Model.DataPoint();                                        item.PotinShape= new Path();                    var v = Common.Helper.GetPropertyName(m, mapping.MemberName); ;                    item.PointType = Model.DataPoint.EnumPointType.ChartPoint;                    item.StringValue = v==null?"":v.ToString();                    System.Windows.Controls.Canvas.SetZIndex(item.PotinShape, Common.BaseParams.ShapZIndex);                                       var el = new EllipseGeometry();                    item.PotinShape.Data = el;                    //画中心位置                    if (circleIndex == -1)                    {                        item.Position = el.Center = center;                        el.RadiusX = el.RadiusY = centerSize;                        item.Width = item.Height = centerSize * 2;                        tocentername = item.StringValue;                        item.StringValue =(CenterName??mapping.MemberName) + "\n" + item.StringValue;                        var label = item.CreateLabel();                        //加入标签                        Canvas.AddChild(label);                        if (ItemClick != null)                        {                            label.Cursor = Cursors.Hand;                            var centerdata =http://www.mamicode.com/ m;                            label.MouseLeftButtonUp += (sender, e) =>                            {                                var arg = new Model.ItemClickEventArg()                                {                                    Data = centerdata,                                    Item = item                                };                                ItemClick(sender, arg);                            };                        }                        var tootip = CreateTooltip(m);                        ToolTipService.SetToolTip(label,tootip);                    }                    //画边上的小圆                    else                    {                        //初始化小圆点                        InitPoint(el, item, rotatestep, circleIndex, radiacenter, center, maxbottom, circlesize, tocentername,circlecount,links,m);                    }                                        if(Canvas.IsFillShape)item.PotinShape.Fill = this.Fill;                    item.PotinShape.Stroke = this.Stroke;                    item.PotinShape.StrokeThickness = Canvas.LineWidth;                    this.Shaps.Add(item.PotinShape);                                        circleIndex++;                                    }            }            return base.CreatePath();        }

画一个中心圆 ,和用InitPoint来画周围的小圆。

View Code
/// <summary>        /// 初始化项        /// </summary>        /// <param name="el"></param>        /// <param name="item"></param>        /// <param name="rotatestep"></param>        /// <param name="circleIndex"></param>        /// <param name="radiacenter"></param>        /// <param name="center"></param>        /// <param name="maxbottom"></param>        /// <param name="circlesize"></param>        /// <param name="tocentername"></param>        private void InitPoint(EllipseGeometry el,Model.DataPoint item,            double rotatestep, int circleIndex, double radiacenter, Point center,            double maxbottom, double circlesize, string tocentername, int circlecount,            System.Collections.Generic.IEnumerable<Model.ItemMapping> links,object data)        {            var position = new Point();                        var rotate = rotatestep * circleIndex + 2.95;            var rsin = Math.Sin(rotate);            var rcos = Math.Cos(rotate);            //二圆偏移量                       var ystep = rsin * radiacenter;            var xstep = rcos * radiacenter;            position.X = center.X + xstep;            position.Y = center.Y - ystep;            if (position.Y >= maxbottom) position.Y = maxbottom;            item.Position = position;            el.RadiusX = el.RadiusY = circlesize;            item.Width = item.Height = circlesize * 2;            var arrow = new Controls.CLMArrow(Canvas);            arrow.Fill = this.Fill;            arrow.Stroke = this.Stroke;            arrow.Rotate = rotate;            arrow.ToName = tocentername;            arrow.FromName = item.StringValue;            arrow.RotateSin = rsin;            arrow.RotateCos = rcos;            var startystep = (circlesize) * arrow.RotateSin;            var startxstep = (circlesize) * arrow.RotateCos;            arrow.StartPoint = new Point(item.Position.X - startxstep, item.Position.Y + startystep);            var endystep = centerSize * arrow.RotateSin;            var endxstep = centerSize * arrow.RotateCos;            arrow.EndPoint = new Point(center.X + endxstep, center.Y - endystep);            if (links != null)            {                var count = links.Count<Model.ItemMapping>();                if (count > 0)                {                    var lnk = links.ElementAt<Model.ItemMapping>(0);                    var tmp = Common.Helper.GetPropertyName(data, lnk.MemberName);                    if (!string.IsNullOrWhiteSpace(lnk.MarkName)) arrow.FromMarkName = lnk.MarkName;                    arrow.FromValue = tmp == null ? "" : tmp.ToString();                }                if (count > 1)                {                    var lnk = links.ElementAt<Model.ItemMapping>(1);                    var tmp = Common.Helper.GetPropertyName(data, lnk.MemberName);                    if (!string.IsNullOrWhiteSpace(lnk.MarkName)) arrow.ToMarkName = lnk.MarkName;                    arrow.ToValue = tmp == null ? "" : tmp.ToString();                }            }            //设置箭头提示事件            if (ArrowTooltipClick != null) arrow.SetClickEvent(ArrowTooltipClick);            arrow.Draw();            item.TargetControl = arrow;            var label = item.CreateLabel();            Canvas.AddChild(label);            if (ItemClick != null)            {                label.Cursor = Cursors.Hand;                label.MouseLeftButtonUp += (sender, e) =>                {                    var arg = new Model.ItemClickEventArg()                    {                        Data = data,                        Item = item                    };                    ItemClick(sender, arg);                };            }            if (Canvas.IsAnimate)            {                label.Visibility = Visibility.Collapsed;                var anima = new PointAnimation();                anima.To = position;                anima.Duration = TimeSpan.FromMilliseconds(AnimateDurtion);                Storyboard.SetTarget(anima, el);                el.Center = center;                Storyboard.SetTargetProperty(anima, new PropertyPath("Center"));                var sizeanimax = new DoubleAnimation();                sizeanimax.From = 0;                sizeanimax.To = circlesize;                Storyboard.SetTarget(sizeanimax, el);                Storyboard.SetTargetProperty(sizeanimax, new PropertyPath("RadiusX"));                var sizeanimay = new DoubleAnimation();                sizeanimay.From = 0;                sizeanimay.To = circlesize;                Storyboard.SetTarget(sizeanimay, el);                Storyboard.SetTargetProperty(sizeanimay, new PropertyPath("RadiusY"));                anima.Completed += new EventHandler((sender, e) =>                {                    label.Visibility = Visibility.Visible;                    InitMouseEvent(label, arrow);                    if (circleIndex == circlecount / 2 - 1) {                         arrow.Show();                        currentShowedArrow = arrow;                    }                });                this.storyboard.Children.Add(anima);                this.storyboard.Children.Add(sizeanimax);                this.storyboard.Children.Add(sizeanimay);            }            else            {                el.Center = position;                //加入标签                //var label = item.CreateLabel();                //Canvas.AddChild(label);                InitMouseEvent(label, arrow);                if (circleIndex == circlecount / 2 - 1) {                     arrow.Show();                    currentShowedArrow = arrow;                }            }        }

 

最后是画坐标图代码:

       /// <summary>        /// 画坐标图        /// </summary>        private void DrawCoor()        {            if (!IsDrawBaseLine) return;            coorGeometry.Figures.Clear();            var xaxis = new Axis.CoorAxis();            xaxis.AxisShap = coorPath;            xaxis.AType = Axis.AxisType.XValue;            var yaxis = new Axis.CoorAxis();            yaxis.AType = Axis.AxisType.YValue;            yaxis.AxisShap = coorPath;                        this.Axises.Add(xaxis);            this.Axises.Add(yaxis);            var coorfigure = new PathFigure();            coorGeometry.Figures.Add(coorfigure);                        //画上箭头            yaxis.StartPoint = coorfigure.StartPoint = new Point(Margin.Left, Margin.Top - arrowMargin);            var tlp = new Point() { X = Margin.Left - arrowMargin, Y = Margin.Top + arrowMargin };            coorfigure.Segments.Add(new LineSegment() { Point = tlp });            coorfigure.Segments.Add(new LineSegment() { Point = tlp });            coorfigure.Segments.Add(new LineSegment() { Point = coorfigure.StartPoint });            var trp = new Point() { X = Margin.Left + arrowMargin, Y = Margin.Top + arrowMargin };            coorfigure.Segments.Add(new LineSegment() { Point = trp });            coorfigure.Segments.Add(new LineSegment() { Point = trp });            coorfigure.Segments.Add(new LineSegment() { Point = coorfigure.StartPoint });            //左侧Y轴            yaxis.EndPoint = xaxis.StartPoint = new Point() { X = Margin.Left, Y = this.Height - Margin.Bottom };            coorfigure.Segments.Add(new LineSegment() { Point = xaxis.StartPoint });            //x轴            xaxis.EndPoint = new Point() { X = this.Width - Margin.Right + arrowMargin, Y = xaxis.StartPoint.Y };            coorfigure.Segments.Add(new LineSegment() { Point = xaxis.EndPoint });                        //画右箭头            var brtp = new Point() { X = this.Width - Margin.Right - arrowMargin, Y = xaxis.EndPoint.Y - arrowMargin };            var brbp = new Point() { X = brtp.X, Y = xaxis.EndPoint.Y + arrowMargin };            coorfigure.Segments.Add(new LineSegment() { Point = brtp });            coorfigure.Segments.Add(new LineSegment() { Point = brtp });            coorfigure.Segments.Add(new LineSegment() { Point = xaxis.EndPoint });            coorfigure.Segments.Add(new LineSegment() { Point = brbp });            coorfigure.Segments.Add(new LineSegment() { Point = brbp });            AddChild(coorPath);            DrawLine();//画虚线        }        /// <summary>        /// 画虚线        /// </summary>        private void DrawLine()        {            var w = this.Width - Margin.Left - Margin.Right;            var h = this.Height - Margin.Top - Margin.Bottom;            var vstep = h / HorizontalCount;                     for (var i = 1; i <= HorizontalCount; i++)            {                var l = new Line();                l.StrokeLineJoin = PenLineJoin.Round;                l.StrokeDashArray.Add(4);                l.Stroke = DashColor;                l.StrokeThickness = 1;                l.X1 = Margin.Left;                l.Y1 = this.Height - Margin.Bottom - (vstep * i);                l.X2 = this.Width - Margin.Right;                l.Y2 = l.Y1;                AddChild(l);            }            var xstep = w / VerticalCount;            for (var i = 1; i <= VerticalCount; i++)            {                var l = new Line();                l.Stroke = DashColor;                l.StrokeDashArray.Add(4);                l.StrokeThickness = 1;                l.X1 = Margin.Left + xstep * i;                l.Y1 = Margin.Top;                l.X2 = l.X1;                l.Y2 = this.Height - Margin.Bottom;                AddChild(l);            }

 

 啊。我是正的不太会讲解。直接上源码算了

源码地址:源码