WPF实现折线图

389c98dcf0c9193b272c064051d1e73a.png

01

代码如下

一、创建 LineChartExample.xaml 继承 UserControl代码如下。

31a0e843cfa8f046a565c6e0d372c307.png

<UserControl x:Class="WPFDevelopers.Samples.ExampleViews.LineChartExample"
             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" 
             xmlns:local="clr-namespace:WPFDevelopers.Samples.ExampleViews"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800"
             PreviewMouseDown="UserControl_PreviewMouseDown"
             Loaded="UserControl_Loaded">
    <Viewbox>
        <Grid Width="800" Height="600">
            <Grid x:Name="gArea"
              Background="{StaticResource ExtralightSolidColorBrush}"
              Margin="50,20,20,20">
            </Grid>
            <Popup Name="pop1"
               AllowsTransparency="True"
               IsOpen="False"
               Placement="Relative"
               StaysOpen="True">
                <Border Effect="{StaticResource NormalShadowDepth}"
                    Background="{StaticResource WhiteSolidColorBrush}"
                    Margin="10">
                    <Grid x:Name="gV"
                  Width="200"
                  Height="43">
                        <TextBlock x:Name="tbT"
                           Foreground="#0C0C26"
                           FontSize="14"
                           Margin="4,4,0,0"
                           HorizontalAlignment="Left"
                           VerticalAlignment="Top"
                           Text="" />
                        <TextBlock x:Name="tbV"
                           Foreground="#0C0C26"
                           FontSize="14"
                           Margin="4,0,0,4"
                           HorizontalAlignment="Left"
                           VerticalAlignment="Bottom"
                           Text="" />
                    </Grid>
                </Border>
            </Popup>
    </Grid>
    </Viewbox>


</UserControl>

二、创建LineChartExample.xaml.cs代码如下

7f85ba8764d063d0da69d558ea2782cc.png

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;


namespace WPFDevelopers.Samples.ExampleViews
{
    /// <summary>
    /// LineChartExample.xaml 的交互逻辑
    /// </summary>
    public partial class LineChartExample : UserControl
    {
        double maxYMarginTop = 20D;//Y轴上方的空白间距
        double yMarginLeft = 30D;//Y轴左侧,用来显示刻度文字的空白间距


        double yNum = 10D;//Y轴的刻度个数
        int yNumInt = 10;//Y轴的刻度个数


        double minY = 10D;//Y轴最小值
        double maxY = 110D;//Y轴最大值
        double yInterval = 10D;//Y轴刻度


        double xMarginBottom = 20D;//X轴下侧,用来显示刻度文字的空白间距


        double xNum = 25D;//x轴的刻度个数
        int xNumInt = 25;//x轴的刻度个数
        double xIntervalLineHeight = 10D;//X轴刻度线的高度
        DateTime minX = Convert.ToDateTime("2021-07-14 00:00:00");//X轴最小值
        DateTime maxX = Convert.ToDateTime("2021-07-15 00:00:00");//X轴最大值
        public LineChartExample()
        {
            InitializeComponent();
        }


        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            gArea.Children.Clear();


            //X轴刻度值,因为要留出左右空白,所以画的时候多画一格,实际计算减1
            double timeInterval = (maxX.ToOADate() - minX.ToOADate()) / (xNum - 1);


            //折线数据
            Dictionary<DateTime, int> csource = new Dictionary<DateTime, int>();
            csource.Add(Convert.ToDateTime("2021-07-14 09:00:00"), 23);
            csource.Add(Convert.ToDateTime("2021-07-14 10:00:00"), 46);
            csource.Add(Convert.ToDateTime("2021-07-14 11:00:00"), 18);
            csource.Add(Convert.ToDateTime("2021-07-14 12:00:00"), 88);
            csource.Add(Convert.ToDateTime("2021-07-14 13:00:00"), 34);
            csource.Add(Convert.ToDateTime("2021-07-14 14:00:00"), 45);
            csource.Add(Convert.ToDateTime("2021-07-14 15:00:00"), 61);
            csource.Add(Convert.ToDateTime("2021-07-14 16:00:00"), 33);
            csource.Add(Convert.ToDateTime("2021-07-14 17:00:00"), 78);
            csource.Add(Convert.ToDateTime("2021-07-14 18:00:00"), 90);
            csource.Add(Convert.ToDateTime("2021-07-14 19:00:00"), 18);
            csource.Add(Convert.ToDateTime("2021-07-14 20:00:00"), 50);


