Hallo,
ich habe folgendes Problem:
ich möchte Daten aus dem SerialPort lesen und in einer Textbox
darstellen (ich weiß, das gab's sicher schon tausend Mal). Nun habe ich
schon herausgefunden (ich bin noch ziemlich grün hinter den Ohren, was
C# und Visual Studio angeht), dass das ganze als Multithread läuft (Form
und Serielle Schnittstelle) und deshalb mittels Delegate und Invoke
gelöst werden muss. Leider gibt mein superdickes sonst ganz nettes
C#-Buch nichts über Invoke her. Kurzum, ich bekomme eine Fehlermeldung,
was die Argumente des Delegates angeht. Und auch bei der Invoke-Methode
meckert er etwas an.
Ich habe ein Delegate erzeugt, das dann die SerialPort.ReadExisting()
Methode aufnehmen soll. Mittels Invoke soll dann im DataReceivedEvent
der Schnittstelle die TextBox aktualisiert werden.
Schaut mal bitte kurz auf den Quellcode und helft mir weiter.
Danke, Alex
Danke erstmal, vielleicht kann mir trotzdem noch jemand sagen, warum ich
immer den Fehler bekomme, dass das Delegate keine 1-Aurgumente aufnehmen
könne. Meine Delegate ist doch vom selben Typ wie die Funktion, die er
übergeben soll. Woher dann die Fehlermeldung?
Danke, Alex
Hallo Chris E.
nochmal viele Dankj für dein Tip, hat alles funktioniert. Beim
nachvollziehen gibt es noch eine kleine Unklarheit, vielleicht kannst du
mir nochmal kurz weiterhelfen.
wenn if(textBox.InvokjeRequered) == true, wird wieder SetText aufgerufen
usw.
Mir ist etwas unklar, wie und wann dieser "Rekursive Aufruf" endet.
Gruß, Alex
Alex schrieb:
> Mir ist etwas unklar, wie und wann dieser "Rekursive Aufruf" endet.
Normalerweise nach dem ersten 'rekursiven Aufruf', der im übrigen nicht
wirklich eine Rekursion ist.
Im Grunde geht es nur darum, den Kontext auf den anderen Thread
umzuschalten. InvokeRequired stellt fest ob dies notwendig ist und
startet die Funktion nochmal in diesem Kontext.
d.h. beim zweiten Durchlauf der Funktion liefert dann
if(textBox.InvokjeRequered) == false und der else-Zweig wird ausgeführt,
verstehe ich das richtig?
Gruß, Alex
Hi, möchte die von Chris oben genannte Lösung gern so erweitern, dass
ich universell von jedem Control den Text ändern kann. Ich hab das wie
folgt versucht (s.u.), komme aber mit der Argumentübergabe des
Action<T1, T2> Delegates nicht so richtig klar (hab schon allerlei
Varianten versucht), sodass ich immer eine ArgumentException bekomme.
private void SetText(string text, Control myControl)
{
//Nicht in GUI-Thread -> Invoke
if(myControl.InvokeRequired) myControl.Invoke(new Action<string,
Control>(this.SetText), new object[] {text}, new object());
//"Rekursiever" Aufruf
//In GUI Thread -> Text setzten
else myControl.Text = text;
}
private string txt = "";
private void serialPort1_DataReceived(object sender,
SerialDataReceivedEventArgs e)
{
txt += myPort.ReadExisting();
SetText(txt, myTextBox);
}
Für den entscheidenden Hinweis wäre ich dankbar,
Gruß, Alex
Alex schrieb:
> Hi, möchte die von Chris oben genannte Lösung gern so erweitern, dass> ich universell von jedem Control den Text ändern kann. Ich hab das wie> folgt versucht (s.u.), komme aber mit der Argumentübergabe des> Action<T1, T2> Delegates nicht so richtig klar (hab schon allerlei> Varianten versucht), sodass ich immer eine ArgumentException bekomme.>> private void SetText(string text, Control myControl)> {> //Nicht in GUI-Thread -> Invoke> if(myControl.InvokeRequired) myControl.Invoke(new Action<string,> Control>(this.SetText), new object[] {text}, new object());> //"Rekursiever" Aufruf>> //In GUI Thread -> Text setzten> else myControl.Text = text;> }> Für den entscheidenden Hinweis wäre ich dankbar,>> Gruß, Alex
Übersichtlicher wird's, wenn man anonyme Funktionen verwendet
Alex schrieb:
> vielen Dank,>> Variante 1 funktioniert. Bei Variante 2 bekomme ich eine Fehlermeldung> für addText. Meintest Du AddText?>> Gruß, Alex
SetText2 war gemeint...
Hallo zusammen,
Thread ist schon etwas älter, aber imer noch aktuell.
Ich verwende seit Jahren in etwa die Variante 1.
Ab und zu kommt es vor, das ich ein Programm beende und die Ausführung
genau bei c.Invoke(... hängen bleibt. Das Programm wird dann nie
beendet.
Hat jemand für dieses Problem eine elegante Lösung?
Momentan versuche ich es auf diese Weise zu lösen:
if (!this.Disposing) c.Invoke(...
Bin aber nicht sicher ob das immer funktioniert, wie gesagt, tritt nur
sehr selten auf und lässt sich schlecht nachvollziehen.
Gruß, Markus
Lösung gefunden!
Ist zwar etwas unschön, da es eigentlich über Standard Methoden zu
erledigen sein sollte, hat aber alles nicht geklappt.
Ich setze im Formclosing-Event eine globale boolsche Variable
isFormClosing auf true, die ich vor dem Invoke-Aufruf abfrage. Schon
klappts!
Alle anderen Varianten mit Abfrage von this.Disposing oder c.Disposing
usw. funktionieren nicht. Anscheinend werden diese Objekt-Eigenschaften
zu spät gesetzt.
Code-Beispiel (funktioniert natürlich auch mit Label etc. statt Textbox:
bool formIsClosing = false;
/// <summary>
/// Is called when the form is closing
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void CSMLParser_FormClosing(object sender, FormClosingEventArgs
e)
{
formIsClosing = true;
}
/// <summary>
/// A delegate for thread safe text display
/// </summary>
/// <param name="tb">The textbox-object which is to be used</param>
/// <param name="msg">The message that has to be shown</param>
delegate void showTextinTBCB(TextBox tb, String msg);
/// <summary>
/// Shows the text msg in textbox tb
/// </summary>
/// <param name="tb">The textbox-object which is to be used</param>
/// <param name="msg">The message that has to be shown</param>
public void showTextinTB(TextBox tb, String msg)
{
try
{
if (tb.InvokeRequired)
{
showTextinTBCB d = new showTextinTBCB(showTextinTB);
if (!formIsClosing)
Invoke(d, new object[] { tb, msg });
//else
// MessageBox.Show("Invoke-Error");
}
else
{
tb.Text = msg;
tb.ScrollToCaret();
}
}
catch (Exception) { }
}
Gruß, Markus