zoukankan      html  css  js  c++  java
  • 自定义RectangleChart图形控件分享

      1.前言
      最近有好一阵子没有写文章了,这几天无聊写了一个主要用于MVVM绑定的自定义RectangleChart柱状图形控件,主要模仿Chart图形中的数据显示。这里跟大家分享下自定义RectangleChart柱状图形控件的思路和心得。

      

      2.功能:
      以下图片为自定义RectangleChart柱状体控件的详细功能。
         刻度表   :通过刻度表可以让整个控件的图形数据显示更加直观。
         刻度列表:显示图形中的所有刻度。
         刻度值   :标记整个图形中各个刻度的进制位(如图形中所示,进制位为10)。
         柱状体   :数据的柱状图形表示。
         柱状体值:具体的数据值。
         数据列表:整个图形中的所有数据。
          
      3.思路:
      这种简单的图形控件的思路一般是这样的。首先我们会考虑到,我们需要画一个刻度表(也就是表格),然后再画上刻度列表(也就是右侧0-100的数据),然后根据数据列表画上相应的柱状体。这个是我们起初的思路。然后我们需要根据一定的计算后,把结果画在Canvas面板中的相应位置上。
       根据初始的思路我们想到我们需要定义一个依赖属性(DataSource 类型为List<ChartData>),ChartData为自定义的一个数据类型。然后我们需要根据接收到的DataSource的值进行计算后绘图。绘图前我们需要实现的功能就是画刻度列表,刻度列表不可以随便画,必须计算出接近数据结合(DataSource值)的值。也就是计算出最接近DataSource中的最大值和最小指,比如图形中的刻度列表最大值为100,最小值为0。计算的思路是找出最大值然后去。
      
      
       计算前我们需要定义一些内部使用的变量,其具体代码如下:  
    View Code
    private string _lineColor = "#D3E5FF";      //刻度列表线颜色

            private double ChartMaxYValue = 100;        //Y轴最大值
            private double ChartMaxXValue { getset; } //X轴最大值
           
            private int leftMargin = 100;       //面板左边框
            private int rightMargin = 100;      //面板右边框

            private int topMargin = 20;         //面板上边框
            private int bottomMargin = 20;      //面板下边框

            private int leftChartMargin = 20;   //具体图形的左边框
            private int rightChartMargin = 20;  //具体图形的右边框

            private int ySize = 40;             //Y轴线间距
            private int xSize = 50;             //X轴线间距

            private int startY = 20;            //Y轴起点
            private int endY = 460;             //Y轴结束点

            private int startX = 0;             //X轴起点
            private int endX = 100;             //Y轴起点

            private double yStepSize = 0.8;     //Y轴的进位制
            private int chartWidth = 40;        //图形宽度
       步骤一:计算的代码如下:
      
    View Code
    /// <summary>
            
    /// 取DataSource最大值
            
    /// </summary>
            public void MeasuneValue()
            {
                if (DataSource != null)
                {
                    double max = 0;
                    foreach(var data in DataSource)
                    {
                        if (data.ChartValue > max)
                        {
                            max = data.ChartValue;
                        }
                    }

                    ChartMaxYValue = GetMaxYValue(max);
                }            
            }
            
            /// <summary>
            
    /// 获取最大进位制(如60返回100 4返回10)
            
    /// </summary>
            
    /// <param name="max">最大值</param>
            
    /// <returns>最大进位制</returns>
            public double GetMaxYValue(double max)
            {
                int valueTime = (int)Math.Log10(max);    //计算出最大的进位制

                return Math.Pow(10, valueTime+1);
            }
       步骤二:然后我们就可以进行刻度表了(也就是画表格了),从上面我们已经计算得出的Y轴最大值和Y轴最小指(这里只是简单开发,所以只考虑最大值,也就是说暂时还没考虑负值的图形),我们就可以进行表格,绘画的思路就是记录下线条的位置然后添加到Canvas面板中,画X轴、Y轴线条具体代码如下:
      
    View Code
            /// <summary>
            
    /// 画Y轴的线条
            
    /// </summary>
            
    /// <param name="container"></param>
            public void DrawYLine(Canvas container)
            {
                var x2Value = leftMargin + (DataSource.Count+1) * xSize;
                for (int i = 0; i < 12; i++)
                {
                    container.Children.Add(new Line { X1 = leftMargin, Y1 = topMargin + (i * ySize), X2 = x2Value, Y2 = topMargin + (i * ySize), Stroke = new SolidColorBrush(ChartLineColor) });
                }
            }

            /// <summary>
            
    /// 画X轴的线条
            
    /// </summary>
            
    /// <param name="container"></param>
            public void DratXLine(Canvas container)
            {            
                for (int i = 0; i < DataSource.Count+2;i++ )
                {
                    var xValue = leftMargin + (i * xSize);
                    container.Children.Add(new Line { X1 = xValue, Y1 = startY, X2 = xValue, Y2 = endY, Stroke = new SolidColorBrush(ChartLineColor) });
                }
            }
      步骤三:我们需要把相应的刻度值画到表格的Y轴上:具体代码如下
      
    View Code
            /// <summary>
            
    /// 画Y轴上的数值
            
    /// </summary>
            
    /// <param name="container"></param>
            public void DrawYValue(Canvas container)
            {
                for (int i = 0; i <=10; i++)
                {
                    double value = i * ChartMaxYValue / 10;

                    TextBlock t = new TextBlock {Text = value.ToString(),Foreground = ValueForeground};
                    t.Width = 80;
                    t.TextAlignment = TextAlignment.Right;
                    Canvas.SetLeft(t,10);
                    Canvas.SetTop(t,50+(10-i)*ySize);
                    MainContainer.Children.Add(t);
                }            
            }
      步骤四:画柱状体:具体代码如下
      
    View Code
            /// <summary>
            
    /// 画图形
            
    /// </summary>
            
    /// <param name="container"></param>
            public void DrawChart(Canvas container)
            {
                container.Width = leftMargin + DataSource.Count * chartWidth * 2 + rightMargin;
                for (int i = 0; i < DataSource.Count; i++)
                {
                    //LinearGradientBrush brush = new LinearGradientBrush();
                    
    //GradientStopCollection c = new GradientStopCollection();
                    
    //c.Add(new GradientStop { Color = GetRandomColor(),Offset=0.0});
                    
    //c.Add(new GradientStop { Color = GetRandomColor(), Offset = 1.0});
                    
    //brush.GradientStops = c;

                    Rectangle rect = new Rectangle
                    {
                        Width = chartWidth,
                        Height =  DataSource[i].ChartValue * yStepSize,
                        Fill = new SolidColorBrush(GetRandomColor()),
                        ToolTip = DataSource[i].ChartValue.ToString()
                    };

                    rect.RadiusX = 5;
                    rect.RadiusY = 5;

                    Canvas.SetLeft(rect, leftMargin + (leftChartMargin + leftChartMargin / 2) + i * (chartWidth + rightChartMargin / 2));
                    Canvas.SetTop(rect, endY - rect.Height);
                    MainContainer.Children.Add(rect);
                }
            }
     
       步骤五:画画数据列表,因为数据列表显示的时候如果是水平放置的话会挡住其他的列表值,所以这里需要对数据列表进行转换。具体代码如下:
      
    View Code
            /// <summary>
            
    /// 画数据列表值
            
    /// </summary>
            
    /// <param name="container"></param>
            public void DrawXValue(Canvas container)
            {
                container.Width = leftMargin + DataSource.Count * chartWidth * 2 + rightMargin;
                for (int i = 0; i < DataSource.Count; i++)
                {
                    TextBlock t = new TextBlock
                    {
                        Margin = new Thickness(5),
                        Text = DataSource[i].Name,
                        TextAlignment = TextAlignment.Center,
                        Foreground = TitleForeground
                    };
                    t.RenderTransform = new RotateTransform { CenterX =5,CenterY=5,Angle = 60 };
                    Canvas.SetLeft(t, leftMargin +10 + (leftChartMargin + leftChartMargin / 2) + i * (chartWidth + rightChartMargin / 2));
                    Canvas.SetTop(t, endY);
                    MainContainer.Children.Add(t);
                }
            }
     
      步骤六:感觉Y轴刻度值跟X轴的数据列表的显示颜色都是一致为黑色,这样不好看,为了方便大家使用,这里定义了两个依赖属性来控制前景颜色。具体代码如下:
      
    View Code
            /// <summary>
            
    /// 数据标题颜色
            
    /// </summary>
            public static readonly DependencyProperty TitleForegroundProperty = DependencyProperty.Register("TitleForeground",typeof(SolidColorBrush),typeof(RectangeChart),new PropertyMetadata(Brushes.Brown));
            public SolidColorBrush TitleForeground
            {
                get { return (SolidColorBrush)GetValue(TitleForegroundProperty); }
                set
                {
                    SetValue(TitleForegroundProperty,value);
                }
            }

            /// <summary>
            
    /// 刻度列表颜色
            
    /// </summary>
            public static readonly DependencyProperty ValueForegroundProperty = DependencyProperty.Register("ValueForeground"typeof(SolidColorBrush), typeof(RectangeChart), new PropertyMetadata(Brushes.Black));
            public SolidColorBrush ValueForeground
            {
                get { return (SolidColorBrush)GetValue(ValueForegroundProperty); }
                set
                {
                    SetValue(ValueForegroundProperty, value);
                }
            }
      这样一个简单的RectangleChart控件就完成了。
     
     4.测试:采用MVVM来进行测试。
       
        我们可以用MVVM的架构来进行测试,先定义一个View(RectangleView) 
      
    View Code
    <UserControl x:Class="SmlAnt.Library.Views.ControlsTest.RectangeChart"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                 mc:Ignorable="d" 
                 xmlns:Controls="clr-namespace:SmlAnt.Library.Controls"
                 d:DesignHeight="300" d:DesignWidth="300"             >
        <Grid>
            <ScrollViewer HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible">
                <Controls:RectangeChart DataSource="{Binding DataSource}" TitleForeground="Brown" ValueForeground="Black"/>
            </ScrollViewer>
        </Grid>
    </UserControl>
      然后再定义ViewModel(RectangleViewModel)
      
    View Code
    public class RectangleChartViewModel:ViewModelBase
        {
            Random random = new Random();
            private List<Controls.ChartData> _dataSource;
            public List<Controls.ChartData> DataSource
            {
                get { return _dataSource; }
                set
                {
                    if (_dataSource != value)
                    {
                        _dataSource = value;
                        RaisePropertyChanged("DataSource");
                    }
                }
            }

            public RectangleChartViewModel()
            {
                
                var temp = new List<Controls.ChartData> { };
                for (int i = 1; i <= 10; i++)
                {
                    Controls.ChartData data = new Controls.ChartData
                    {
                        ChartValue = random.Next(40,100),
                        Name = "数据"+i.ToString()                    
                    };
                    temp.Add(data);
                }

                DataSource = temp;
            }
        }
       
      
      
      
      
      

     

        5.总结:

     这个自定义RectangleChart的开发只是一个简单的而且不完善的控件开发,只是凭借自己的一种兴趣和思路的分享。希望能够给大家带来一些帮助。同时也欢迎园子里的朋友多提出一些宝贵的意见。共同讨论、共同进步、一起分享。

      


      作者:SmlAnt

      出处:http://www.cnblogs.com/smlAnt

      注意:转载请保留以上内容,并标作者和出处。


  • 相关阅读:
    JavaSE知识-14(正则表达式&常用工具类)
    JavaSE知识-13(StringBuffer&数组排序)
    JavaSE知识-12(String类)
    JavaSE知识-11(Eclipse使用以及Object类型)
    JavaSE知识-10(面向对象_权限修饰符&匿名内部类)
    JavaSE知识-09(面向对象_多态&抽象类&接口)
    JavaSE知识-08(面向对象_继承&方法&final)
    JavaSE知识-07(面向对象-构造方法&静态static)
    20145205 20145231 《信息安全系统设计基础》第二次实验
    20145231 《信息安全系统设计基础》第9周学习总结
  • 原文地址:https://www.cnblogs.com/smlAnt/p/2193724.html
Copyright © 2011-2022 走看看