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
Alex schrieb: > Leider gibt mein superdickes sonst ganz nettes > C#-Buch nichts über Invoke her. Dafür die Suchfunktion hier im Forum: http://www.mikrocontroller.net/search?query=invoke&forums[]=8&max_age=-&sort_by_date=0
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
Hi, So wie ich das sehe wendest du Invoke einfach falsch an. Ich hab das Ganze mal korrigiert.
1 | |
2 | private void SetText(string text) { |
3 | //Nicht in GUI-Thread -> Invoke
|
4 | if(textBox1.InvokeRequired) textBox1.Invoke(new Action<string>(this.SetText), new object[] {text}); //"Rekursiever" Aufruf |
5 | |
6 | //In GUI Thread -> Text setzten
|
7 | else textBox1.Text = text; |
8 | }
|
9 | |
10 | private string txt = ""; |
11 | |
12 | private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e) |
13 | {
|
14 | txt += myPort.ReadExisting(); |
15 | SetText(txt); |
16 | |
17 | //textBoxUpdateDelegate = new TextBoxUpdateDelegate(myPort.ReadExisting);
|
18 | //textBox1.Text = this.Invoke(textBoxUpdateDelegate());
|
19 | }
|
Hier noch ein guter Artikel zu dem Thema: http://www.mycsharp.de/wbb2/thread.php?threadid=33113 greets PS: es wäre immer hilfreich wenn du die exakte Fehlermeldung postest
Ich werd's gleich mal lesen und testen. Vielen Dank. Gruß, 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
Du hast einen Debugger. Du kannst Breakpoints setzen. Du kannst dein Programm in Einzelschritten durchgehen.
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
1 | // Variante 1
|
2 | // Umwandlung anonyme Funktion -> delegate
|
3 | delegate void SetTextDelegate(Control c, string s); |
4 | SetTextDelegate setTextDel = (a, b) => a.Text = b; |
5 | private void SetText(Control c, string s) { |
6 | if (c.InvokeRequired) { |
7 | c.Invoke(setTextDel, new object[] { c, s }); |
8 | } else { |
9 | c.Text = s; |
10 | }
|
11 | }
|
12 | |
13 | // Variante 2
|
14 | private void SetText2(Control c, string s) { |
15 | if (c.InvokeRequired) { |
16 | Action<Control, string> act = new Action<Control, string>((a, b) => addText(a, b)); |
17 | c.Invoke(act, new object[] { c, s }); |
18 | } else { |
19 | c.Text = s; |
20 | }
|
21 | }
|
vielen Dank, Variante 1 funktioniert. Bei Variante 2 bekomme ich eine Fehlermeldung für addText. Meintest Du AddText? Gruß, Alex
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...
Besser - ohne die Methode immer aufzuruffen, Ereigniss ins Queue packen:
1 | private void AddText(String msg) |
2 | {
|
3 | if (this.textBox1.InvokeRequired) |
4 | {
|
5 | this.textBox1.Invoke(new EventHandler(delegate { this.textBox1.Text += msg; })); |
6 | }
|
7 | else
|
8 | {
|
9 | this.textBox1.Text += msg; |
10 | }
|
11 | }
|
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
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.