WPF 图表控件LiveCharts的应用——室内监控可视化

2023-11-04

需求

为了实现一个完整软件系统,必须具备一些基本的数据呈现控件,例如曲线图、柱状图、饼图等。本次的业务需求为:利用LiveCharts展示后台模拟的温度变化。像Winform里面,微软为我们提供了比较完整的Chart控件,但是在WPF组件中,就没有找到类似的控件,它的意图是让我们自己去实现。我们应该对当下的代码共享时代环抱感激,迄今位置有很多面向WPF的第三方控件库,大部分都是免费开源的。例如:OxyPlot、ModernuiCharts......以及我们今天的主角——LiveCharts。这是一个具备动画效果的图表控件。

首先上效果:

 

环境

Windows 10

Visual Studio 2019

.Net Framework 4.7.2

Ninject

LiveCharts

LiveCharts.Wpf

 

实现

1.通过NuGet引入Ninject、LiveCharts.Wpf、LiveCharts程序包。

解决方案资源管理器-->项目(右键)-->管理NuGet程序包

在“浏览”中搜索:

Ninject

LiveCharts

LiveCharts.Wpf

并安装,完成之后如下图所示:

 

2.在XAML中引用LiveCharts控件的程序集。

在ChamberView.xaml文件的节点上添加对程序集的应用,如下:

<local:AnimationPageBaseView x:Class="Deamon.View.ChamberView"
      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:Deamon.View"
      xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
      Title="ChamberView">

其中 xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"就是命名空间的引用。

3.使用LiveCharts控件,值用绑定的方式传递到界面。

                    <lvc:CartesianChart Grid.Row="1" AnimationsSpeed="0:0:0.2" Hoverable="False" DataTooltip="{x:Null}">
                        <lvc:CartesianChart.Series>
                            <lvc:LineSeries Values="{Binding ChartValues}" 
                                    PointGeometry="{x:Null}"
                                LineSmoothness="0"
                                StrokeThickness="3" 
                                Stroke="#12B1AD"
                                Fill="#E6F7F8"/>
                        </lvc:CartesianChart.Series>
                        <lvc:CartesianChart.AxisX>
                            <lvc:Axis LabelFormatter="{Binding DateTimeFormatter}" 
                          MaxValue="{Binding AxisMax}" 
                          MinValue="{Binding AxisMin}" ShowLabels="False"
                          Unit="{Binding AxisUnit}">
                                <lvc:Axis.Separator>
                                    <lvc:Separator Step="{Binding AxisStep}" />
                                </lvc:Axis.Separator>
                            </lvc:Axis>
                        </lvc:CartesianChart.AxisX>
                        <lvc:CartesianChart.AxisY>
                            <lvc:Axis ShowLabels="false"  />
                        </lvc:CartesianChart.AxisY>
                    </lvc:CartesianChart>

如上所示,我们这里有一些属性是通过绑定来赋值的。因此接下来我们来完善这块工作。

4.数据绑定与刷新

4.1定义一个数据点结构——MeasureModel

    /// <summary>
    /// 测量模型
    /// </summary>
    public class MeasureModel
    {
        /// <summary>
        /// X 轴数据
        /// </summary>
        public DateTime DateTime { get; set; }

        /// <summary>
        /// Y 轴数据
        /// </summary>
        public double Value { get; set; }
    }

4.2定义界面相关的视图模型ViewModel

定义通知属性、普通属性和命令以及构造时初始化等工作。

