Обновление GUI (WPF) с использованием другого streamа

Просто проблема здесь, я понятия не имею, как исправить. Я делаю небольшой проект, который включает графический интерфейс и последовательные данные. GUI запускается главным streamом, и поскольку переменные данных, которые содержат мои входящие последовательные данные, необходимо постоянно обновлять, они обновляются во втором streamе. Проблема в том, что когда мне нужно обновить некоторые текстовые поля в графическом интерфейсе, их необходимо обновить данными из вторичного streamа, и именно там моя проблема. Я не могу обновить их непосредственно из вторичного streamа, и я понятия не имею, как я буду передавать данные из моего вторичного streamа и выработать систему их обновления из основного streamа. Я поставил свой код ниже:

Любая помощь будет большой.

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.IO; using System.IO.Ports; using System.Threading; namespace GUIBike { ///  /// Interaction logic for MainWindow.xaml ///  public partial class MainWindow : Window { public static string inputdata; public static int MaximumSpeed, maximumRiderInput, RiderInput, Time, CurrentSpeed, DistanceTravelled, MaximumMotorOutput, MotorOutput, InputSpeed; public static string SaveDataString; public Thread Serial; public static SerialPort SerialData; public static string[] portlist = SerialPort.GetPortNames(); public static string[] SaveData = new string[4]; public static string directory = "C:\\"; public MainWindow() { Serial = new Thread(ReadData); InitializeComponent(); int Count = 0; for (Count = 0; Count < portlist.Length; Count++) { ComPortCombo.Items.Add(portlist[Count]); } } private void StartDataButton_Click(object sender, RoutedEventArgs e) { SerialData = new SerialPort(ComPortCombo.Text, 19200, Parity.None, 8, StopBits.One); SerialData.Open(); SerialData.WriteLine("P"); Serial.Start(); StartDataButton.IsEnabled = false; EndDataButton.IsEnabled = true; ComPortCombo.IsEnabled = false; CurrentSpeed = 0; MaximumSpeed = 0; Time = 0; DistanceTravelled = 0; MotorOutput = 0; RiderInput = 0; SaveData[0] = ""; SaveData[1] = ""; SaveData[2] = ""; SaveData[3] = ""; SaveDataButton.IsEnabled = false; if (SerialData.IsOpen) { ComPortStatusLabel.Content = "OPEN"; SerialData.NewLine = "/n"; SerialData.WriteLine("0"); SerialData.WriteLine("/n"); } } private void EndDataButton_Click(object sender, RoutedEventArgs e) { SerialData.Close(); SaveDataButton.IsEnabled = true; SerialData.WriteLine("1"); SerialData.WriteLine("0"); if (!SerialData.IsOpen) { ComPortStatusLabel.Content = "CLOSED"; } int i = 0; for (i = 0; i  MaximumSpeed) { MaximumSpeed = CurrentSpeed; } SpeedTextBox.Text = "Current Wheel Speed = " + Convert.ToString(CurrentSpeed) + "Km/h"; DistanceTravelled = DistanceTravelled + (Convert.ToInt16(CurrentSpeed) * Time); DistanceTravelledTextBox.Text = "Total Distance Travelled = " + Convert.ToString(DistanceTravelled) + "Km"; //} //catch (Exception) { } } if (counter == 1) { try { RiderInput = Convert.ToInt16(SerialData.ReadLine()); if (RiderInput > maximumRiderInput) { maximumRiderInput = RiderInput; } RiderInputTextBox.Text = "Current Rider Input Power =" + Convert.ToString(RiderInput) + "Watts"; } catch (Exception) { } } if (counter == 2) { try { MotorOutput = Convert.ToInt16(SerialData.ReadLine()); if (MotorOutput > MaximumMotorOutput) { MaximumMotorOutput = MotorOutput; } MotorOutputTextBox.Text = "Current Motor Output = " + Convert.ToString(MotorOutput) + "Watts"; } catch (Exception) { } } counter++; if (counter == 3) { counter = 0; } } } private void ComPortCombo_SelectionChanged(object sender, SelectionChangedEventArgs e) { StartDataButton.IsEnabled = true; } private void Window_Closed(object sender, RoutedEventArgs e) { if (SerialData.IsOpen) { SerialData.Close(); } } 

Вы можете использовать делегата для решения этой проблемы. Ниже приведен пример, показывающий, как обновлять текстовый блок с помощью другой темы

 public delegate void UpdateTextCallback(string message); private void TestThread() { for (int i = 0; i <= 1000000000; i++) { Thread.Sleep(1000); richTextBox1.Dispatcher.Invoke( new UpdateTextCallback(this.UpdateText), new object[] { i.ToString() } ); } } private void UpdateText(string message) { richTextBox1.AppendText(message + "\n"); } private void button1_Click(object sender, RoutedEventArgs e) { Thread test = new Thread(new ThreadStart(TestThread)); test.Start(); } 

Метод TestThread используется тестом thread named для обновления textBox

там.

Я также разрабатываю инструмент тестирования последовательного порта с использованием WPF, и я хотел бы поделиться своим опытом.

Я думаю, вам нужно реорганизовать исходный код в соответствии с шаблоном проектирования MVVM.

В начале я встретил ту же проблему, что и вы, и я решил ее с помощью этого кода:

 new Thread(() => { while (...) { SomeTextBox.Dispatcher.BeginInvoke((Action)(() => SomeTextBox.Text = ...)); } }).Start(); 

Это работает, но слишком уродливо. Я понятия не имею, как его реорганизовать, пока я не увидел это: http://www.codeproject.com/Articles/165368/WPF-MVVM-Quick-Start-Tutorial

Это очень любезное пошаговое руководство MVVM для начинающих. Нет блестящего интерфейса, не сложной логики, только базового MVVM.

Вы можете использовать Dispatcher.Invoke для обновления вашего графического интерфейса из вторичного streamа.

Вот пример:

  private void Window_Loaded(object sender, RoutedEventArgs e) { new Thread(DoSomething).Start(); } public void DoSomething() { for (int i = 0; i < 100000000; i++) { this.Dispatcher.Invoke(()=>{ textbox.Text=i.ToString(); }); } } 

Используйте следующий метод обновления графического интерфейса.

  Public Void UpdateUI() { //Here update your label, button or any string related object. //Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background, new ThreadStart(delegate { })); Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new ThreadStart(delegate { })); } 

Держите его в разуме, когда вы используете этот метод в то время, не обновляйте тот же объект прямо из streamа диспетчера, иначе вы получите только эту обновленную строку, и этот метод беспомощный / бесполезный. Если все еще не работает, то комментируйте, что строка внутри метода и комментарий не комментируются, оба имеют почти такой же эффект, что и другой способ доступа к нему.

Поскольку akjoshi и Julio говорят об отправке Action для обновления GUI в том же streamе, что и элемент GUI, но из метода обработки фоновых данных. Вы можете увидеть этот код в определенной форме в ответе akjoshi выше. Это общая версия.

 myTextBlock.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(delegate() { myTextBlock.Text = Convert.ToString(myDataObject.getMeData()); })); 

Ключевой частью является вызов диспетчера вашего объекта пользовательского интерфейса – который гарантирует, что у вас есть правильный stream.

Из личного опыта кажется намного проще создать и использовать Action inline, как это. Объявление его на уровне classа дало мне много проблем со статическими / нестатическими контекстами.

Вам нужно использовать Dispatcher.BeginInvoke . Я не тестировал его, но вы можете проверить эту ссылку (это та же ссылка, предоставленная Julio G), чтобы лучше понять, как обновлять элементы управления пользовательского интерфейса из разных streamов. Я изменил ваш код ReadData()

 public void ReadData() { int counter = 0; while (SerialData.IsOpen) { if (counter == 0) { //try //{ InputSpeed = Convert.ToInt16(SerialData.ReadChar()); CurrentSpeed = InputSpeed; if (CurrentSpeed > MaximumSpeed) { MaximumSpeed = CurrentSpeed; } SpeedTextBox.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(delegate() { SpeedTextBox.Text = "Current Wheel Speed = " + Convert.ToString(CurrentSpeed) + "Km/h"; });//update GUI from this thread DistanceTravelled = DistanceTravelled + (Convert.ToInt16(CurrentSpeed) * Time); DistanceTravelledTextBox.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(delegate() {DistanceTravelledTextBox.Text = "Total Distance Travelled = " + Convert.ToString(DistanceTravelled) + "Km"; });//update GUI from this thread //} //catch (Exception) { } } if (counter == 1) { try { RiderInput = Convert.ToInt16(SerialData.ReadLine()); if (RiderInput > maximumRiderInput) { maximumRiderInput = RiderInput; } RiderInputTextBox.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(delegate() { RiderInputTextBox.Text = "Current Rider Input Power =" + Convert.ToString(RiderInput) + "Watts"; });//update GUI from this thread } catch (Exception) { } } if (counter == 2) { try { MotorOutput = Convert.ToInt16(SerialData.ReadLine()); if (MotorOutput > MaximumMotorOutput) { MaximumMotorOutput = MotorOutput; } MotorOutputTextBox.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(delegate() { MotorOutputTextBox.Text = "Current Motor Output = " + Convert.ToString(MotorOutput) + "Watts"; });//update GUI from this thread } catch (Exception) { } } counter++; if (counter == 3) { counter = 0; } } } 

Думаю, у вас есть пара вариантов.

Можно было бы использовать BackgroundWorker. Это общий помощник для многопоточности в приложениях. Он предоставляет событие DoWork, которое обрабатывается в фоновом streamе из пула streamов и событие RunWorkerCompleted, которое вызывается обратно в основной stream при завершении фонового streamа. Он также имеет преимущество try / catch кода, выполняющегося на фоновом streamе, так что необработанное исключение не убивает приложение.

Если вы не хотите идти по этому маршруту, вы можете использовать объект диспетчера WPF для вызова действия для обновления GUI обратно в основной stream. Случайная ссылка:

http://www.switchonthecode.com/tutorials/working-with-the-wpf-dispatcher

Есть много других вариантов вокруг, но это два наиболее распространенных, которые приходят на ум.

  • Как проверить, работает ли std :: thread?
  • C ++ эквивалентно Java BlockingQueue
  • Что такое мьютекс?
  • Java Wait и Notify: IllegalMonitorStateException
  • Почему один stream быстрее, чем multithreading в Java?
  • Как я должен тестировать многопоточный код?
  • Когда вы вызываете java-stream thread.run () вместо thread.start ()?
  • Если streamи разделяют один и тот же PID, как их можно идентифицировать?
  • Как правильно использовать Java Executor?
  • Анимация при использовании Gridbag Layout.
  • Решить перекрестное streamовое исключение в WinForms
  • Interesting Posts

    Можно ли разделить одну линию Ethernet, выходящую из моей стены, на несколько отдельных линий?

    Поиск обходного пути для файла gtable_add_grob, нарушенного ggplot 2.2.0

    constexpr инициализирует статический член, используя статическую функцию

    Почему кеш-код hashCode () не указан 0?

    Как узнать путь к текущему изображению рабочего стола?

    Групповая агрегация Mongodb $ group, ограничить длину массива

    Приложение, которое автоматически отслеживает количество активного времени, проведенного на компьютере

    Разница между File.separator и slash в путях

    Почему бросание 2 исключений в строке не генерирует недопустимый код предупреждения?

    Использование пользовательских шрифтов без прав администратора?

    Что происходит, когда загрузка файла отменяется?

    Ошибка Angularfire 2: указанный поставщик аутентификации не включен для этой Firebase

    Как получить родительский контроль в Windows 8

    Mercurial .hgignore для проектов Visual Studio 2010

    Диаграмма Венна пропорциональная и цветовая штриховка с полупрозрачностью

    Давайте будем гением компьютера.