开发者

Need help on Problem in using c# .net 4.0 realtime chart in windows form application

开发者 https://www.devze.com 2023-02-24 04:41 出处:网络
I have a windows form which has 1 button (for simplicity). On clicking this button, I start an algorithm in a separate background thread using ParameterizedThreadStart.

I have a windows form which has 1 button (for simplicity).

On clicking this button, I start an algorithm in a separate background thread using ParameterizedThreadStart.

Now this algorithm produces output at regular time intervals which needs to be displayed in a chart.

  1. If i init the chart in MainForm and pass this object to algorithm thread, then it doesnt allow and says chart's allocation and access are cross-thread.

  2. If i have chart object inside algo class, then it doesnt display the chart ticks. Only a blank form shows up (that too by doing _chart.Show()) and no ticks displayed.

Also, how to AddPoint to the chart, I used chart.Invoke(chart.AddPointDelegate, params) in the 2nd case, but it gets stuck at Invoke.

Please help me with a way out.

EDIT:

    public MyForm()
    {
        InitializeComponent();

        _getChartDataDelegate = AddTickToChart;
    }

    public AddTickChartDelegate _getChartDataDelegate;

    public void AddTickToChart(ChartTickPoint point)
    {
        DateTime x = point.X;
        double y = point.Y;
        object[] parameters = { x, y };


        if (this.InvokeRequired)
        {// this prevents the invoke loop
            this.Invoke(new Action<ChartTickPoint>(_chart.chartDelegate), new object[] {  point }); // invoke call for _TH开发者_StackOverflowIS_ function to execute on the UI thread
        }
        else
        {
            //function logic to actually add the datapoint goes here
            //_chart.Invoke(_chart.chartDelegate, parameters);
            _chart.AddTick(point);
        }
    }

    private void button_Click(object sender, EventArgs e)
    {
        Thread thread = new Thread(new ParameterizedThreadStart(MyUtils.RunAlgo));
        AlgoData algoData = new AlgoData(myFile, _getChartDataDelegate);
        thread.Start(algoData);
    }
    // MyForm ends

    // Intermediate static Util class to run algo
    MyUtils.RunAlgo(object obj)
    {
       // new Algo
       // Get delegate from algoData obj
       algo.Run(delegateInTheMyForm);

    }

    // Algo class's Run
    Run(AddTickChartDelegate delegateInTheMyForm)
    {
        delegateInTheMyForm(point);
    }

    // Chart class
    public AddTickChartDelegate chartDelegate;

    public void AddTick(ChartTickPoint point)
    {
        DateTime timeStamp = point.X;
        double y = point.Y;

        foreach (Series ptSeries in chart1.Series)
        {
            AddNewPoint(timeStamp, y, ptSeries);
        }
    }

Here again i am getting cross-thread issue at this.Invoke(new Action..) in MyForm class.

Further,

If i replace this.Invoke(Action..) with chart.Invoke(..)
if (this.InvokeRequired)
{
_chart.Invoke(_chart.chartDelegate, point);
// instead of Action...
}

then it goes through , but the chart form is not responsive and is blank.


Write a function for adding datapoints in your form class

For example:

public void addDataPoint(YourDataClass entity)
{
    if(this.InvokeRequired)
    {// this prevents the invoke loop
       this.Invoke(new Action<YourDataClass>(addDataPoint),new object[]{entity}); // invoke call for _THIS_ function to execute on the UI thread
    }
    else{
       //function logic to actually add the datapoint goes here
       chartControl.Series[0].Points.AddXY(entity.X,entity.Y); // assuming your dataclass has the members X and Y and you are using the first Series on a MSChart control
    }
}

You can call this function from your worker thread since it will invoke the UI thread to avoid cross-thread access

So... yes, your worker will need to have a reference to that function... if you want to keep your algo class knowledge of the form to a minimum, you can build an object that holds the reference to the form (cast to ISynchronizedInvoke) and the delegate (addDataPoint) so the worker keeps separated from the UI.

//EDIT: Complete example Form

using System;
using System.Windows.Forms;
using System.Threading;

namespace ExampleApplication
{
    public class Form1 : Form
    {

        #region designer-generated-code
        private System.ComponentModel.IContainer components = null;

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        private void InitializeComponent()
        {
            System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea1 = new System.Windows.Forms.DataVisualization.Charting.ChartArea();
            System.Windows.Forms.DataVisualization.Charting.Legend legend1 = new System.Windows.Forms.DataVisualization.Charting.Legend();
            System.Windows.Forms.DataVisualization.Charting.Series series1 = new System.Windows.Forms.DataVisualization.Charting.Series();
            this.chart1 = new System.Windows.Forms.DataVisualization.Charting.Chart();
            this.button1 = new System.Windows.Forms.Button();
            ((System.ComponentModel.ISupportInitialize)(this.chart1)).BeginInit();
            this.SuspendLayout();
            // 
            // chart1
            // 
            this.chart1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
                        | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
            chartArea1.Name = "ChartArea1";
            this.chart1.ChartAreas.Add(chartArea1);
            legend1.Name = "Legend1";
            this.chart1.Legends.Add(legend1);
            this.chart1.Location = new System.Drawing.Point(12, 12);
            this.chart1.Name = "chart1";
            series1.ChartArea = "ChartArea1";
            series1.Legend = "Legend1";
            series1.Name = "Series1";
            this.chart1.Series.Add(series1);
            this.chart1.Size = new System.Drawing.Size(733, 192);
            this.chart1.TabIndex = 0;
            this.chart1.Text = "chart1";
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(620, 210);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(125, 40);
            this.button1.TabIndex = 1;
            this.button1.Text = "button1";
            this.button1.UseVisualStyleBackColor = true;
            this.button1.Click += new System.EventHandler(this.button1_Click);
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(757, 262);
            this.Controls.Add(this.button1);
            this.Controls.Add(this.chart1);
            this.Name = "Form1";
            this.Text = "Form1";
            ((System.ComponentModel.ISupportInitialize)(this.chart1)).EndInit();
            this.ResumeLayout(false);

        }

        private System.Windows.Forms.DataVisualization.Charting.Chart chart1;
        private System.Windows.Forms.Button button1;
        #endregion

        public Form1()
        {
            InitializeComponent();
        }

        public void AddDataPoint(myData d)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new Action<myData>(this.AddDataPoint), new object[] { d });
            }
            else
            {
                chart1.Series[0].Points.AddXY(d.X, d.Y);
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            new Thread(new ParameterizedThreadStart(worker)).Start(new Action<myData>(this.AddDataPoint));
        }
        private void worker(object obj)
        {
            var _delegate = (Action<myData>)obj;
            for (int x = 0; x < 50; x++)
            {
                _delegate(new myData { X = x, Y = 2 * x });
                Thread.Sleep(1000);
            }
        }
    }
    public class myData
    {
        public int X;
        public int Y;
    }
}
0

精彩评论

暂无评论...
验证码 换一张
取 消