wpf柱状图等相关技术

时间:2021-06-25 15:07:40

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>

效果如图所示

wpf柱状图等相关技术

在我的程序中需要添加这样的六个点样式,这样不同的系列就有六种颜色可供选择。

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>

效果如下图所示

wpf柱状图等相关技术

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;
        }

效果图如下:

wpf柱状图等相关技术

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
    }
}