using Deamon.Util.DI;
using LiveCharts;
using LiveCharts.Configurations;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Deamon.ViewModel
{    
    /// <summary>
    /// 房间信息
    /// </summary>
    public class ChamberViewModel : BaseViewModel
    {
        /// <summary>
        /// 默认构造函数
        /// </summary>
        public ChamberViewModel()
        {
            // 初始化创建一个 X Y 轴上数据显示的图表,并确定 X Y 轴的数据结构
            var mapper = Mappers.Xy<MeasureModel>()
              .X(model => model.DateTime.Ticks) 
              .Y(model => model.Value);

            // 配置这个图表,可以被其他地方(特定的方式)使用
            Charting.For<MeasureModel>(mapper);

            // 初始化测量的数据集
            ChartValues = new ChartValues<MeasureModel>();

            // 设置 X 轴显示的标签格式
            DateTimeFormatter = value => new DateTime((long)value).ToString("mm:ss");

            // 设置图表的其他相关属性
            AxisStep = TimeSpan.FromSeconds(1).Ticks;
            AxisUnit = TimeSpan.TicksPerSecond;

            SetAxisLimits(DateTime.Now);

            // 默认情况下开始数据刷新
            IsReading = false;
            this.InjectStopOnClick();
        }

        #region 公共命令

        /// <summary>
        /// 显示导航栏命令
        /// </summary>
        public RelayCommand ShowBarCommand
        {
            get
            {
                return new RelayCommand(() =>
                {
                    IoC.Get<ApplicationViewModel>().HasNavigationBar = true;
                });
            }
        }

        /// <summary>
        /// 显示导航栏命令
        /// </summary>
        public RelayCommand ReadingCommand
        {
            get
            {
                return new RelayCommand(() =>
                {
                    this.InjectStopOnClick();
                    RaisePropertyChanged(nameof(ReadingCommandText));
                });
            }
        }

        #endregion

        #region 属性

        /// <summary>
        /// 缓存的测量数据
        /// </summary>
        public ChartValues<MeasureModel> ChartValues { get; set; }

        /// <summary>
        /// 时间格式化器
        /// </summary>
        public Func<double, string> DateTimeFormatter { get; set; }

        /// <summary>
        /// X 轴上每个数据的等距跳变(时)长
        /// </summary>
        public double AxisStep { get; set; }
        public double AxisUnit { get; set; }

        /// <summary>
        /// 正在读取
        /// </summary>
        public bool IsReading { get; set; }

        /// <summary>
        /// 控制按钮的文本
        /// </summary>
        public string ReadingCommandText
        {
            get
            {
                return IsReading ? "Stop" : "Start";
            }
        }

        private string curvalue;

        /// <summary>
        /// 当前值
        /// </summary>
        public string Curvalue
        {
            get { return curvalue; }
            set
            {
                curvalue = value;
                RaisePropertyChanged(nameof(Curvalue));
            }
        }

        private double axisMax;

        /// <summary>
        /// X轴最大
        /// </summary>
        public double AxisMax
        {
            get { return axisMax; }
            set
            {
                axisMax = value;
                RaisePropertyChanged(nameof(AxisMax));
            }
        }

        private double axisMin;

        /// <summary>
        /// X轴最小
        /// </summary>
        public double AxisMin
        {
            get { return axisMin; }
            set
            {
                axisMin = value;
                RaisePropertyChanged(nameof(AxisMin));
            }
        }

        #endregion

        /// <summary>
        /// 模拟数据采集
        /// </summary>
        public void Read()
        {
            while (IsReading)
            {
                Thread.Sleep(400);
                var now = DateTime.Now;
                double value = Math.Round(new Random().Next(10, 11) + (1 * new Random().NextDouble()), 2);
                ChartValues.Add(new MeasureModel
                {
                    DateTime = now,
                    Value = value
                });
                SetAxisLimits(now);
                Curvalue = value + "℃";
                // 只保留160个数据,满了就把前面的数据移除
                if (ChartValues.Count > 160) ChartValues.RemoveAt(0);
            }
            IsReading = false;
        }

        /// <summary>
        /// 设置 X 轴上呈现的时间范围
        /// </summary>
        /// <param name="now"></param>
        private void SetAxisLimits(DateTime now)
        {
            // X轴显示区域的最大时间
            AxisMax = now.Ticks + TimeSpan.FromSeconds(1).Ticks;
            // X轴显示区域的最大时间
            AxisMin = now.Ticks - TimeSpan.FromSeconds(8).Ticks; 
        }

        /// <summary>
        /// 根据标识是否启动采集
        /// </summary>
        public void InjectStopOnClick()
        {
            IsReading = !IsReading;
            if (IsReading)
            {
                Task.Factory.StartNew(Read);
            }
        }

    }
}

