自定义RectangleChart图形控件--分享

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

  

   2.功能:
   以下图片为自定义RectangleChart柱状体控件的详细功能。
     刻度表   :通过刻度表可以让整个控件的图形数据显示更加直观。
     刻度列表:显示图形中的所有刻度。
     刻度值   :标记整个图形中各个刻度的进制位(如图形中所示,进制位为10)。
     柱状体   :数据的柱状图形表示。
     柱状体值:具体的数据值。
     数据列表:整个图形中的所有数据。
       自定义RectangleChart图形控件--分享
   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的开发只是一个简单的而且不完善的控件开发,只是凭借自己的一种兴趣和思路的分享。希望能够给大家带来一些帮助。同时也欢迎园子里的朋友多提出一些宝贵的意见。共同讨论、共同进步、一起分享。

  

你可能感兴趣的:(chart)