WPF 曲线图表控件(自制)(二)

简介: 原文:WPF 曲线图表控件(自制)(二) 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/koloumi/article/details/77521872 如果还有不懂的请去资源区下载控件包含所有源码 http://download.csdn.net/download/koloumi/9947692 接下来将代码上的。
原文: WPF 曲线图表控件(自制)(二)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/koloumi/article/details/77521872
    如果还有不懂的请去资源区下载控件包含所有源码

http://download.csdn.net/download/koloumi/9947692
接下来将代码上的。
界面调整部分的代码函数
这里写图片描述

    还有一个重要的函数就是控件的MyChart_SizeChanged事件。
/// <summary>
        /// 尺寸改变
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MyChart_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            Size newsize = e.NewSize;
            if(!e.WidthChanged)
            {
                this.Width = newsize.Width;
            }
            if(!e.HeightChanged)
            {
                this.Height = newsize.Height;
            }
            AdjustSize();
            AdjustScale();
            AdjustLines();
            AdjustLinesAndPoints();
        }
    接下来是先看下定义的属性:
/// <summary>
        /// 数据集
        /// </summary>
        private Dictionary<string, DrawData> _data = new Dictionary<string, DrawData>();
        /// <summary>
        /// 坐标原点
        /// </summary>
        private Point _origPoint = new Point();
        /// <summary>
        /// X轴上的文字
        /// </summary>
        private List<TextBlock> _xTip = new List<TextBlock>();
        /// <summary>
        /// Y轴上的文字
        /// </summary>
        private List<TextBlock> _yTip = new List<TextBlock>();
        /// <summary>
        /// X轴的区间
        /// </summary>
        private VectorChart2 _xLimt = new VectorChart2(double.MaxValue, double.MinValue);
        /// <summary>
        /// Y轴的区间
        /// </summary>
        private VectorChart2 _yLimt = new VectorChart2(double.MaxValue, double.MinValue);
        /// <summary>
        /// 每个像素映射为多少距离
        /// </summary>
        private VectorChart2 _everyDisForPiexl = new VectorChart2();
        /// <summary>
        /// 缩放中心点的偏移
        /// </summary>
        private VectorChart2 _centerOffect = new VectorChart2(0, 0);
        /// <summary>
        /// 当前线的缩放比例
        /// </summary>
        private Vector4 _currentLinesScale = new Vector4(1, 1, 1, 1);
        /// <summary>
        /// 每个刻度之间的间隔
        /// </summary>
        private static double _everyDis = 30;
    在这个控件上我们将每个刻度的间隔直接定死,这样有需要的时候直接修改刻度上的数值,而不用去移动刻度。如果尺寸变大只要增加刻度线,减少反之。
    在控件中用到的数据结构
