none
C# Nachricht / Event von innerem UserControl in äußeres UserControl weitergeben RRS feed

  • Frage

  • Hallo,

    ich rufe von einer Page aus ein UserControl1 auf.
    Via Event-Handler übergebe ich Daten vom UcerControl1 zur Page.
    (entsprechend dieser Lösung!)

    Nun rufe ich innerhalb des oben genannten UserControl1 ein weiteres UserControl2 auf.
    Die darin manipulierten Daten müssen am Ende bei der Page ankommen, ob über Umweg UserControl1 oder direkt ist mir egal.
    Aber das UserControl1 kann nur vom UserControl1 initialisiert werden und nicht bereits in der Page.

    Ich habe im UserControl2 das Event und das Datenobjekt deklariert:

    public event EventHandler<MyEventArgs> ContentChanged;

            private BsSynthesis.SStep _step;
            public BsSynthesis.SStep step
            {
                get { return _step; }
                set { _step = value; }
            }

    eine Methode zum Aufruf des Event-Handlers angelegt:

            private void setContentChangedEventHandler()
            {
                var evt = ContentChanged;
                if (ContentChanged != null)
                    evt(this, new MyEventArgs(_step));
            }

    Nun müsste ich im UserControl1 das Objekt des UserControl2 für das Event anmelden.
    userControl2.ContentChanged += ucDataChanged;
    aber das geht natürlich nicht, da ucDataChanged() in der Page steht und nicht im UserControl1.

    Zusammenfassend meine Frage:
    Wie bekomme ich meine Daten von UserControl2 zur Page?


    www.mehlhop.com

    Donnerstag, 27. März 2014 14:17

Antworten

  • Hallo,
    wenn ContextChanged in UC1 immer null ist, dann scheint es so, als wenn das Event von der Seite nicht abonniert wurde.

    Ich habe das Ganze in einer Konsole getestet. Hier mal der Code dazu:

    class Program
    {
        static void Main(string[] args)
        {
            UserControl1 c1 = new UserControl1();
            c1.ContentChanged += c1_ContentChanged;//Event nur in C1 abonnieren
            c1.C2.setContentChangedEventHandler();//Event in C2 auslösen
            Console.ReadKey();
        }
    
        static void c1_ContentChanged(object sender, EventArgs e)
        {
            Console.WriteLine("Event ist angekommen");
        }
    }
    
    class UserControl1
    {
        public UserControl1()
        {
            this.C2 = new UserControl2();//Zuweisung
            C2.ContentChanged += C2_ContentChanged;//Gehöhrt auch zu Lösung 2
        }
    
        //Lösung 2
        void C2_ContentChanged(object sender, EventArgs e)
        {
            var evt = ContentChanged;
            if (ContentChanged != null)
                evt(this, new EventArgs());
        }
        public event EventHandler<EventArgs> ContentChanged;
    
        //Lösung 3
        ////Adaptiert das Event nach UC2
        //public event EventHandler<EventArgs> ContentChanged
        //{
        //    add
        //    {
        //        this.C2.ContentChanged += value;
        //    }
        //    remove
        //    {
        //        this.C2.ContentChanged -= value;
        //    }
        //}
        public UserControl2 C2 { get; set; }
    }
    class UserControl2
    {
        public event EventHandler<EventArgs> ContentChanged;
        public void setContentChangedEventHandler()//Public, damit ich es von außen auslösen kann
        {
            var evt = ContentChanged;
            if (ContentChanged != null)
                evt(this, new EventArgs());
        }
    }

    Bei richtigen Controls ist das nichts anderes, da es auch nur Klassen sind.

    In der Lölsung siehst du auch die Implementation von Lösung 3. Diese funktioniert genauso ohne Probleme.

    Was ich außerdem nicht verstehe ist, dass bei der Event-Handler-Methode ucKPDataChanged im UC1 (siehe oben) die Fehlermeldung kommt:
    Der Typ 'BioSyn.CreateSynthesis.SynUserControls.UCLuerDock' enthält bereits eine Definition für 'ucKPDataChanged'.

    In dem Fall kannst du mal in deine Klasse UCLuerDock klicken und oben rechts in der 2. ComboBox nach ucKPDataChanged suchen. Dort sollten dann 2 Einträge aufgelistet sein. Einer ist die Eventhandlermethode und das Andere verursacht den Konflikt.
    Diese Liste (rot umrahmt) meine ich:

    Ich vermute mal, dass Lösung 2 (und vermutlich auch 3) nicht funktionieren liegt daran, dass irgendetwas falsch benannt ist.


    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    • Als Antwort markiert frank me Montag, 31. März 2014 08:02
    Freitag, 28. März 2014 13:43
    Moderator

