wpf柱状图等相关技术
1.面对的问题
wpf的charting(System.Windows.Controls.DataVisualization.Charting)画图很容易,但是还是面对着一些基本问题,如:tooltip只能显示值,但是实际当中我们希望能够显示的更全面(希望有时间和值),还有就是时间轴的问题,很难控制时间轴的区间,基于这些,自己做了以下处理。
2.问题解决方案
2.1 tooltip解决方案
2.1.1 区域图形,条形图形,柱状图,线条图通过添加以下点的样式解决:
<Style x:Key="DataPointStyle0" TargetType="chartingToolkit:DataPoint">
<Setter Property="Background" Value="#40699C"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="chartingToolkit:DataPoint">
<Grid x:Name="Root" Opacity="1">
<ToolTipService.ToolTip>
<StackPanel Margin="2,2,2,2">
<ContentControl Content="{TemplateBinding IndependentValue}" />
<StackPanel Orientation="Horizontal">
<ContentControl Content="{TemplateBinding DependentValue}"/>
</StackPanel>
</StackPanel>
</ToolTipService.ToolTip>
<Rectangle StrokeThickness="{TemplateBinding BorderThickness}" Stroke="{TemplateBinding BorderBrush}" Fill="{TemplateBinding Background}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
效果如图所示
在我的程序中需要添加这样的六个点样式,这样不同的系列就有六种颜色可供选择。
2.1.2 气泡图,散点图由于每个点都应该是圆点,所有又添加了六种圆点的样式:
<Style x:Key="DataPointEllipseStyle0" TargetType="chartingToolkit:DataPoint">
<Setter Property="Background" Value="#40699C"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="chartingToolkit:DataPoint">
<Grid x:Name="Root" Opacity="1">
<ToolTipService.ToolTip>
<StackPanel Margin="2,2,2,2">
<ContentControl Content="{TemplateBinding IndependentValue}" />
<StackPanel Orientation="Horizontal">
<ContentControl Content="{TemplateBinding DependentValue}"/>
</StackPanel>
</StackPanel>
</ToolTipService.ToolTip>
<Ellipse StrokeThickness="{TemplateBinding BorderThickness}" Stroke="{TemplateBinding BorderBrush}" Fill="{TemplateBinding Background}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
效果如下图所示
2.1.3 饼图通过添加点模板来解决问题
<ControlTemplate x:Key="pi" TargetType="chartingToolkit:PieDataPoint">
<Grid x:Name="Root" Opacity="1">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0.1"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="MouseOverHighlight"
Storyboard.TargetProperty="Opacity"
To="0.6"
Duration="0"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="SelectionStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0.1"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Unselected"/>
<VisualState x:Name="Selected">
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="SelectionHighlight"
Storyboard.TargetProperty="Opacity"
To="0.6"
Duration="0"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="RevealStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0.5"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Shown">
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="Root"
Storyboard.TargetProperty="Opacity"
To="1"
Duration="0"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Hidden">
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="Root"
Storyboard.TargetProperty="Opacity"
To="0"
Duration="0"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Path x:Name="Slice"
Data="{TemplateBinding Geometry}"
Fill="{TemplateBinding Background}"
Stroke="{TemplateBinding BorderBrush}"
StrokeMiterLimit="1">
<ToolTipService.ToolTip>
<StackPanel>
<ContentControl Content="{TemplateBinding FormattedIndependentValue}"/>
<ContentControl Content="{TemplateBinding FormattedDependentValue}"/>
<ContentControl Content="{TemplateBinding FormattedRatio}"/>
</StackPanel>
</ToolTipService.ToolTip>
</Path>
<Path x:Name="SelectionHighlight"
Data="{TemplateBinding GeometrySelection}"
Fill="Red"
StrokeMiterLimit="1"
IsHitTestVisible="False"
Opacity="0"/>
<Path x:Name="MouseOverHighlight"
Data="{TemplateBinding GeometryHighlight}"
Fill="White"
StrokeMiterLimit="1"
IsHitTestVisible="False"
Opacity="0"/>
</Grid>
</ControlTemplate>
最终需要把样式加入到饼图的Palette当中,自然还需要给饼图指定几种常用的颜色。具体解决方案如下:
private void setPieColors()//设置饼状图颜色
{
ResourceDictionaryCollection palette = new ResourceDictionaryCollection();
string[] colors = { "#40699C", "#9E413E", "#7F9A48", "#695185", "#3C8DA3", "#CC7B38" };
for (int i = 0; i < colors.Length; i++)
{
System.Windows.Style style = new System.Windows.Style(typeof(Control));
style.Setters.Add(new Setter(Chart.BackgroundProperty, new SolidColorBrush((Color)ColorConverter.ConvertFromString((colors[i])))));
style.Setters.Add(new Setter(Chart.TemplateProperty, (System.Windows.Controls.ControlTemplate)(new stylesWindow()).FindResource("pi")));
ResourceDictionary dictionary = new ResourceDictionary();
dictionary.Add("DataPointStyle", style);
palette.Add(dictionary);
}
newChart.Palette = palette;
}
效果图如下:
2.2 时间轴解决方案
直接加上xy轴就行了,代码如下
IAxis datetimeAxis = new DateTimeAxis { Orientation = AxisOrientation.Y, ShowGridLines = true, Title = xName, IntervalType = DateTimeIntervalType.Auto };
IAxis valueAxis = new LinearAxis { Orientation = AxisOrientation.X, ShowGridLines = false, Title = yName };
newChart.Axes.Add(datetimeAxis);
newChart.Axes.Add(valueAxis);
时间会随着datatable中时间的区间动态调整,在此就不附图了,在上面相关的图中可以看到横轴的坐标。
3 关于代码。
不清楚能不能添加附件,下面把charting的源码先附上,如果没有附件的话,大家可以搜索wpf 柱状图 饼状图 tooltip,找到本人的相关下载(http://download.csdn.net/detail/luozuolincool/5997529)。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Windows.Data;
using System.Windows.Controls.DataVisualization.Charting;
using System.Windows.Media;
using System.Windows.Controls;
using System.Windows.Controls.DataVisualization;
using System.Windows;
namespace WpfApplication35
{
/// <summary>
/// 绘制图形枚举。
/// </summary>
public enum GraphEnum : int
{
/// <summary>
/// 区域图形。
/// </summary>
Area = 1,
/// <summary>
/// 条形图形。
/// </summary>
Bar = 2,
/// <summary>
/// 气泡图形。
/// </summary>
Bubble = 3,
/// <summary>
/// 柱图形。
/// </summary>
Column = 4,
/// <summary>
/// 线图形。
/// </summary>
Line = 5,
/// <summary>
/// 饼图形。
/// </summary>
Pie = 6,
/// <summary>
/// 散点图形
/// </summary>
Scatter = 7,
}
/// <summary>
/// DataGrid数据生成图形信息操作类。
/// </summary>
public class CreateToolKit
{ /// <summary>
/// Chart的基本宽度。
/// </summary>
private double KitWitdth = 0;
/// <summary>
/// Chart的基本高度。
/// </summary>
private double KitHeight = 0;
/// <summary>
/// 绘制图表类。
/// </summary>
public Chart newChart = null;
/// <summary>
/// 设置图形表的基本属性内容。
/// </summary>
/// <param name="width"></param>
/// <param name="height"></param>
private int countSeries = 0;
private GraphEnum graph = GraphEnum.Line;
private Boolean datetimeAxisIsDateTime = false;
private string xName = null, yName = null;
public CreateToolKit(double width, double height)
{
createChart(width, height, null, null, false);
}
public CreateToolKit(double width, double height, string xName, string yName, Boolean datetimeAxisIsDateTime)
{
createChart(width, height, xName, yName, datetimeAxisIsDateTime);
}
private void createChart(double width, double height, string xName, string yName, Boolean datetimeAxisIsDateTime)
{
this.KitHeight = height;
this.KitWitdth = width;
this.newChart = new Chart();
this.newChart.Width = this.KitWitdth;
this.newChart.Height = this.KitHeight;
this.newChart.LegendTitle = null;
this.newChart.Background = Brushes.Azure;
this.newChart.Title = "统计报表";
newChart.LegendTitle = "图例说明";
this.xName = xName;
this.yName = yName;
this.datetimeAxisIsDateTime = datetimeAxisIsDateTime;
}
#region================实现方法=====================
/// <summary>
/// 生成指定图形的表。
/// </summary>
/// <param name="table">表数据。</param>
/// <param name="graph">绘制图形类型。</param>
/// <returns></returns>
public Chart GetNewChart(DataTable table, GraphEnum graph)
{
try
{
this.graph = graph;
switch (graph)
{
case GraphEnum.Area:
newChart.Series.Add(SetDrawData<AreaSeries>(table));
break;
case GraphEnum.Bar:
newChart.Series.Add(SetDrawData<BarSeries>(table));
break;
case GraphEnum.Bubble:
newChart.Series.Add(SetDrawData<BubbleSeries>(table));
break;
case GraphEnum.Column:
newChart.Series.Add(SetDrawData<ColumnSeries>(table));
break;
case GraphEnum.Line:
newChart.Series.Add(SetDrawData<LineSeries>(table));
break;
case GraphEnum.Pie:
newChart.Series.Add(SetDrawData<PieSeries>(table));
break;
case GraphEnum.Scatter:
newChart.Series.Add(SetDrawData<ScatterSeries>(table));
break;
}
if (countSeries == 0)
{
while (newChart.Axes.Count > 0)
{
newChart.Axes.RemoveAt(0);
}
if (graph == GraphEnum.Bar)
{
if (datetimeAxisIsDateTime)
{
IAxis datetimeAxis = new DateTimeAxis { Orientation = AxisOrientation.Y, ShowGridLines = true, Title = xName, IntervalType = DateTimeIntervalType.Auto };
IAxis valueAxis = new LinearAxis { Orientation = AxisOrientation.X, ShowGridLines = false, Title = yName };
newChart.Axes.Add(datetimeAxis);
newChart.Axes.Add(valueAxis);
}
else
{
IAxis datetimeAxis = new CategoryAxis { Orientation = AxisOrientation.Y, ShowGridLines = false, Title = xName };
IAxis valueAxis = new LinearAxis { Orientation = AxisOrientation.X, ShowGridLines = false, Title = yName };
newChart.Axes.Add(datetimeAxis);
newChart.Axes.Add(valueAxis);
}
}
else if (graph == GraphEnum.Pie)
{
setPieColors();
}
else
{
if (datetimeAxisIsDateTime)
{
IAxis datetimeAxis = new DateTimeAxis { Orientation = AxisOrientation.X, ShowGridLines = true, Title = xName, IntervalType = DateTimeIntervalType.Auto };
IAxis valueAxis = new LinearAxis { Orientation = AxisOrientation.Y, ShowGridLines = false, Title = yName };
newChart.Axes.Add(datetimeAxis);
newChart.Axes.Add(valueAxis);
}
else
{
IAxis s = new CategoryAxis { Orientation = AxisOrientation.X, ShowGridLines = true, Title = xName };
IAxis datetimeAxis = new LinearAxis { Orientation = AxisOrientation.X, ShowGridLines = false, Title = xName };
IAxis valueAxis = new LinearAxis { Orientation = AxisOrientation.Y, ShowGridLines = false, Title = yName };
newChart.Axes.Add(s);
newChart.Axes.Add(valueAxis);
}
}
}
countSeries++;
return newChart;
}
catch (Exception TableEx)
{
//错误是计算的数值有非数字时,就可能出错,返回空值!
return null;
}
}
private void setPieColors()//设置饼状图颜色
{
ResourceDictionaryCollection palette = new ResourceDictionaryCollection();
string[] colors = { "#40699C", "#9E413E", "#7F9A48", "#695185", "#3C8DA3", "#CC7B38" };
for (int i = 0; i < colors.Length; i++)
{
System.Windows.Style style = new System.Windows.Style(typeof(Control));
style.Setters.Add(new Setter(Chart.BackgroundProperty, new SolidColorBrush((Color)ColorConverter.ConvertFromString((colors[i])))));
style.Setters.Add(new Setter(Chart.TemplateProperty, (System.Windows.Controls.ControlTemplate)(new stylesWindow()).FindResource("pi")));
ResourceDictionary dictionary = new ResourceDictionary();
dictionary.Add("DataPointStyle", style);
palette.Add(dictionary);
}
newChart.Palette = palette;
}
/// <summary>
/// 对图形进行值的添加。
/// </summary>
/// <param name="table">数据源。</param>
/// <returns>图形对象。</returns>
private T SetDrawData<T>(DataTable table) where T : new()
{
Binding KeyBind = new Binding("Key");
Binding ValueBind = new Binding("Value");
T ret = new T();
Type obj = ret.GetType();
obj.GetProperty("Title").SetValue(ret, table.TableName, null);
obj.GetProperty("IndependentValueBinding").SetValue(ret, KeyBind, null);
obj.GetProperty("DependentValueBinding").SetValue(ret, ValueBind, null);
if (graph != GraphEnum.Pie)
{
int n = countSeries % 6;
if (graph == GraphEnum.Area || graph == GraphEnum.Bar || graph == GraphEnum.Column)
{
System.Windows.Style s1 = (System.Windows.Style)(new stylesWindow()).FindResource("DataPointStyle" + n);
obj.GetProperty("DataPointStyle").SetValue(ret, s1, null);
}
else
{
System.Windows.Style s1 = (System.Windows.Style)(new stylesWindow()).FindResource("DataPointEllipseStyle" + n);
obj.GetProperty("DataPointStyle").SetValue(ret, s1, null);
}
}
if (datetimeAxisIsDateTime)
{
obj.GetProperty("ItemsSource").SetValue(ret, GetTableDataDateTime(table), null);//横轴为string类型
}
else
{
obj.GetProperty("ItemsSource").SetValue(ret, GetTableDataString(table), null);//横轴为Datetime类型
}
return ret;
}
/// <summary>
/// 对数据进行标准转换,转成图形能加载的形式。
/// 【DataTable转换成Dictionary】,是按列的统计来添加数据。
/// </summary>
/// <param name="table">DataTable数据源。</param>
/// <returns></returns>
private Dictionary<string, double> GetTableDataString(DataTable table)
{
Dictionary<string, double> ReDict = new Dictionary<string, double>();
double tempDouble = 0;
foreach (DataRow row in table.Rows)
{
tempDouble = 0;
if (!string.IsNullOrEmpty(row[1].ToString()) && isDecimal(row[1].ToString()))
tempDouble += Convert.ToDouble(row[1]);
ReDict.Add(row[0].ToString(), tempDouble);
}
return ReDict;
}
private Dictionary<DateTime, double> GetTableDataDateTime(DataTable table)
{
Dictionary<DateTime, double> ReDict = new Dictionary<DateTime, double>();
double tempDouble = 0;
foreach (DataRow row in table.Rows)
{
tempDouble = 0;
if (!string.IsNullOrEmpty(row[1].ToString()) && isDecimal(row[1].ToString()))
tempDouble += Convert.ToDouble(row[1]);
ReDict.Add(Convert.ToDateTime(row[0].ToString()), tempDouble);
}
return ReDict;
}
private Boolean isDecimal(string data)//判断是否是decimal,在此处简化处理
{
return true;
}
#endregion
}
}