            //折线数据
            Dictionary<DateTime, int> bsource = new Dictionary<DateTime, int>();
            bsource.Add(Convert.ToDateTime("2021-07-14 09:00:00"), 66);
            bsource.Add(Convert.ToDateTime("2021-07-14 10:00:00"), 56);
            bsource.Add(Convert.ToDateTime("2021-07-14 11:00:00"), 87);
            bsource.Add(Convert.ToDateTime("2021-07-14 12:00:00"), 33);
            bsource.Add(Convert.ToDateTime("2021-07-14 13:00:00"), 54);
            bsource.Add(Convert.ToDateTime("2021-07-14 14:00:00"), 27);
            bsource.Add(Convert.ToDateTime("2021-07-14 15:00:00"), 80);
            bsource.Add(Convert.ToDateTime("2021-07-14 16:00:00"), 23);
            bsource.Add(Convert.ToDateTime("2021-07-14 17:00:00"), 43);
            bsource.Add(Convert.ToDateTime("2021-07-14 18:00:00"), 77);
            bsource.Add(Convert.ToDateTime("2021-07-14 19:00:00"), 56);
            bsource.Add(Convert.ToDateTime("2021-07-14 20:00:00"), 43);




            //画X轴
            Rectangle rx = new Rectangle();
            rx.Height = 1;
            rx.Stroke = new SolidColorBrush(Color.FromRgb(128, 128, 142));
            rx.HorizontalAlignment = HorizontalAlignment.Stretch;
            rx.VerticalAlignment = VerticalAlignment.Bottom;
            gArea.Children.Add(rx);


            //画Y轴
            Rectangle ry = new Rectangle();
            ry.Width = 1;
            ry.Stroke = new SolidColorBrush(Color.FromRgb(128, 128, 142));
            ry.HorizontalAlignment = HorizontalAlignment.Left;
            ry.VerticalAlignment = VerticalAlignment.Stretch;
            gArea.Children.Add(ry);


            //画Y轴横虚线
            for (int i = 0; i < yNumInt; i++)
            {
                Line ly = new Line();
                ly.X1 = 0;
                ly.Y1 = ((gArea.ActualHeight - maxYMarginTop) / yNum * i) + maxYMarginTop;
                ly.X2 = gArea.ActualWidth;
                ly.Y2 = ((gArea.ActualHeight - maxYMarginTop) / yNum * i) + maxYMarginTop;
                ly.Stroke = new SolidColorBrush(Color.FromRgb(73, 73, 91));
                ly.StrokeThickness = 1;
                ly.StrokeDashArray = new DoubleCollection() { 10, 15 };//实线的长度和间距


                TextBlock tb = new TextBlock();
                tb.FontSize = 12;
                tb.Foreground = new SolidColorBrush(Color.FromRgb(128, 128, 142));
                tb.Text = (maxY - (i * yInterval)).ToString();
                tb.VerticalAlignment = VerticalAlignment.Top;
                tb.HorizontalAlignment = HorizontalAlignment.Left;
                tb.Margin = new Thickness(-yMarginLeft, (((gArea.ActualHeight - maxYMarginTop) / yNum * i) + maxYMarginTop) - (GetFontSizeByTextBlock(tb).Height / 2D), 0, 0);
                gArea.Children.Add(tb);
                gArea.Children.Add(ly);
            }


            //画X轴刻度线
            for (int i = 0; i < xNumInt; i++)
            {
                Line ly = new Line();
                ly.X1 = (gArea.ActualWidth / xNum * i) + ((gArea.ActualWidth / xNum) / 2D);
                ly.Y1 = gArea.ActualHeight;
                ly.X2 = (gArea.ActualWidth / xNum * i) + ((gArea.ActualWidth / xNum) / 2D);
                ly.Y2 = gArea.ActualHeight - xIntervalLineHeight;
                ly.Stroke = new SolidColorBrush(Color.FromRgb(128, 128, 142));
                ly.StrokeThickness = 1;


                if (i % 2 == 0)//两个刻度显示一次文字
                {
                    TextBlock tb = new TextBlock();
                    tb.FontSize = 12;
                    tb.Foreground = new SolidColorBrush(Color.FromRgb(128, 128, 142));
                    if (i < 10)//个位数补零
                    {
                        tb.Text = "0" + i.ToString() + ":00";
                    }
                    else
                    {
                        tb.Text = i.ToString() + ":00";
                    }
                    tb.VerticalAlignment = VerticalAlignment.Bottom;
                    tb.HorizontalAlignment = HorizontalAlignment.Left;
                    tb.Margin = new Thickness((gArea.ActualWidth / xNum * i) + ((gArea.ActualWidth / xNum) / 2D) - (GetFontSizeByTextBlock(tb).Width / 2D), 0, 0, -xMarginBottom);
                    gArea.Children.Add(tb);
                }


                gArea.Children.Add(ly);
            }


