Entwickler-Ecke

WinForms - Zugriff auf die Haupt-Form aus der Programm.cs


Määx - Fr 08.02.13 16:10
Titel: Zugriff auf die Haupt-Form aus der Programm.cs
Hallo zusammen,

ich bin relativ neu in der C#-Welt und habe nun folgendes Problem: in der Main-Methode erzeuge ich in einer globalen Variabel meine Haupt-Form MainApp und rufe sie auf. Weiterhin initialisiere ich in der Main-Methode auch einige SOAP-Services und MSMQ-Messaging. Beim eintreffen einer neuen Nachricht soll nun auf dem TabControl im MainApp erstellt werden. Hierfür habe ich mir eine kleine Methode in der MainApp.cs geschrieben die auch wunderbar funktioniert wenn ich sie durch einen Button-Klick oder ähnlichem aufrufe. Beim Aufruf aus der Programm.cs funktioniert sie leider nicht. Es ist, als ob sich die Referenz auf meine MainApp ändern würde oderso... kan mir jmd. einen Tipp geben, woran es liegen könnte?
Hier noch der wichtigste Code:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
static class Program
    {
        static MainApp mainApp;

        static void Main()
        {
            Application.ApplicationExit += new EventHandler(OnApplicationExit);
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            
            [...]

            mainApp = new MainApp();
            mainApp.addNewTab("Test");

            Application.Run( mainApp );
        }

        public static void newMessage(String msg)
        {
            mainApp.addNewTab(msg);
        }
    }


Vielen Dank schonmal für eure Hilfe!

Edit:
ich habe mein mainApp.addNewTab jetzt in try/catch gesetzt und es kommt tatsächlich folgende Fehlermeldung:
Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement tabControlMsgs erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde.

bin aber absolut ratlos, wie ich das beheben kann...


Ralf Jansen - Fr 08.02.13 16:55

Wird denn die newMessage Methode von einem Event der besagten SOAP bzw. MSMQ Services asynchron aufgerufen? Wenn ja solltest du mal chechen ob das nicht eine andere Thread ist als der zu dem deine Form gehört und du in Crossthreading Probleme läufst.


Määx - Di 12.02.13 15:04

Hey,

erstmal vielen Dank für deine Hilfe und entschuldige meine späte Rückmeldung - über Karneval hatte ich meine Anfrage ganz vergessen :)
Das mit dem threading könnte tatsächlich der Fehler sein.
Ich habe eine Klasse erstellt, die die MSMQ-Funktionalität übernimmt:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
[...]
string msmq_name =  ConfigurationManager.AppSettings["MSMQ_NAME"];
if (!MessageQueue.Exists(msmq_name))
    MessageQueue.Create(msmq_name);
MessageQueue receiveQueue = new MessageQueue(msmq_name );
receiveQueue.MulticastAddress = ConfigurationManager.AppSettings["MSMQ_MC_ADDR"];
receiveQueue.BeginReceive();
receiveQueue.ReceiveCompleted += new ReceiveCompletedEventHandler(processRequestNewMsg);
[...]


In der processRequestNewMsg wird die Nachricht dann aufgeschlüsselt und per EventHandler an die Program.cs übergeben (die problematisch newMessage() wird dort aufgerufen). Ich selber starte keinen Thread, das MSMQ.BeginReceive() wird aber sicherlich als Thread arbeiten... aber da ich diese Funktionalitäten ja nicht manipulieren kann, habe ich keine Ahnung wie ich jetzt aus der newMessage-Methode auf meine GUI zugreifen kann?

Kannst du mir hierbei weiter helfen?

Vielen Dank


Ralf Jansen - Di 12.02.13 15:43

Wenn es ein CrossThread Call ist solltest du da eigentlich eine Exception bekommen. Wenn da keine kommt solltest du auf jedenfall mal dein Exceptionhandling prüfen ob du da nicht ein paar Lücken hast.

Um die Form zu ändern bedarfs es eines synchronisierten Aufrufs. Dazu stellen dir die Controls in Winforms die Invoke [http://msdn.microsoft.com/de-de/library/zyzhdc6b%28v=vs.80%29.aspx] Methode zur Verfügung. In deinem Fall könnte das so Aussehen


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
public static void newMessage(String msg)
{
    if(mainApp.InvokeRequired)
        mainApp.Invoke((MethodInvoker)(() => newMessage(mainApp, msg)));
    else            
        mainApp.addNewTab(msg);
}


Määx - Di 12.02.13 16:32

Hey,

super, das ganze klappt! Habe dafür allerdings noch eine weitere Methode erstellen müssen und habe, damit man den Code nicht doppelt schreiben muss den else-Zweig angepasst:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
public static void newMessage(String msg)
{
    if(mainApp.InvokeRequired)
        mainApp.Invoke((MethodInvoker)(() => newMessage(mainApp, msg)));
    else            
        newMessage(mainApp, msg);
}  
public static void newMessage(MainApp mp, String msg){
    mp.addNewTab(msg);
}


Ist das so korrekt oder mache ich das zu kompliziert?

Aber schonmal vielen vielen Dank - wäre ich ohne deine Hilfe nie drauf gekommen!


Ralf Jansen - Di 12.02.13 17:25

Zitat:
Habe dafür allerdings noch eine weitere Methode erstellen müssen und habe, damit man den Code nicht doppelt schreiben muss den else-Zweig angepasst:


Häh? Was müßte man da doppelt schreiben?


Määx - Di 12.02.13 17:36

Hey,

sorry, das war aus meinem gekürzten Code so nicht ersichtlich. Bei Ankunft einer neuen Nachricht mache ich mehr als den einen Aufruf addNewTab. Den hattest du entsprechend in deinem Beispiel ja direkt in die else-Verzweigung gelegt. Ich rufe hier wie beim invoke dann die Methode auf. Das meinte ich mit "doppelt schreiben".

Viele Grüße und vielen Dank für die schnelle & kompetente Hilfe!
Määx