4.3数据绑定和界面完善

<local:AnimationPageBaseView x:Class="Deamon.View.ChamberView"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="clr-namespace:Deamon.View"
      xmlns:vm="clr-namespace:Deamon.ViewModel"
      xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
      DataContext="{Binding ChamberVM, Source={ x:Static vm:ViewModelLocator.Locator}}"
      Title="ChamberView">
    <Grid Background="#FF033F42">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <StackPanel Orientation="Horizontal" Margin="10 0 0 0">
            <Button  VerticalAlignment="Center" 
                     Style="{StaticResource OnlyTextButton}" 
                     Command="{Binding ReadingCommand}" 
                     Content="{Binding ReadingCommandText}"/>
        </StackPanel>
        
        <DockPanel Grid.Row="1">
            <Grid Width="300" Height="200">
                <Border CornerRadius="5" Background="White"/>
                <Grid  Margin="5">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="60"/>
                        <RowDefinition/>
                    </Grid.RowDefinitions>
                    <StackPanel Margin="5 0 0 0"  VerticalAlignment="Center">
                        <TextBlock Text="NO.607" FontSize="22" VerticalAlignment="Center" Foreground="#6C757D"/>
                        <TextBlock Text="Temperature:" FontSize="15"  Foreground="#6C757D"/>
                    </StackPanel>
                    <TextBlock Text="{Binding Curvalue}" Margin="10 5 0 0" Grid.Row="1" FontFamily="Consolas" FontSize="22" Foreground="#6C757D" FontWeight="Bold" />
                    <lvc:CartesianChart Grid.Row="1" AnimationsSpeed="0:0:0.2" Hoverable="False" DataTooltip="{x:Null}">
                        <lvc:CartesianChart.Series>
                            <lvc:LineSeries Values="{Binding ChartValues}" 
                                    PointGeometry="{x:Null}"
                                LineSmoothness="0"
                                StrokeThickness="3" 
                                Stroke="#12B1AD"
                                Fill="#E6F7F8"/>
                        </lvc:CartesianChart.Series>
                        <lvc:CartesianChart.AxisX>
                            <lvc:Axis LabelFormatter="{Binding DateTimeFormatter}" 
                          MaxValue="{Binding AxisMax}" 
                          MinValue="{Binding AxisMin}" ShowLabels="False"
                          Unit="{Binding AxisUnit}">
                                <lvc:Axis.Separator>
                                    <lvc:Separator Step="{Binding AxisStep}" />
                                </lvc:Axis.Separator>
                            </lvc:Axis>
                        </lvc:CartesianChart.AxisX>
                        <lvc:CartesianChart.AxisY>
                            <lvc:Axis ShowLabels="True"  />
                        </lvc:CartesianChart.AxisY>
                    </lvc:CartesianChart>
                </Grid>
            </Grid>
            
        </DockPanel>
       
    </Grid>
</local:AnimationPageBaseView>

 

Over

每次记录一小步...点点滴滴人生路...

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