/// <summary>
    /// 向量
    /// </summary>
    public struct VectorChart2
    {
        public double Vec1;

        public double Vec2;

        public VectorChart2(double v1, double v2)
        {
            this.Vec1 = v1;
            this.Vec2 = v2;
        }
        /// <summary>
        /// Point 转换VectorChart2
        /// </summary>
        /// <param name="p"></param>
        public static implicit operator VectorChart2(Point p)
        {
            return new VectorChart2(p.X, p.Y);
        }
        /// <summary>
        /// 乘法
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="ratio"></param>
        /// <returns></returns>
        public static VectorChart2 operator * (VectorChart2 v1, double ratio)
        {
            return new VectorChart2(v1.Vec1 * ratio, v1.Vec2 * ratio);
        }
    }
    /// <summary>
    /// 4维向量
    /// </summary>
    public struct Vector4
    {
        /// <summary>
        ///
        /// </summary>
        public static Vector4 Empty = new Vector4() { _isEmpty = true };

        public double Vec1;

        public double Vec2;

        public double Vec3;

        public double Vec4;
        /// <summary>
        /// 是否为空数据
        /// </summary>
        private bool _isEmpty;

        public Vector4(double Vec1, double Vec2, double Vec3, double Vec4)
        {
            this.Vec1 = Vec1;
            this.Vec2 = Vec2;
            this.Vec3 = Vec3;
            this.Vec4 = Vec4;
            _isEmpty = false;
        }

        /// <summary>
        /// 重载等号运算符
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        public static bool operator ==(Vector4 v1, Vector4 v2)
        {
            if (v1.Vec1 == v2.Vec1 && v1.Vec2 == v2.Vec2 && v1.Vec3 == v2.Vec3 && v1.Vec4 == v2.Vec4 && v1._isEmpty == v2._isEmpty)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        /// <summary>
        /// 重载
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        public static bool operator !=(Vector4 v1, Vector4 v2)
        {
            return !(v1 == v2);
        }

        public override bool Equals(object obj)
        {
            return base.Equals(obj);
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
    }
    /// <summary>
    /// 绘图数据
    /// </summary>
    public class DrawData : IDisposable
    {
        /// <summary>
        /// 线
        /// </summary>
        public Polyline Line;
        /// <summary>
        /// 线的标记点
        /// </summary>
        public List<Ellipse> Points = new List<Ellipse>();
        /// <summary>
        /// 源数据集合
        /// </summary>
        public ObservableCollection<Point> Ps = null;
        /// <summary>
        /// 极值
        /// </summary>
        public Vector4 Vec4 = Vector4.Empty;
        /// <summary>
        /// 提示
        /// </summary>
        public LineTitle LineTitle = new LineTitle();


        public DrawData()
        {

        }

        ~DrawData()
        {
            this.Dispose();
        }
        #region IDisposable Support
        private bool disposedValue = false; // 要检测冗余调用

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    // TODO: 释放托管状态(托管对象)。
                    Line = null;
                    Ps = null;
                    Points = null;
                    LineTitle = null;
                }

                // TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。
                // TODO: 将大型字段设置为 null。

                disposedValue = true;
            }
        }

        // TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。
        // ~DrawData() {
        //   // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
        //   Dispose(false);
        // }

        // 添加此代码以正确实现可处置模式。
        public void Dispose()
        {
            // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
            Dispose(true);
            // TODO: 如果在以上内容中替代了终结器,则取消注释以下行。
            GC.SuppressFinalize(this);
        }
        #endregion
    }
调整曲线 刻度(直接修改)部分:

曲线部分

然后是调整所有绘图区的尺寸:

这里写图片描述

极值的调整 这里乘以0.15 是为了扩大上限 让初始曲线能够包括在视图中。

这里写图片描述

