自定义RectangleChart图形控件--分享
1.前言
最近有好一阵子没有写文章了,这几天无聊写了一个主要用于MVVM绑定的自定义RectangleChart柱状图形控件,主要模仿Chart图形中的数据显示。这里跟大家分享下自定义RectangleChart柱状图形控件的思路和心得。- 以下图片为自定义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 { get; set; } //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的开发只是一个简单的而且不完善的控件开发,只是凭借自己的一种兴趣和思路的分享。希望能够给大家带来一些帮助。同时也欢迎园子里的朋友多提出一些宝贵的意见。共同讨论、共同进步、一起分享。