WPF 图表控件LiveCharts的应用——室内监控可视化 的相关文章

  • 如何在MVVM中实现对话框架构

    我正在开发一个基于 PRISM 框架 Unity Container 的 WPF 4 0 MVVM 应用程序 我想知道在 mvvm 模式中实现对话框的最佳方法是什么 我计划在我的应用程序中使用相当多的东西 所以我想要一些可重用的东西 由于您
  • 如何在 WPF 中实现虚线或点线边框?

    我有一个ListViewItem我正在申请Style到 我想把灰色虚线作为底部Border 我怎样才能在 WPF 中做到这一点 我只能看到纯色画笔 这在我们的应用程序中效果很好 允许我们使用真正的边框而不是乱用矩形
  • 使用 INotifyPropertyChanged

    有人可以解释一下为什么在 wpf 中使用绑定时需要使用 INotifyPropertyChanged 的 实现吗 我可以在不实现此接口的情况下绑定属性吗 例如我有代码 public class StudentData INotifyProp
  • 该组件没有由 uri 标识的资源

    我想创建一个通用数据网格以在我的所有视图 用户控件上使用 这是我的结构 Class Library called Core Class called ViewBase public class ViewBase UserControl pu
  • 网格内的 ContentPresenter 可见性绑定不起作用?

    我有以下网格
  • 更改鼠标悬停时的矩形背景

    所以我有一个没有背景的矩形 当用户将鼠标悬停在其上时 我想给它一个背景渐变 然后当鼠标离开矩形时删除渐变 请有人发布所需的代码 并告诉我将其放在 cs xaml 文件中的位置吗 Thanks This
  • 根据属性的类型使用文本框或复选框

    如果我有这样的结构 public class Parent public string Name get set public List
  • WPF 数据绑定到复合类模式?

    我是第一次尝试 WPF 并且正在努力解决如何将控件绑定到使用其他对象的组合构建的类 例如 如果我有一个由两个单独的类组成的类 Comp 为了清楚起见 请注意省略的各种元素 class One int first int second cla
  • 在 WPF 中使用 ReactiveUI 提供长时间运行命令反馈的正确方法

    我有一个 C WPF NET 4 5 应用程序 用户将用它来打开某些文件 然后 应用程序将经历很多动作 读取文件 通过许多插件和解析器传递它 这些文件可能相当大 gt 100MB 因此这可能需要一段时间 我想让用户了解 UI 中发生的情况
  • 使用特定参数从 SQL 数据库填充组合框

    我在使用参数从 sql server 获取特定值时遇到问题 任何人都可以解释一下为什么它在 winfom 上工作但在 wpf 上不起作用以及我如何修复它 我的代码 private void UpdateItems COMBOBOX1 Ite
  • 先学Silverlight还是先学WPF?

    看来 Silverlight WPF 是 NET 用户界面开发的长期未来 这很棒 因为我可以看到在客户端和 Web 开发端重用 XAML 技能的优势 但看看 WPF XAML Silverlight 它们似乎是非常庞大的技术 那么从哪里开始
  • Wpf TextBlock 中的垂直文本

    是否可以垂直显示 TextBlock 中的文本 以便所有字母彼此堆叠 不使用 LayoutTransform 旋转 还没有人提到使用纯 XAML 垂直堆叠任意字符串的字母 不旋转它们 的明显而简单的方法
  • WPF 绑定 CompositeCollection 中的 MenuItem 不起作用

    我在将命令绑定到复合集合中的菜单项时遇到问题 这MenuItem是其一部分ContextMenu这是定义在UserControl Resources 问题是新标签的绑定不起作用 当我将 MenuItem 放置在复合集合之外时 它将起作用 有
  • 需要“依赖属性”的简短而清晰的定义

    我试图弄清楚依赖属性到底是什么 但是当我在任何地方寻找定义时 我只找到 如何使用 而不是 它是什么 想象一下 您在面试时被问到 什么是依赖属性 你的答案是什么 DependencyProperty 是一个属性 其值取决于 或可以取决于 某些
  • ScrollViewer 滚动条始终禁用

    我是 xaml 和 wpf 的新手 我正在尝试将一些用户控件从代码隐藏插入到容器中 我已阅读此博客文章MSDN http blogs msdn com b marcelolr archive 2009 06 09 stackpanel do
  • WPF控件默认大小

    为 wpf 应用程序定义自定义资源主题时 我可以设置宽度 高度等 如何找到这些属性的默认值 即框架中提供的控件中使用的值 WPF 控件通常不包含任何类型的默认大小 WPF 的主要功能点之一是 除非您指定大小 否则所有内容都会动态调整大小 如
  • 是什么导致 WPF 打印机输出被光栅化?

    我在 WPF 中遇到了许多打印问题 其中正在打印的特定项目组合会突然触发打印机输出的光栅化 导致打印机输出丑陋且庞大 并可能导致打印每页需要 30 秒 我发现在控件上设置任何级别的不透明度之类的事情通常会触发此问题 我忘记了它还有什么作用
  • 无法将像素着色器渲染到 RenderTargetBitmap!请帮忙!

    我编写了一个非常简单的 WPF 应用程序来测试渲染具有与 RenderTargetBitmap 关联的像素着色器的控件的能力 然后我将位图写入文件 jpeg 该控件被渲染到位图上 但是像素着色器效果不会应用于该控件 代码和 XAML 如下
  • 如何使取消按钮像“X”按钮一样工作?

    在我的 XAML 文件中 我有一个窗口 我试图将其设置为无论用户单击 X 按钮还是单击 取消 按钮 行为都是相同的 我的缩写代码如下 public partial class Dialog Window private void Windo
  • 计算 Richtextbox 中所有单词的最有效方法是什么?

    我正在编写一个文本编辑器 需要提供实时字数统计 现在我正在使用这个扩展方法 public static int WordCount this string s s s TrimEnd if String IsNullOrEmpty s re

