Вызывающий поток должен быть потоком STA, поскольку он требуется для многих компонентов пользовательского интерфейса с помощью livechart. - PullRequest
0 голосов
/ 20 сентября 2018

Может быть, мой код является дубликатом, но на самом деле я попробовал другие решения и не смог решить проблему.Форма графика не является моей основной формой, но она открывается из основной формы.в основной программе у меня есть [STAThread] перед Main ().Я хочу запустить Ploting () для обновления графика, если произойдут какие-либо изменения в данных в таблице sql.мой код выглядит следующим образом:

namespace MainProject1{
  public partial class Plot : Form
{
    System.Timers.Timer timer = new System.Timers.Timer();
    BackgroundWorker PlotWorker;  

    public Plot()
    {
        InitializeComponent();
        timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);

        PlotWorker = new BackgroundWorker();
        PlotWorker.DoWork += new DoWorkEventHandler(PlotWorker_DoWork);
        PlotWorker.WorkerSupportsCancellation = true;

        //Ploting(); //here it works
    }

    void PlotWorker_DoWork(object sender, DoWorkEventArgs e)
    {

        while (true)
        {
            //Ploting(); //here also receive the error.
            if (PlotWorker.CancellationPending)
            {
                e.Cancel = true;
                return;
            }
        }
    }

    private void timer_Elapsed(object sender, ElapsedEventArgs elapsedEventArg)
    {
        Ploting(); //here i receive the error
    }

    private void test_Click(object sender, EventArgs e)
    {
        timer.AutoReset = true;
        timer.Interval = 100;
        timer.Start();
    }

    private void Ploting()
    {
        try
        {
            var list1 = new List<double>();
            var list2 = new List<double>();

            SqlResultTable rt = new SqlResultTable();
            rt.ReadtoPlot(84, "readingS", ref list1, ref list2); //here i read the data from sql. it works corrrect.
            cartesianChart1.Series = new SeriesCollection
            {
                new ScatterSeries
                {
                    Title = "Setpoints",
                    Values = new ChartValues<double>(list1),
                    PointGeometry = DefaultGeometries.Diamond,
                    StrokeThickness = 2,
                    Fill = Brushes.Transparent,
                },
                new LineSeries
                {
                    Title = "UUT readings",
                    Values = new ChartValues<double>(list2),
                    LineSmoothness = 0,
                    PointGeometrySize = 10
                },
            };
            cartesianChart1.DataClick += CartesianChart1OnDataClick;
        }
        catch(Exception err)
        {
            MessageBox.Show(err.Message);
        }
    }

    private void CartesianChart1OnDataClick(object sender, ChartPoint chartPoint)
    {
        MessageBox.Show("You clicked (" + chartPoint.X + "," + chartPoint.Y + ")");
    }
}

}

, когда я использую метод Ploting () в конструкторе, он работает, но когда я использую его в работнике или в timer_Elapsed, я получаюошибка снова.Я знаю, что это как-то связано с потоком, но не знаю, как это решить.Я попытался создать новый поток в фоновом режиме или timer_Elapsed, но получил еще одну ошибку.Извините за вопрос.Я новичок в C #!

Новое обновление: код с делегированием:

  namespace MainProject1{
  public partial class Plot : Form
{
    public delegate void plotDel(); //**** new added ***
    System.Timers.Timer timer = new System.Timers.Timer();
    BackgroundWorker PlotWorker;  

    public Plot()
    {
        InitializeComponent();
        timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);

        PlotWorker = new BackgroundWorker();
        PlotWorker.DoWork += new DoWorkEventHandler(PlotWorker_DoWork);
        PlotWorker.WorkerSupportsCancellation = true;

        plotDel pl1 = OnPlotNeeded; //**** new added ***
        //Ploting(); //here it works
    }

    protected virtual void OnPlotNeeded()  //**** new added ***
    {
        Ploting();
    }
    void PlotWorker_DoWork(object sender, DoWorkEventArgs e)
    {

        while (true)
        {
            //Ploting(); //here also receive the error.
            if (PlotWorker.CancellationPending)
            {
                e.Cancel = true;
                return;
            }
        }
    }

    private void timer_Elapsed(object sender, ElapsedEventArgs elapsedEventArg)
    {
        System.Threading.SynchronizationContext.Current.Post(new System.Threading.SendOrPostCallback(o => Ploting()), null); //when i add this line, it does not go to next line(no error, no action) and without it, again receive the error as before!
        OnPlotNeeded();
    }

    private void test_Click(object sender, EventArgs e)
    {
        timer.AutoReset = true;
        timer.Interval = 100;
        timer.Start();
    }

    private void Ploting()
    {
        // the inside code is same as first code
    }

    private void CartesianChart1OnDataClick(object sender, ChartPoint chartPoint)
    {
        MessageBox.Show("You clicked (" + chartPoint.X + "," + chartPoint.Y + ")");
    }
}

}

1 Ответ

0 голосов
/ 20 сентября 2018

Вероятно, проблема в том, что timer_Elapsed не выполняется в UiThread (на самом деле это то, что пытается сказать вам исключение).Но вы можете делегировать вызов Ploting() обратно в UiThread.

Попробуйте этот вариант timer_Elapsed

private void timer_Elapsed(object sender, ElapsedEventArgs elapsedEventArg)
{
    System.Threading.SynchronizationContext.Current.Post(delegate { this.Ploting(); }, null);
}

, а также заменить все другие вызовы на Plotting() с отслаиванием.

Редактировать: сохранить SynchronizationContext при создании в переменной класса, такой как

private readonly SynchronizationContext syncContext;

public Plot()
{
    InitializeComponent();
    this.syncContext = System.Threading.SynchronizationContext.Current;
    ...
}

, и вызвать Plotting() с таким контекстом, например:

private void Timer_Elapsed(object sender, ElapsedEventArgs e) => this.DelagetePloting();

private void DelagetePloting()
    => this.syncContext.Post(delegate
    {
        this.Ploting();
    }, null);

private void Ploting()
{
    // your work
}
...