Just have a problem here that I have no idea how to fix. I am doing a small project which involves a GUI and serial data. The GUI is being run by the main thread and since the data variables that hold my incoming serial data need to be updated continuously, these are being updated in a second thread. The problem is when I need to update some textboxes on the GUI, these need to be updated with data from the secondary thread and that is where my problem lies. I can't update them directly from the secondary thread and I have no idea how I would transfer data from my secondary thread and work out a system of updating them from main thread. I have put my code below:
Any help would be great.
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 { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> 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 < 4; i++) { if (i == 0) { SaveDataString = "MaximumSpeed during the Ride was = " + Convert.ToString(MaximumSpeed) + "m/h"; SaveData[i] = SaveDataString; } if (i == 1) { SaveDataString = "Total Distance Travelled = " + Convert.ToString(DistanceTravelled) + "m"; SaveData[i] = SaveDataString; } if (i == 2) { SaveDataString = "Maximum Rider Input Power = " + Convert.ToString(maximumRiderInput) + "Watts"; SaveData[i] = SaveDataString; } if (i == 3) { SaveDataString = "Maximum Motor Output Power = " + Convert.ToString(MaximumMotorOutput) + "Watts"; SaveData[i] = SaveDataString; } } } private void SaveDataButton_Click(object sender, RoutedEventArgs e) { //File.WriteAllBytes(directory + "image" + imageNO + ".txt", ); //saves the file to Disk File.WriteAllLines(directory + "BikeData.txt", SaveData); } 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.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(); } }
WPF supports a single-threaded apartment model that has the following rules: One thread runs in the entire application and owns all the WPF objects. WPF elements have thread affinity, in other words other threads can't interact with each other.
The UI thread queues work items inside an object called a Dispatcher. The Dispatcher selects work items on a priority basis and runs each one to completion. Every UI thread must have at least one Dispatcher, and each Dispatcher can execute work items in exactly one thread.
A dispatcher is often used to invoke calls on another thread. An example would be if you have a background thread working, and you need to update the UI thread, you would need a dispatcher to do it. Follow this answer to receive notifications.
WPF requires that most of its objects be tied to the UI thread. This is known as thread affinity, meaning you can only use a WPF object on the thread on which it was created.
You can use Dispatcher.Invoke to update your GUI from a secondary thread.
Here is an example:
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(); }); } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With