随机推荐

  • 【整型提升】 【算术转换】【两千字详解】

    目录 1 隐式类型转换 1 1整型提升的概念 1 2整型提升的意义 1 3如何进行整型提升 负数的整形提升 正数的整形提升 无符号数的整形提升 1 4实战应用 2 算数转化 1 隐式类型转换 表达式求值的顺序一部分是由操作符的优先级和结合性
  • opencv旋转矩形定义以及求交叉面积

    目录 代码 运行结果 结果分析 用途 可以用来计算目标检测或者分割等结果IOU 代码 import cv2 旋转矩形的定义 中心点x 中心点y 宽 高 角度值 rect1 0 0 100 100 10 x y w h rect2 0 0 5
  • Ubuntu Workbench连接失败 your connection attempt failed for user ‘root‘ to the MySQL server at 127.0.0.1

    1 打开终端进入root帐号 2 进入 etc mysql debian cnf文件 查看debian sys maint帐号密码 3 运行 mysql u debian sys maint p 输入密码 4 修改root帐号的密码 ALT
  • 经纬度换算数值_经纬度换算

    1 经纬度和弧度的转换 转换方法 角度转弧度为 180 角度 弧度变角度为180 弧度 经度分东经和西经 从0 经线向东内到180 为东容经 用字母E表示 从0 经线向西到180 为西经 用字母W表示经度的变化规律是东经向东度数越来越大 西
  • VBA:按照Excel工作表中的名称列自动汇总多个工作薄中对应sheet中所需要的数据

    需求如下 B列为产品名为合并单元格 C列为供应商名 G H列为金额数据 数据源放在同一个文件夹内 B列产品名来源于工作薄名称中间的字符串 C列供应商名来源于工作薄中的sheet名 G H列金额数据来源于工作薄中sheet中固定单元格P25
  • c++函数指针

    1 声明函数指针 double cal int prototype double pf int 指针pf指向的函数 输入参数为int 返回值为double pf cal 指针赋值 2 指针作为函数的参数传递 void estimate in
  • Pytorch Tensor的索引与切片

    1 Pytorch风格的索引 根据Tensor的shape 从前往后索引 依次在每个维度上做索引 示例代码 import torch a torch rand 4 3 28 28 print a 0 shape 取到第一个维度 print
  • svg格式文件转换为png图片文件

    快要下班的时候 领导突然找我 发给我一个页面 说觉得这个页面的图标感觉不错 想把它做成图片放在项目里 我打开网页 用f12一看 用的是svg 这个我也不知道咋处理啊 但是遇到事情我们先不要慌 先在网上找找有没有解决办法 一顿搜索之下 我找到
  • 1. TensorRT量化的定义及意义

    前言 手写AI推出的全新TensorRT模型量化课程 链接 TensorRT下的模型量化 课程大纲如下 1 量化的定义及意义 1 1 什么是量化 定义 量化 Quantization 是指将高精度浮点数 如float32 表示为低精度整数
  • warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]

    下面的文章详细介绍了这个warning的来源和解决方法 也可以关闭优化 当然关闭优化并不是最终解决方法 down vote accepted First off let s examine why you get the aliasing
  • Linux 问题排查及性能调优

    总目录 一 问题场景 1 CPU问题 2 内存问题 3 iowait问题 二 性能优化简介 三 Linux 问题排查顺序 前言 监控大盘 第一步 平均负载 第二步 cpu核 第三步 内存 第四步 磁盘 第五步 带宽 第六步 具体应用 第七步
  • Redis知识整理一:基础知识

    什么是Redis Redis是一个开源的使用ANSI C语言编写 支持网络 可基于内存亦可持久化的日志型 Key Value数据库 并提供多种语言的API Redis缓存数据库特点 1 Redis支持数据的持久化 可以将内存中的数据保存在磁
  • 使用Yii查询CodeStriker

    公司使用的质量管理系统 评审活动 缺陷跟踪和文档库 中 需要与CodeStriker互动 开发如下类实现基本功能 View Code 1
  • adb shell 中直接激活界面命令

    adb root adb remount adb shell am start a android settings WIFI SETTINGS am activityManager a action n 包名 包名 activity ad
  • Selenium成长之路-03使用IDLE编写第一个自动化脚本

    windows用户 在电脑的开始菜单搜索IDLE 并启动ILDE python GUI 编译器 接下来我们就进入编译的环节了 1 导入webdriver包 from selenium import webdriver 2 初始化一个火狐浏览
  • mac支持的硬盘格式 什么硬盘格式是mac和win支持的

    大部分硬盘在默认出厂前都会设定好一个固定的磁盘格式 如果该格式与用户电脑所使用的系统不兼容 就无法正确地进行硬盘读写甚至硬盘识别 对于Mac系统和Windows系统而言 它们都有自己支持的某些硬盘格式 什么是Mac支持的硬盘格式 什么硬盘格
  • 如何关闭电脑防火墙

    开始 控制面板 WINDOWS防火墙 打开或关闭WINDOWS防火墙 关闭WINDOWS防火墙 不推荐 效果截图
  • C++ 中的变量定义

    变量定义就是告诉编译器在何处创建变量的存储 以及如何创建变量的存储 变量定义指定一个数据类型 并包含了该类型的一个或多个变量的列表 如下所示 type variable list 在这里 type 必须是一个有效的 C 数据类型 可以是 c
  • 有源医疗器械电磁兼容EMC的测试标准、测试项目以及测试方法

    目录 有源医疗器械电磁兼容的测试标准 测试项目以及测试方法 一 什么是电磁兼容 1 1电磁兼容概念 1 2电磁兼容三要素 1 3为什么要做电磁兼容 1 4电磁兼容测试项目 二 电磁兼容标准 2 1基础标准 2 2通用标准 2 3产品族标准
  • WPF 图表控件LiveCharts的应用——室内监控可视化

    需求 为了实现一个完整软件系统 必须具备一些基本的数据呈现控件 例如曲线图 柱状图 饼图等 本次的业务需求为 利用LiveCharts展示后台模拟的温度变化 像Winform里面 微软为我们提供了比较完整的Chart控件 但是在WPF组件中