C#使用Oxyplot繪制監控界面


 

 

C#中可選的繪圖工具有很多,除了Oxyplot還有DynamicDataDisplay(已經改名為InteractiveDataDisplay)等等。不過由於筆者這里存在一些環境上的特殊要求,.Net Framework的版本被限制在4,而InteractiveDataPlay要求的最低版本是4.5.2。所以選擇了對版本要求較低的Oxyplot。

IDE為VS2012,創建工程后首先通過NuGet程序包管理器控制台,通過下述命令添加對Oxyplot的引用:

PM> Install-Package Oxyplot.Core
PM> Install-Package Oxyplot.Wpf

 XAML里添加上必要的引用:

<Window x:Class="MonitorForm.MonitorForm"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MonitorForm"
        xmlns:oxy="http://oxyplot.org/wpf"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <oxy:PlotView Model="{Binding Path= SimplePlotModel}"></oxy:PlotView>

    </Grid>
</Window>

筆者的開發場景是需要統計四類數據,因此開放四個公共方法供外部調用:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Threading;

namespace MonitorForm {

    /// <summary>
    /// MainWindow.xaml 的交互邏輯
    /// </summary>
    public partial class MonitorForm : Window {

        private PlotViewModel _viewModel;

        public MonitorForm() {

            InitializeComponent();

            _viewModel = new PlotViewModel();
            this.DataContext = _viewModel;
        }

        public void addSendCount(int iCount) {

            _viewModel.addSendCount(iCount);
        }

        public void addUnsendCount(int iCount) {

            _viewModel.addUnsendCount(iCount);
        }

        public void addConfirmCount(int iCount) {

            _viewModel.addConfirmCount(iCount);
        }

        public void addDealCount(int iCount) {

            _viewModel.addDealCount(iCount);
        }
    }
}

MVVM模式,需要添加ViewModel層:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OxyPlot;
using OxyPlot.Series;
using OxyPlot.Axes;
using System.Threading.Tasks;
using System.Threading;
using System.Collections.Concurrent;

namespace MonitorForm {

    public class PlotViewModel {

        /// <summary>
        /// 畫直線
        /// </summary>
        public PlotModel SimplePlotModel { get; set; }

        //每條線對應一個隊列用作實時數據統計
        private ConcurrentQueue<int> queueSend = new ConcurrentQueue<int>();
        private ConcurrentQueue<int> queueUnsend = new ConcurrentQueue<int>();
        private ConcurrentQueue<int> queueConfirm = new ConcurrentQueue<int>();
        private ConcurrentQueue<int> queueDeal = new ConcurrentQueue<int>();

        public void addSendCount(int iCount) {

            queueSend.Enqueue(iCount);
        }

        public void addUnsendCount(int iCount) {

            queueUnsend.Enqueue(iCount);
        }

        public void addConfirmCount(int iCount) {

            queueConfirm.Enqueue(iCount);
        }

        public void addDealCount(int iCount) {

            queueDeal.Enqueue(iCount);
        }

        public int getSendCount() { 
        
            int iSingle = 0;
            int iTotal = 0;
            while (queueSend.TryDequeue(out iSingle)) {

                iTotal += iSingle;
            }

            return iTotal;
        }

        public int getUnsendCount() {

            int iSingle = 0;
            int iTotal = 0;
            while (queueUnsend.TryDequeue(out iSingle)) {

                iTotal += iSingle;
            }

            return iTotal;
        }

        public int getConfirmCount() {

            int iSingle = 0;
            int iTotal = 0;
            while (queueConfirm.TryDequeue(out iSingle)) {

                iTotal += iSingle;
            }

            return iTotal;
        }

        public int getDealCount() {

            int iSingle = 0;
            int iTotal = 0;
            while (queueDeal.TryDequeue(out iSingle)) {

                iTotal += iSingle;
            }

            return iTotal;
        }        