Alle Antworten

  • Hallo,
    grundsätzlich hast du 3 Möglichkeiten:

    1. Die unschöne Möglichkeit:
      Du Deklarierst die Instanz von UC2 in UC1 als public oder internal. Dadurch kann die Page dann über uc.UC2.Event zugreifen. Diese Variante ist aber eher unschön, weil darüber alles an UC2 geändert werden kann.
    2. Die "einfachste" Variante:
      Du erstellst genau das selbe Event noch einmal in UC1. Nun abonniert UC1 das Event von UC2. Sobald es ausgelöst wurde, löst UC1 sein eigenes Event aus, welches wiederrum von Page abonniert wurde. Hierbei gehst du also über 2 Events.
    3. Der eleganteste Weg:
      Du deklarierst in UC1 einen Eventhandler, der wie folgt aussieht (die Klassen musst du entsprechend anpassen):
      public event EventHandler<EventArgs> ContentChanged
      {
          add
          {
              this.C2.ContentChanged += value;
          }
          remove
          {
              this.C2.ContentChanged -= value;
          }
      }
      public UserControl2 C2 { get; set; }
      Dadurch wird nun das Abonnieren bzw. Deabonnieren direkt an das eigentliche Event in UC2 übertragen.

    Ich würde wahrscheinlich Möglichkeit 3 verwenden.


    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    Donnerstag, 27. März 2014 14:41
    Moderator
  • 4. MVVM Pattern verwenden

    Übergib deinen User Controlls ein Objekt, welches dem ViewModel entspricht auf dem kannst du dann Arbeiten.

    Schau dir mal das Beispiel an.

    Donnerstag, 27. März 2014 15:57
  • Danke für Eure Vorschläge!!!

    Den 3.Weg habe ich versucht und wegen diverser Konflikte erst mal auf Eis gelegt.

    Den 2.Weg hatte ich zuvor auch schon versucht und nun noch einmal.
    Aber ich scheitere immer daran, dass wenn im UC2 die Methode:

            private void setContentChangedEventHandler()
            {
                var evt = ContentChanged;
                if (ContentChanged != null)
                    evt(this, new MyEventArgs(_step));
            }

    aufgerufen wird ContextChanged immer null ist und daher die Daten nicht übergeben werden.

    In UC2 deklariere ich das Event wie folgt:

            public event EventHandler<MyEventArgs> ucKPDataChanged;

    In UC1 steht folgende Methode:

            private void uc2DataChanged(object sender, MyEventArgs e)
            {
                step = e.step;
                bsSynthesis.CurrentStep = step;
            }

    und beim deklarieren des UC2 melde ich das Event an:

    uc2.ContentChanged += uc2DataChanged;
    Was ich außerdem nicht verstehe ist, dass bei der Event-Handler-Methode ucKPDataChanged im UC1 (siehe oben) die Fehlermeldung kommt:
    Der Typ 'BioSyn.CreateSynthesis.SynUserControls.UCLuerDock' enthält bereits eine Definition für 'ucKPDataChanged'.
    Womöglich steht das Problem, dass ContextChanged null ist und diese Fehlermeldung im Zusammenhang, aber ich kann es nicht deuten und würde mich über weitere Hilfe sehr freuen.


    www.mehlhop.com


    • Bearbeitet frank me Freitag, 28. März 2014 13:24
    Freitag, 28. März 2014 10:09
  • Hi Frank,

    dein Code ist so wirklich schwer nachzuvollziehen. Ist uc jetzt UC1 oder UC2. Benenne die Variablen bitte sinnvoll, das macht es für andere Leichte deinen Code zu verstehen.

    Nun wenn ich dich richtig verstanden habe ist ContentChanged, das Event in UC2 was UC1 abonnieren soll. Wenn uc.ContentChanged += ucKPDataChanged in UC1 steht (und auch ausgeführt wird) scheint. Das schon mal richtig zu sein. Die Methode ucKPDataChange sollte dann im UC1 sein.

    Das Event ucKPDataChange in UC2 ergibt für mich hier keinen Sinn, ich denke das könnte der Fehler sein.

    Ansonsten erstelle mal ein einfache Beispiel Projekt, und Poste den ganzen Code so das man es auch nachvollziehen kann.

    Mit freundlichen Grüßen

    Björn

    Freitag, 28. März 2014 11:23
  • Hi Björn,

    danke für den Hinweis mit den missverständlichen Objektbezeichnungen. Ich habe sie (oben) angepasst.

    Dass die Methode setContentChangeEventHandler() in UC1 muss glaube ich nicht. Das selbe Konstrukt verwende ich auch, um Daten von UC1 zur Page zu schicken und auch dort habe ich die Methode im sendenden Objekt (UC1). Und das funktioniert erstklassig. Innerhalb dieser Methode wird das Event gefeuert und damit die Daten mitgegeben.


    www.mehlhop.com


    • Bearbeitet frank me Freitag, 28. März 2014 13:29
    Freitag, 28. März 2014 13:29
  • Hallo,
    wenn ContextChanged in UC1 immer null ist, dann scheint es so, als wenn das Event von der Seite nicht abonniert wurde.

    Ich habe das Ganze in einer Konsole getestet. Hier mal der Code dazu:

    class Program
    {
        static void Main(string[] args)
        {
            UserControl1 c1 = new UserControl1();
            c1.ContentChanged += c1_ContentChanged;//Event nur in C1 abonnieren
            c1.C2.setContentChangedEventHandler();//Event in C2 auslösen
            Console.ReadKey();
        }
    
        static void c1_ContentChanged(object sender, EventArgs e)
        {
            Console.WriteLine("Event ist angekommen");
        }
    }
    
    class UserControl1
    {
        public UserControl1()
        {
            this.C2 = new UserControl2();//Zuweisung
            C2.ContentChanged += C2_ContentChanged;//Gehöhrt auch zu Lösung 2
        }
    
        //Lösung 2
        void C2_ContentChanged(object sender, EventArgs e)
        {
            var evt = ContentChanged;
            if (ContentChanged != null)
                evt(this, new EventArgs());
        }
        public event EventHandler<EventArgs> ContentChanged;
    
        //Lösung 3
        ////Adaptiert das Event nach UC2
        //public event EventHandler<EventArgs> ContentChanged
        //{
        //    add
        //    {
        //        this.C2.ContentChanged += value;
        //    }
        //    remove
        //    {
        //        this.C2.ContentChanged -= value;
        //    }
        //}
        public UserControl2 C2 { get; set; }
    }
    class UserControl2
    {
        public event EventHandler<EventArgs> ContentChanged;
        public void setContentChangedEventHandler()//Public, damit ich es von außen auslösen kann
        {
            var evt = ContentChanged;
            if (ContentChanged != null)
                evt(this, new EventArgs());
        }
    }

    Bei richtigen Controls ist das nichts anderes, da es auch nur Klassen sind.

    In der Lölsung siehst du auch die Implementation von Lösung 3. Diese funktioniert genauso ohne Probleme.

    Was ich außerdem nicht verstehe ist, dass bei der Event-Handler-Methode ucKPDataChanged im UC1 (siehe oben) die Fehlermeldung kommt:
    Der Typ 'BioSyn.CreateSynthesis.SynUserControls.UCLuerDock' enthält bereits eine Definition für 'ucKPDataChanged'.

    In dem Fall kannst du mal in deine Klasse UCLuerDock klicken und oben rechts in der 2. ComboBox nach ucKPDataChanged suchen. Dort sollten dann 2 Einträge aufgelistet sein. Einer ist die Eventhandlermethode und das Andere verursacht den Konflikt.
    Diese Liste (rot umrahmt) meine ich:

    Ich vermute mal, dass Lösung 2 (und vermutlich auch 3) nicht funktionieren liegt daran, dass irgendetwas falsch benannt ist.


    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    • Als Antwort markiert frank me Montag, 31. März 2014 08:02
    Freitag, 28. März 2014 13:43
    Moderator
  • Hallo,
    wenn ContextChanged in UC1 immer null ist, dann scheint es so, als wenn das Event von der Seite nicht abonniert wurde.

    Ne ne, uc2ContextChanged in UC2 ist null.

    Bei Änderungen in UC2 rufe ich die Methode 

            private void setContentChangedEventHandler()
            {
                var evt = uc2DataChanged;
                if (ucKPDataChanged != null)
                    evt(this, new MyEventArgs(_step));
            }

    auf und uc2DataChanged ist null.

    (sorry, wen ich mich schlecht ausgedrückt habe.)

    Ich werde mir nun dein Konsolenbeispiel anschauen...


    www.mehlhop.com

    Freitag, 28. März 2014 14:05
  • Mir ist da etwas in setContentChangedEventHandler unschlüssig. Du löst das mit das uc2DataChanged-Event aus, fragst aber das ucKPDataChanged-Event ab.
    Das kann in den meisten Fällen so nicht funktionieren. Du musst das Event abfragen, welches du in evt gespeichert hast.

    Sonst sind Events nur null, wenn sie nicht abonniert sind. In dem Fall könnte also das Abonnieren von uc2.DataChanged in UC1 fehlen.


    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    Freitag, 28. März 2014 14:23
    Moderator
  • Vielen Dank für Eure Unterstützung!

    Mit dem Beispiel von Tom konnte ich ich meine Codesuppe anpassen, so dass es funktioniert wie gewünscht.

    DANKE! :)


    www.mehlhop.com

    Montag, 31. März 2014 08:03