La mise à jour de l'interface graphique (WPF) à l'aide d'un thread différent
Juste un problème que je n'ai aucune idée de comment le résoudre. Je suis en train de faire un petit projet qui implique une interface graphique et des données en série. L'interface graphique est en cours d'exécution par le thread principal et, puisque les données des variables qui retiennent le plus mon entrants série de données doivent être mis à jour en permanence, ils sont mis à jour dans un deuxième thread. Le problème est quand j'ai besoin de mettre à jour certaines zones de texte sur l'interface graphique, ceux-ci doivent être mis à jour avec les données de la thread secondaire et c'est là que mon problème se situe. Je ne peux pas mettre à jour directement à partir du thread secondaire et je n'ai aucune idée de comment je pourrais transférer des données de mon thread secondaire et de travailler sur un système de mise à jour de thread principal. J'ai mis mon code ci-dessous:
Toute aide serait super.
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();
}
}
- Possible dupe de stackoverflow.com/questions/1684341/...
Vous devez vous connecter pour publier un commentaire.
Vous pouvez utiliser un délégué pour résoudre ce problème.
Voici un exemple qui montre comment mettre à jour une zone de texte à l'aide de différentes thread
TestThread méthode est utilisée par thread nommé test de mise à jour de zone de texte
là.
Je suis également à l'élaboration d'un port série outil de test, l'utilisation de WPF,
et je tiens à partager une certaine expérience de la mine.
Je pense que vous devriez refactoriser le code source, selon le design pattern MVVM.
Au début, j'ai rencontré le même problème que vous avez rencontré, et je l'ai résolu en utilisant ce code:
Cela fonctionne, mais c'est trop laid.
Je n'ai aucune idée de la façon de refactoriser le code, jusqu'à ce que j'ai vu ceci:
http://www.codeproject.com/Articles/165368/WPF-MVVM-Quick-Start-Tutorial
C'est un très gentiment étape-par-étape MVVM tutoriel pour les débutants.
Pas brillant de l'INTERFACE utilisateur, pas de logique complexe, seule la base de MVVM.
Vous pouvez utiliser Répartiteur.Invoquer de mettre à jour votre interface graphique à partir d'un thread secondaire.
Voici un exemple:
Utiliser la Méthode Suivante pour mettre à Jour l'interface graphique.
Garder à l'Esprit lorsque vous utilisez cette méthode ne mettent pas à Jour en même objet direct de l'expéditeur thread sinon, vous obtenez seulement la mise à jour de chaîne et cette méthode est impuissant/inutile.
Si ne fonctionne toujours pas Commenter cette ligne à l'intérieur de la méthode et des nations unies-commentaire, a commenté l'un des deux ont pratiquement le même effet différentes manières d'y accéder.
Comme akjoshi et Julio dire que c'est à propos de l'envoi d'Action pour la mise à jour de l'interface graphique sur le même thread que l'interface utilisateur graphique de l'élément, mais à partir de la méthode qui est la manipulation des données en arrière-plan. Vous pouvez voir ce code dans le formulaire spécifique akjoshi la réponse ci-dessus. C'est une version générale.
La partie critique est l'appel de l'opérateur de votre INTERFACE utilisateur de l'objet qui vous assure d'avoir le bon thread.
Par expérience personnelle, il semble beaucoup plus facile de créer et d'utiliser l'Action inline comme ça. Déclarant au niveau de la classe m'a donné beaucoup de problèmes de statique/non-statique contextes.
Vous devez utiliser
Dispatcher.BeginInvoke
. Je n'ai pas tester, mais vous pouvez vérifier cette lien(c'est le même lien fourni par Julio G) d'avoir une meilleure compréhension sur la façon de mettre à jour les contrôles d'INTERFACE utilisateur de thread différent. J'ai modifié votreReadData()
codeVous avez deux options ici, je pense.
Un serait d'utiliser un BackgroundWorker. C'est une aide pour le multithreading dans les applications. Il expose un événement DoWork qui est géré sur un thread d'arrière-plan à partir du Pool de Threads et un RunWorkerCompleted événement qui est appelé en arrière sur le thread principal lorsque le thread d'arrière-plan se termine. Il a également l'avantage d'essayer/attraper le code en cours d'exécution sur le thread d'arrière-plan de sorte qu'une exception non gérée ne pas tuer l'application.
Si vous ne voulez pas aller dans cette voie, vous pouvez utiliser le répartiteur WPF objet d'invoquer une action de mise à jour de l'interface graphique de retour sur le thread principal. Aléatoire référence:
http://www.switchonthecode.com/tutorials/working-with-the-wpf-dispatcher
Il existe de nombreuses autres options autour de trop, mais ce sont les deux plus courantes qui viennent à l'esprit.
Voici un exemple complet qui met à jour l'INTERFACE utilisateur des zones de texte
et dans le code