还有曲线刻度的添加式修改:(因为增加删除速度比较慢,所以只在有必要的时候调用这个,比如尺寸改变的时候,平时直接修改刻度即可)
/// <summary>
        /// 调整刻度\网格线(删除重新添加)
        /// </summary>
        protected void AdjustScale()
        {
            //if (!IsHaveLine) return;
            if (X_Axis.Width == 0 || X_Axis.Height == 0 || X_Axis.Width == double.NaN || X_Axis.Height == double.NaN) return;
            //if (GridLines.Width == 0 || GridLines.Height == 0 || GridLines.Width == double.NaN || double.IsNaN(GridLines.Height)) return;
            ///清除X轴的刻度
            if (X_Axis.Children.Count > 1)
            {
                X_Axis.Children.RemoveRange(1, X_Axis.Children.Count - 1);
            }
            ///清除Y轴的刻度
            if (Y_Axis.Children.Count > 1)
            {
                Y_Axis.Children.RemoveRange(1, Y_Axis.Children.Count - 1);
            }
            ///清除网格线
            GridLines.Children.Clear();
            ///清除刻度线的提示文字
            _xTip.Clear();
            _yTip.Clear();

            ///添加X轴的
            for (double i = _origPoint.X; i < X_Axis.Width; i += _everyDis)
            {
                Line x_scale = new Line();
                x_scale.StrokeThickness = 2;
                x_scale.Stroke = new SolidColorBrush(Colors.Black);
                x_scale.StrokeStartLineCap = PenLineCap.Round;
                x_scale.StrokeEndLineCap = PenLineCap.Round;
                x_scale.Width = 4;
                x_scale.Height = 20;
                x_scale.X1 = 2;
                x_scale.Y1 = 4;
                x_scale.X2 = 2;
                x_scale.Y2 = x_scale.Height;
                Canvas.SetLeft(x_scale, i);
                Canvas.SetTop(x_scale, 0);
                X_Axis.Children.Add(x_scale);
                ///网格线
                GridLines.Children.Add(GetGridLine(new Point(i - 2, 0), false, GridLinesArea.Vec2));
                //double.IsNaN(GridLines.Height) ? GridLines.ActualHeight : GridLines.Height
                ///添加提示文字
                TextBlock block = new TextBlock();
                block.FontSize = 10;
                block.Text = i.ToString();
                Canvas.SetLeft(block, i);
                Canvas.SetTop(block, 25);
                _xTip.Add(block);
                X_Axis.Children.Add(block);
            }
            ///添加Y轴的
            for (double i = _origPoint.Y; i < Y_Axis.Height; i += _everyDis)
            {
                Line y_scale = new Line();
                y_scale.StrokeThickness = 2;
                y_scale.Stroke = new SolidColorBrush(Colors.Black);
                y_scale.StrokeStartLineCap = PenLineCap.Round;
                y_scale.StrokeEndLineCap = PenLineCap.Round;
                y_scale.Width = 20;
                y_scale.Height = 4;
                y_scale.X1 = 4;
                y_scale.Y1 = 2;
                y_scale.X2 = y_scale.Width;
                y_scale.Y2 = 2;
                Canvas.SetBottom(y_scale, i + 1);
                Canvas.SetRight(y_scale, 2);
                Y_Axis.Children.Add(y_scale);
                ///网格线
                GridLines.Children.Add(GetGridLine(new Point(i - 2, 0), true, GridLinesArea.Vec1));
                //double.IsNaN(GridLines.Width) ? GridLines.ActualWidth : GridLines.Width)
                ///添加提示文字
                TextBlock block = new TextBlock();
                block.FontSize = 10;
                block.Text = i.ToString();
                Canvas.SetBottom(block, i);
                Canvas.SetRight(block, 10);
                _yTip.Add(block);
                Y_Axis.Children.Add(block);
            }

        }
调整线和点:(主要是,缩放和拖动)

这里写图片描述

调整线和点的大小比例AdjustLinesAndPointsSize 这个函数可以删掉,后来发现没有用。

鼠标滚轮事件:
/// <summary>
        /// 鼠标滚轮事件
        /// </summary>
        /// <param name="e"></param>
        protected override void OnMouseWheel(MouseWheelEventArgs e)
        {
            base.OnMouseWheel(e);
            VectorChart2 curPDraw = (VectorChart2)e.GetPosition(Draw);
            VectorChart2 curPLinesAndPoint = (VectorChart2)e.GetPosition(DrawLineAndPoint);

            double delta = 1;
            if(e.Delta >= 120)
            {
                delta = 1.2;
            }
            else if(e.Delta <= -120)
            {
                delta = (double)5 / (double)6;
            }
            if (IsXZoom)
            {
                DrawLineAndPoint.Width *= delta;
                curPDraw.Vec1 *= delta;
                curPLinesAndPoint.Vec1 *= delta;
            }
            if (IsYZoom)
            {
                DrawLineAndPoint.Height *= delta;
                curPDraw.Vec2 *= delta;
                curPLinesAndPoint.Vec2 *= delta;
            }

            _currentLinesScale.Vec4 *= delta;
            //Canvas.SetLeft(DrawLineAndPoint, curPDraw.Vec1 - curPLinesAndPoint.Vec1);
            //Canvas.SetBottom(DrawLineAndPoint, -(DrawArea.Vec2 - (curPLinesAndPoint.Vec2 - curPDraw.Vec2)));

            ///调整刻度
            AdjustLines();

            AdjustLinesAndPoints();
        }