            Polyline plView = new Polyline();
            plView.Stroke = new SolidColorBrush(Color.FromRgb(66, 120, 255));
            plView.StrokeThickness = 1;
            plView.StrokeLineJoin = PenLineJoin.Round;
            plView.IsHitTestVisible = false;
            foreach (DateTime k in csource.Keys)
            {
                Point p = new Point();
                p.X = (gArea.ActualWidth / xNum * ((k.ToOADate() - minX.ToOADate()) / timeInterval)) + ((gArea.ActualWidth / xNum) / 2D);
                p.Y = ((gArea.ActualHeight - maxYMarginTop) / (maxY - minY) * (maxY - csource[k])) + maxYMarginTop;
                plView.Points.Add(p);


                Ellipse ep = new Ellipse();
                ep.Width = 10;
                ep.Height = 10;
                ep.Stroke = new SolidColorBrush(Color.FromRgb(230, 230, 236));
                ep.StrokeThickness = 0;
                ep.Fill = new SolidColorBrush(Color.FromRgb(66, 120, 255));
                ep.HorizontalAlignment = HorizontalAlignment.Left;
                ep.VerticalAlignment = VerticalAlignment.Top;
                ep.Margin = new Thickness(p.X - 5, p.Y - 5, 0, -5);//底部设置-5(圆球宽高的一半),当Y等于0的时候,可以让圆球全部显示
                ep.MouseEnter += Ep_MouseEnter;
                ep.MouseLeave += Ep_MouseLeave;
                ep.MouseLeftButtonDown += Ep_MouseLeftButtonDown;
                ep.Tag = k + "|" + csource[k];
                gArea.Children.Add(ep);
            }
            gArea.Children.Add(plView);




            Polyline plPeople = new Polyline();
            plPeople.Stroke = new SolidColorBrush(Color.FromRgb(27, 221, 58));
            plPeople.StrokeThickness = 1;
            plPeople.StrokeLineJoin = PenLineJoin.Round;
            plPeople.IsHitTestVisible = false;
            foreach (DateTime k in bsource.Keys)
            {
                Point p = new Point();
                p.X = (gArea.ActualWidth / xNum * ((k.ToOADate() - minX.ToOADate()) / timeInterval)) + ((gArea.ActualWidth / xNum) / 2D);
                p.Y = ((gArea.ActualHeight - maxYMarginTop) / (maxY - minY) * (maxY - bsource[k])) + maxYMarginTop;
                plPeople.Points.Add(p);


                Ellipse ep = new Ellipse();
                ep.Width = 10;
                ep.Height = 10;
                ep.Stroke = new SolidColorBrush(Color.FromRgb(230, 230, 236));
                ep.StrokeThickness = 0;
                ep.Fill = new SolidColorBrush(Color.FromRgb(27, 221, 58));
                ep.HorizontalAlignment = HorizontalAlignment.Left;
                ep.VerticalAlignment = VerticalAlignment.Top;
                ep.Margin = new Thickness(p.X - 5, p.Y - 5, 0, -5);//底部设置-5(圆球宽高的一半),当Y等于0的时候,可以让圆球全部显示
                ep.MouseEnter += Ep_MouseEnter;
                ep.MouseLeave += Ep_MouseLeave;
                ep.MouseLeftButtonDown += Ep_MouseLeftButtonDown;
                ep.Tag = k + "|" + bsource[k];
                gArea.Children.Add(ep);
            }
            gArea.Children.Add(plPeople);
        }
        private void Ep_MouseLeave(object sender, MouseEventArgs e)
        {
            Ellipse ep = sender as Ellipse;
            ep.StrokeThickness = 0;
        }


        private void Ep_MouseEnter(object sender, MouseEventArgs e)
        {
            Ellipse ep = sender as Ellipse;
            ep.StrokeThickness = 2;
        }
        private void Ep_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Ellipse ep = sender as Ellipse;
            string[] ts = ep.Tag.ToString().Split('|');


            pop1.PlacementTarget = ep;
            pop1.VerticalOffset = -46;
            pop1.HorizontalOffset = -26;
            tbT.Text = "时间:" + ts[0];
            tbV.Text = "数量:" + ts[1];
            pop1.IsOpen = true;
        }


        //测量文本框的尺寸
        public static Size GetFontSizeByTextBlock(TextBlock tb)
        {
            var formattedText = new FormattedText(
               tb.Text,
               CultureInfo.CurrentUICulture,
               FlowDirection.LeftToRight,
               new Typeface(tb.FontFamily, tb.FontStyle, tb.FontWeight, tb.FontStretch),
               tb.FontSize,
               tb.Foreground
               );
            Size size = new Size(formattedText.Width, formattedText.Height);
            return size;
        }
        private void UserControl_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            if (pop1.IsOpen)
            {
                pop1.IsOpen = false;
            }
        }


    }
}

02


效果预览

鸣谢素材提供者 - 刘开双

源码地址如下

github:https://github.com/yanjinhuagood/WPFDevelopers.git

gitee:https://gitee.com/yanjinhua/WPFDevelopers.git

WPF开发者QQ群: 340500857 

blogs: https://www.cnblogs.com/yanjinhua

Github:https://github.com/yanjinhuagood

出处:https://www.cnblogs.com/yanjinhua

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

转载请著名作者 出处 https://github.com/yanjinhuagood

技术群:添加小编微信并备注进群

小编微信:mm1552923   

公众号:dotNet编程大全    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值