none
Event-Handling: Seltsames Phänomen auf nicht-Entwicklungsrechnern RRS feed

  • 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

    Dienstag, 12. April 2011 08:24

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

    Dienstag, 12. April 2011 11:51
  • 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.aspx

    Gruß
    Marcel



    Dienstag, 12. April 2011 12:02
    Moderator

Alle Antworten

  • hallo,

    Es stellt sich die Frage: Wie wird dein UserControl erzeugt und konfiguriert?

    Microsoft MVP Office Access
    https://mvp.support.microsoft.com/profile/Stefan.Hoffmann
    Dienstag, 12. April 2011 09:18
  • 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

    Dienstag, 12. April 2011 11:51
  • 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.aspx

    Gruß
    Marcel



    Dienstag, 12. April 2011 12:02
    Moderator
  • 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

    Mittwoch, 13. April 2011 05:47
  • 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

    Mittwoch, 13. April 2011 10:05
    Moderator