重置按钮:

这里写图片描述
鼠标移动的时候计算移动的差值然后调用该函数:
这里写图片描述
获取偏移量函数:

/// <summary>
        /// 获取偏移量
        /// </summary>
        /// <returns></returns>
        protected VectorChart2 GetOffectValue(VectorChart2 offectValue)
        {
            return new VectorChart2( -offectValue.Vec1 * _everyDisForPiexl.Vec1, offectValue.Vec2 * _everyDisForPiexl.Vec2);
        }
至此全部结束 如果还有不懂的请去资源区下载控件包含所有源码

http://download.csdn.net/download/koloumi/9947692

目录
相关文章
|
6月前
|
C# 开发者 Windows
基于Material Design风格开源、易用、强大的WPF UI控件库
基于Material Design风格开源、易用、强大的WPF UI控件库
385 0
|
6月前
|
C#
浅谈WPF之装饰器实现控件锚点
使用过visio的都知道,在绘制流程图时,当选择或鼠标移动到控件时,都会在控件的四周出现锚点,以便于修改大小,移动位置,或连接线等,那此功能是如何实现的呢?在WPF开发中,想要在控件四周实现锚点,可以通过装饰器来实现,今天通过一个简单的小例子,简述如何在WPF开发中,应用装饰器,仅供学习分享使用,如有不足之处,还请指正。
138 1
|
C# Windows
WPF技术之RichTextBox控件
WPF RichTextBox是Windows Presentation Foundation (WPF)中提供的一个强大的文本编辑控件,它可以显示富文本格式的文本,支持多种文本处理操作。
595 0
|
3月前
|
开发框架 缓存 前端开发
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(11) -- 下拉列表的数据绑定以及自定义系统字典列表控件
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(11) -- 下拉列表的数据绑定以及自定义系统字典列表控件
|
3月前
|
C# 开发者 Windows
一款基于Fluent设计风格、现代化的WPF UI控件库
一款基于Fluent设计风格、现代化的WPF UI控件库
|
3月前
|
C# Windows
WPF中如何使用HandyCotrol控件库
WPF中如何使用HandyCotrol控件库
190 1
|
3月前
|
C# 前端开发 UED
WPF数据验证实战:内置控件与自定义规则,带你玩转前端数据验证,让你的应用程序更上一层楼!
【8月更文挑战第31天】在WPF应用开发中,数据验证是确保输入正确性的关键环节。前端验证能及时发现错误,提升用户体验和程序可靠性。本文对比了几种常用的WPF数据验证方法,并通过示例展示了如何使用内置验证控件(如`TextBox`)及自定义验证规则实现有效验证。内置控件结合`Validation`类可快速实现简单验证;自定义规则则提供了更灵活的复杂逻辑支持。希望本文能帮助开发者更好地进行WPF数据验证。
110 0
|
3月前
|
C# UED 定位技术
WPF控件大全:初学者必读,掌握控件使用技巧,让你的应用程序更上一层楼!
【8月更文挑战第31天】在WPF应用程序开发中,控件是实现用户界面交互的关键元素。WPF提供了丰富的控件库,包括基础控件(如`Button`、`TextBox`)、布局控件(如`StackPanel`、`Grid`)、数据绑定控件(如`ListBox`、`DataGrid`)等。本文将介绍这些控件的基本分类及使用技巧,并通过示例代码展示如何在项目中应用。合理选择控件并利用布局控件和数据绑定功能,可以提升用户体验和程序性能。
67 0
|
3月前
|
开发框架 前端开发 JavaScript
WPF应用开发之控件动态内容展示
WPF应用开发之控件动态内容展示
|
3月前
|
开发框架 前端开发 JavaScript
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(3)--自定义用户控件
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(3)--自定义用户控件