        public PlotViewModel() {            

            SimplePlotModel = new PlotModel();            

            //創建於建立初始化數據節點
            var lineSend = new LineSeries() { Title = "報送" };
            lineSend.Points.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now), 0));
            SimplePlotModel.Series.Add(lineSend);

            var lineUnsend = new LineSeries() { Title = "待報送" };
            lineUnsend.Points.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now), 0));
            SimplePlotModel.Series.Add(lineUnsend);

            var lineConfirm = new LineSeries() { Title = "確認" };
            lineConfirm.Points.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now), 0));
            SimplePlotModel.Series.Add(lineConfirm);

            var lineDeal = new LineSeries() { Title = "成交" };
            lineDeal.Points.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now), 0));
            SimplePlotModel.Series.Add(lineDeal);            

            //定義y軸
            LinearAxis leftAxis = new LinearAxis() {

                Position = AxisPosition.Left,
                Minimum = 0,
                Maximum = 100,
                Title = "筆數",//顯示標題內容
                TitlePosition = 0,//顯示標題位置
                MajorGridlineStyle = LineStyle.Solid,
                MinorGridlineStyle = LineStyle.None,
            };

            //定義x軸 報盤監控界面x軸統一為時間
            DateTimeAxis bottomAxis = new DateTimeAxis() {

                Position = AxisPosition.Bottom,
                StringFormat = "hh:mm:ss",
                Minimum = DateTimeAxis.ToDouble(DateTime.Now),
                Maximum = DateTimeAxis.ToDouble(DateTime.Now.AddMinutes(1)),
                Title = "時間",
                TitlePosition = 0,
                IntervalLength = 60,
                MinorIntervalType = DateTimeIntervalType.Seconds,
                IntervalType = DateTimeIntervalType.Seconds,
                MajorGridlineStyle = LineStyle.Solid,
                MinorGridlineStyle = LineStyle.None,
            };

            SimplePlotModel.Axes.Add(leftAxis);
            SimplePlotModel.Axes.Add(bottomAxis);

            bool bToMove = false;
            Task.Factory.StartNew(() => {

                while (true) {
                        
                    lineSend.Points.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now), getSendCount()));
                    lineUnsend.Points.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now), getUnsendCount()));
                    lineConfirm.Points.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now), getConfirmCount()));
                    lineDeal.Points.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now), getDealCount()));

                    if (!bToMove) {

                        //當前時間減去起始時間達到30秒后開始左移時間軸
                        TimeSpan timeSpan = DateTime.Now - DateTimeAxis.ToDateTime(bottomAxis.Minimum);
                        if (timeSpan.TotalSeconds >= 30) {

                            bToMove = true;
                        }
                    } else { 
                    
                        //左移時間軸,跨度維持在60秒
                        bottomAxis.Minimum = DateTimeAxis.ToDouble(DateTime.Now.AddSeconds(-30));
                        bottomAxis.Maximum = DateTimeAxis.ToDouble(DateTime.Now.AddSeconds(30));

                        //刪除歷史節點,防止DataPoint過多影響效率,也防止出現內存泄漏                        
                        lineSend.Points.RemoveAt(0);
                        lineConfirm.Points.RemoveAt(0);
                        lineUnsend.Points.RemoveAt(0);
                        lineDeal.Points.RemoveAt(0);
                    }
                           
                    //根據報單筆數判斷是否需要更新y軸刻度                    
                    
                    //首先找出四條統計線中當前最大的節點
                    double iMax = lineSend.MaxY;
                    if (iMax < lineConfirm.MaxY) {
                        iMax = lineConfirm.MaxY;
                    }
                    if (iMax < lineUnsend.MaxY) {
                        iMax = lineUnsend.MaxY;
                    }
                    if (iMax < lineDeal.MaxY) {
                        iMax = lineDeal.MaxY;
                    }
                    
                    //如果當前的y軸最大刻度小於數據集中的最大值,放大
                    leftAxis.Maximum = iMax + (100 - iMax % 100);
                    leftAxis.IntervalLength = leftAxis.Maximum / 5;

                    //每秒刷新一次視圖                                       
                    SimplePlotModel.InvalidatePlot(true);
                    Thread.Sleep(1000);
                }
            });
        }
    }
}

從上述代碼可以看出,功能主要為實現了Y軸為筆數統計,而X軸為時間軸且當線畫到了中間時開始左移時間軸。統計的間隔為1秒。

如果要導出DLL封裝給其他程序使用的話,在項目屬性中將輸出類型調整為類庫即可。這時可能會有例如不存在InitializeComponent()之類的報錯,將App.xmal屬性中的生成操作修改成無即可順利導出。

自己寫了個小DEMO調用該DLL,不停往里添加數據,效果圖如下:

時間軸會左移,而Y軸的筆數統計也會根據當前DataPoint集合中的最大值進行相應的調整。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM