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


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

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

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

    简单讲解一下:

    代码结构分为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 = 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 = GetValue(yName);
                    var tmp = Common.Helper.DserLabelName(this.ItemTooltipFormat,
                        new System.Collections.Generic.Dictionary<string, string>() { { "YName", yName }, { "Y", yvalue==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 = 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 = 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);
                }

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

    源码地址:源码

  • 相关阅读:
    jquery中的ajax方法参数的用法和他的含义:
    链接
    数据库视图作用?什么时候用视图?
    八大排序算法的 Python 实现
    @wrap装饰器
    model方法取值总结
    js获取select改变事件
    SQL Server查询时添加一列连续的自增列
    UIAppearance使用详解-备
    NSString、NSData、char* 类型之间的转换-备
  • 原文地址:https://www.cnblogs.com/jiamao/p/2538599.html
Copyright © 2020-2023  润新知