Benutzer mit den meisten Antworten
Event-Handling: Seltsames Phänomen auf nicht-Entwicklungsrechnern

Frage
-
Hallo zusammen,
ich habe ein seltsames Phänomen bei der Verwendung von Events.
Ich habe in einem UserControl eine ComboBox, die den Inhalt darüber erhält, dass ein Ereignis geschmissen wird und in der Methode zur Ereignisbehandlung werden den EventArgs die Entsprechenden Werte zugewiesen. Dies funktioniert auf meinem Rechner auch ohne Probleme, wenn ich jedoch alle Assemblies auf einen Rechner kopiere oder installiere, wird dem Ereignis keine Ereignisbehandlung zugewiesen.
Hier der Code des Steuerelements:
//Methode im Steuerelement. //Soll das Ereignis werfen und so die Daten für die //ComboBox erhalten. //m_Logger -> log4Net protected override void OnLoad(EventArgs e) { base.OnLoad(e); if (RetrieveColumns != null) { //Dies trifft auf meinem Rechner zu m_Logger.Debug("Retrieving Columns"); var args = new RetrieveColumnsEventArgs(); RetrieveColumns(this, args); m_Columns = args.Columns; m_DefaultColumn = args.DefaultColumn; if (m_Columns != null) m_Logger.DebugFormat("Columns retrieved: {0} Entries; DefaultColumn: {1}", m_Columns.Length, m_DefaultColumn); else m_Logger.Debug("Columns are empty"); UpdateComponents(); } else { // Dies trifft auf allen anderen Rechnern zu. m_Logger.Debug("RetrieveColumns event not assigned"); } } //Ereignis das geworfen werden soll public event EventHandler<RetrieveColumnsEventArgs> RetrieveColumns; //Die EventArgs des Ereignisses. public sealed class RetrieveColumnsEventArgs : EventArgs { public string[] Columns { get; set; } public string DefaultColumn { get; set; } }
Hier der Code der Windows-Form, die das Steuerelement enthält:
//Zuweisung der Ereignisbehandlung private void InitializeComponent() { //nur ein Auszug this.myControl.RetrieveColumns += new System.EventHandler<RetrieveColumnsEventArgs>(this.OnRetrieveDropDownValues); } //Methode zur Ereignisbehandlung private void OnRetrieveDropDownValues(object sender, RetrieveColumnsEventArgs e) { if (m_Columns == null) m_Columns = new string[]{"a", "b", "c"}; e.Columns = m_Columns; e.DefaultColumn = m_Columns[0]; }
Nun, wie bereits kommentiert ist auf allen anderen Rechnern, bis auf meinem, RetrieveColumns == null.
Wie kann so etwas kommen? Kann es mit der Signierung o.ä. zu tun haben?
Danke schon im voraus für eure Hilfe.
Gruß, Mike
Antworten
-
Hallo Stefan,
danke für deine Antwort. Ich habe in der Zwischenzeit herausgefunden, woran es lag.
Und zwar hatte ich auf dem UserControl ein Panel, dessen Sichtbarkeit ich über eine neue Eigenschaft des UserControls gesteuert habe
public bool ShowInput { get { return inputPanel.Visible; } set { inputPanel.Visible = value; } }
Diese Eigenschaft wurde jedoch in der InitializeComponent-Methode des Fensters gesetzt, bevor die Ereignisbehandlungen erzeugt wurden. Durch das setzen der ShowInput Eigenschaft wurde dann die CreateControl-Methode und darüber OnLoad aufgerufen. Dadurch waren zu dem Zeitpunkt noch keine Ereignisbehandlungen hinterlegt.
Die Frage, die ich mir allerdings stelle, ist, warum funktionierte das auf meinem Rechner (auch wenn ich die .exe aus dem Explorer gestartet habe) aber auf anderen System funktionierte es nicht. Wenn schon, sollte es einheitlich funktionieren.
Gruß, Mike
- Als Antwort markiert Software-Entwickler Dienstag, 12. April 2011 12:02
-
Hallo Mike, Hallo Stefan,
Die Frage, die ich mir allerdings stelle, ist, warum funktionierte das auf meinem Rechner (auch wenn ich die .exe aus dem Explorer gestartet habe) aber auf anderen System funktionierte es nicht. Wenn schon, sollte es einheitlich funktionieren.
Timing-Probleme sind naturgemäß vom System abhängig, auf dem eine Anwendung ausgeführt wird.
Die Dokumentation sagt ganz klar:
"Das Load-Ereignis tritt ein, wenn der Handle für das UserControl erstellt wird."
Dieser Zeitpunkt ist eben nicht einheitlich auf verschiedenen Systemen und hängt von einer Menge Faktoren ab, die sich nicht exakt quantifizieren lassen. Darum sollte man unter anderem auch keine Datenbindung in OnLoad vornehmen.UserControl.Load-Ereignis (s. Abschnitt "Vorsicht"):
http://msdn.microsoft.com/de-de/library/system.windows.forms.usercontrol.load.aspxGruß
Marcel
- Als Antwort markiert Software-Entwickler Mittwoch, 13. April 2011 05:36
Alle Antworten
-
Hallo Stefan,
danke für deine Antwort. Ich habe in der Zwischenzeit herausgefunden, woran es lag.
Und zwar hatte ich auf dem UserControl ein Panel, dessen Sichtbarkeit ich über eine neue Eigenschaft des UserControls gesteuert habe
public bool ShowInput { get { return inputPanel.Visible; } set { inputPanel.Visible = value; } }
Diese Eigenschaft wurde jedoch in der InitializeComponent-Methode des Fensters gesetzt, bevor die Ereignisbehandlungen erzeugt wurden. Durch das setzen der ShowInput Eigenschaft wurde dann die CreateControl-Methode und darüber OnLoad aufgerufen. Dadurch waren zu dem Zeitpunkt noch keine Ereignisbehandlungen hinterlegt.
Die Frage, die ich mir allerdings stelle, ist, warum funktionierte das auf meinem Rechner (auch wenn ich die .exe aus dem Explorer gestartet habe) aber auf anderen System funktionierte es nicht. Wenn schon, sollte es einheitlich funktionieren.
Gruß, Mike
- Als Antwort markiert Software-Entwickler Dienstag, 12. April 2011 12:02
-
Hallo Mike, Hallo Stefan,
Die Frage, die ich mir allerdings stelle, ist, warum funktionierte das auf meinem Rechner (auch wenn ich die .exe aus dem Explorer gestartet habe) aber auf anderen System funktionierte es nicht. Wenn schon, sollte es einheitlich funktionieren.
Timing-Probleme sind naturgemäß vom System abhängig, auf dem eine Anwendung ausgeführt wird.
Die Dokumentation sagt ganz klar:
"Das Load-Ereignis tritt ein, wenn der Handle für das UserControl erstellt wird."
Dieser Zeitpunkt ist eben nicht einheitlich auf verschiedenen Systemen und hängt von einer Menge Faktoren ab, die sich nicht exakt quantifizieren lassen. Darum sollte man unter anderem auch keine Datenbindung in OnLoad vornehmen.UserControl.Load-Ereignis (s. Abschnitt "Vorsicht"):
http://msdn.microsoft.com/de-de/library/system.windows.forms.usercontrol.load.aspxGruß
Marcel
- Als Antwort markiert Software-Entwickler Mittwoch, 13. April 2011 05:36
-
Guten morgen Marcel,
vielen Dank für deinen Hinweis. Gibt es eine weitere Methode, in der ich das RetrieveColumns-Ereignis auslösen kann? Im Konstruktor, wie im MSDN-Artikel beschrieben, bring es mir ja auch nicht - obwohl es dann immerhin auf allen Systemen einheitlich nicht funktionieren würde ;-)
Die Daten erst beim anzeigen des UserControls laden zu lassen möchte ich auch nicht so gern.
Gruß, Mike
-
Hallo Mike,
Gibt es eine weitere Methode, in der ich das RetrieveColumns-Ereignis auslösen kann?
Das eigentliche Problem in Deinem Code besteht darin, dass das RetrieveColumns-Ereignis in Form1 erst nach der Ausführung von UserControl1.OnLoad() abonniert wird. Wir haben es also mit zwei Problemen zu tun: 1) der Zeitpunkt zu dem UserControl1.OnLoad() aufgerufen wird (das ist soweit klar, glaube ich) und 2) der Zeitpunkt zu dem das RetrieveColumns-Ereignis in Form1 abonniert wird. Da wir ja wissen, dass OnLoad nur nach dem Erstellen eines Handles ausgeführt werden kann, könnte man UserControl1.HandleCreated in Form1 abonnieren und erst dort das UserControl1.RetrieveColumns-Ereignis abonnieren. Die Ausführungs-Sequenz würde in etwa so aussehen:
-------------------------------- 1. Control.CreateControl() -------------------------------- if(!this.IsHandleCreated) { this.CreateHandle(); } -------------------------------- 2. Control.WndProc mit m.Msg == 1 -------------------------------- Control.WmCreate(ref m) -------------------------------- 3. Control.WmCreate(ref m) -------------------------------- DefWndProc(ref m) OnHandleCreated() -------------------------------- 4. Control.OnHandleCreated -------------------------------- [...] handler(this) -------------------------------- 5. UserControl.OnHandleCreated -------------------------------- -------------------------------- 6. Form1.UserControl1_OnHandleCreated -------------------------------- userControl1.RetrieveColumns += OnRetrieveColumns [...] this.OnCreateControl(); -------------------------------- 7. UserControl.OnCreateControl() -------------------------------- [...] OnLoad(); -------------------------------- 8. UserControl.OnLoad() -------------------------------- [...] RetrieveColumns(this, args); -------------------------------- 9. Form1.OnRetrieveColumns() -------------------------------- e.Columns = m_Columns; [...]
Wenn wir uns aber das Hollywood-Prinzip mal vergegenwärtigen ("Don't call us, we'll call you."), könnte man das Ganze vom Design her auch so abändern, dass die Form eine Schnittstelle mit der Methode RetrieveColumns implementiert welche vom UserControl, sobald OnLoad() ausgeführt wird, direkt aufgerufen werden kann. Mir scheint das einfacher.
Gruß
Marcel