none
[windows forms] event in event RRS feed

  • Frage

  • Hallo Forum,

    ich habe eine Klasse DB_Import, welche alle Ausgaben in die Konsole schreibt. Nun möchte ich dieser Klasse ein WinForms GUI verpassen. Dabei soll DB_Import möglichst gekapselt (also unabhängig vom Rest des WinForms Programms) sein.

    • Auf einen ButtonClick hin soll die StartImport()-Methode der DB_Import-Klasse gestartet werden.
    • Außerdem möchte ich alle Console.WriteLines("Nachricht") durch events ersetzen: also event("Nachricht") --> ShowMyMessage(string message) {listbox1.Items.Add(message);}

    Leider hagelt es Fehlermeldungen, wenn ich folgendes versuche: (folgender Code ist per Hand eingetippt, kann also Schreibfehler beinhalten)

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; namespace WindowsApplication2 { public partial class Form1 : Form { public Form1() { InitializeComponent(); Text = "MyImport V0.0" } private void button1_Click(object sender, EventArgs e) { DB_Import.StartImport(); } public static void ShowNotification(string notificationStr)

    {

    MessageBox.Show(notificationStr);

    //besser: listBox1.Items.Add(notificationStr); //listBox1 ist aber nicht sichtbar, warum

    } } class DB_Import { public DB_Import() {}

    public delegate void Notify (strig notificationStr);

    public event Notify notifier = new Notify(Form1.ShowNotification);

    public void StartImport() {

    //lots of DB-Code

    //...

    notifier("Instead of Console.WriteLine"); } } }


    Wie müsste ich das Programm strukturieren, damit es wie gewünscht funktioniert?

    MfG Flux



    • Bearbeitet Flux1989 Montag, 6. August 2012 09:19
    Montag, 6. August 2012 08:56

Antworten

  • Hallo, erstmal was grundsätzliches zu den EventHandlern, diese haben immer folgende Syntax:

    Methodenname(object sender, EventArgs e){/*...*/}

    so wie bei deinem

    button1_Click(object sender, EventArgs e)

    Eventhandler.

    In der Klasse DBImport würde ich an deiner Stelle eine eigene, von EventArgs abgeleitete Klasse implementieren und diese im Event übergeben.

        public partial class Form1 : Form
        {
            MyClass mc = new MyClass(); //Deine Klasse instanzieren
            public Form1()
            {
                InitializeComponent();
                mc.ThrowedEvent += mc_ThrowedEvent;
                mc.DoEvent(); //Event erzeugen
            }
    
            void mc_ThrowedEvent(object sender, MyEventArgs e)
            {
                MessageBox.Show(e.String);
            }
        }
    
        class MyClass
        {
            public MyClass()
            {
    
            }
    
            public event EventHandler<MyEventArgs> ThrowedEvent;
    
    
            public void DoEvent()
            {
                if (ThrowedEvent != null) //Überprüfen ob das Event abbonniert ist
                    ThrowedEvent(this, new MyEventArgs("Meldung")); //Event auslösen mit dieser Instanz un ddeinen Parametern
            }
        }
    
        class MyEventArgs : EventArgs //Eiegne Eventsklasse für deinen String
        {
            public readonly string String;
            public MyEventArgs(string s)
            {
                this.String = s;
            }
        }

    Der Code dürfte die reine Funktionalität darstellen, musst du natürlich entsprechend übersetzen und anpassen.
    Du kannst natürlich auch über einen Delegaten arbeiten, aber ich bevorzuge diese Variante.


    Koopakiller - http://koopakiller.ko.ohost.de/

    • Als Antwort markiert Flux1989 Montag, 6. August 2012 15:24
    Montag, 6. August 2012 09:43
    Moderator

Alle Antworten

  • Um innerhalb von Form1.ShowNotification() auf Form1.listBox1 zugreifen zu können, sagt mir der debugger ich müsse Form1 instanziieren.. hm, ok. Also ändere ich Form1.ShowNotification() folgendermaßen:

    public static void ShowNotification(string notificationStr)
    {
      Form1 f1 = new Form1();
      f1.listBox.Items.Add(notificationStr);
    }
    
    Problem: Wenn ich das Programm ausführe und auf button1 klicke, dann bleibt listBox1 leer, warum??

    Montag, 6. August 2012 09:39
  • Hallo, erstmal was grundsätzliches zu den EventHandlern, diese haben immer folgende Syntax:

    Methodenname(object sender, EventArgs e){/*...*/}

    so wie bei deinem

    button1_Click(object sender, EventArgs e)

    Eventhandler.

    In der Klasse DBImport würde ich an deiner Stelle eine eigene, von EventArgs abgeleitete Klasse implementieren und diese im Event übergeben.

        public partial class Form1 : Form
        {
            MyClass mc = new MyClass(); //Deine Klasse instanzieren
            public Form1()
            {
                InitializeComponent();
                mc.ThrowedEvent += mc_ThrowedEvent;
                mc.DoEvent(); //Event erzeugen
            }
    
            void mc_ThrowedEvent(object sender, MyEventArgs e)
            {
                MessageBox.Show(e.String);
            }
        }
    
        class MyClass
        {
            public MyClass()
            {
    
            }
    
            public event EventHandler<MyEventArgs> ThrowedEvent;
    
    
            public void DoEvent()
            {
                if (ThrowedEvent != null) //Überprüfen ob das Event abbonniert ist
                    ThrowedEvent(this, new MyEventArgs("Meldung")); //Event auslösen mit dieser Instanz un ddeinen Parametern
            }
        }
    
        class MyEventArgs : EventArgs //Eiegne Eventsklasse für deinen String
        {
            public readonly string String;
            public MyEventArgs(string s)
            {
                this.String = s;
            }
        }

    Der Code dürfte die reine Funktionalität darstellen, musst du natürlich entsprechend übersetzen und anpassen.
    Du kannst natürlich auch über einen Delegaten arbeiten, aber ich bevorzuge diese Variante.


    Koopakiller - http://koopakiller.ko.ohost.de/

    • Als Antwort markiert Flux1989 Montag, 6. August 2012 15:24
    Montag, 6. August 2012 09:43
    Moderator
  • Es gibt mehrere Möglichkeiten, um eine Information aus dem Objekt an den Nutzer zu übergeben:
     
    1. Du teilst dem Objekt eine Referenz auf das aufrufende Objekt mit, z.B. im Konstruktor übergibst Du this als Verweis auf das Form1-Objekt. Damit kannst Du dann auf alle Innereien von Form1 zugreifen. Diese Lösung ist nicht optimal, da Dein Objekt den Typ (=Klasse) “Form1” kennen muss.
     
    2. Eine im Objekt aufgerufene Methode gibt als Rückgabewert Information zurück.
     
    3. Der Aufrufer fragt eine Eigenschaft des Objektes ab, um sich damit Information über den Zustand des Objektes zu holen.
     
    4. Dem Objekt wird ein Delegate übergeben, den das Objekt für einen “Rückruf” nutzt, um beim “Rückruf” Information zurückzusenden.
     
    5. Dein Objekt bietet ein Ereignis (event), welches das Objekt auslösen kann, um einen neuen Zustand zu signalisieren. Derjenige, der das Ereignis abonniert hat, kann sich entweder den neuen Zustand durch Auslesen (analog 3.) abholen, oder bekommt die Information über die Ereignisargumente in der Ereignisroutine geliefert.
     
    --
    Viele Gruesse
    Peter
    Montag, 6. August 2012 10:01
  • Danke euch. Allerdings erhalte ich folgende Fehlermeldung:

    Fehler 1 Inkonsistenter Zugriff: Parametertyp "WindowsApplication2.MyEventArgs" ist weniger

    zugreifbar als Methode "WindowsApplication2.Form1.ShowNotification(object, WindowsApplication2.MyEventArgs)"

    C:\Users\JB\Documents\Visual Studio 2005\Projects\WindowsApplication2\WindowsApplication2\Form1.cs

    26 28 WindowsApplication2

    Hier mein Code:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    
    namespace WindowsApplication2
    {
    
        public partial class Form1 : Form
        {
            DB_Import it = new DB_Import();
    
            public Form1()
            {
                InitializeComponent();
                Text = "Import src2dst V 0.9";
    
               // it.ThrownEvent += it_ShowNotification;
    
                //ThrownEvent(this, new MyEventArgs("Instead of Console.WriteLine"));
            }
    
            public static void ShowNotification(object sender, MyEventArgs e)
            {
                MessageBox.Show(e.EventString);
    
                //Form1 f1 = new Form1();
               // f1.listBox1.Items.Add(notificationStr);
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                it.StartImport();
            }
    
        }
    
    
        class DB_Import
        {
            public DB_Import() { }
    
    
            public event EventHandler<MyEventArgs> ThrownEvent = new EventHandler<MyEventArgs>(Form1.ShowNotification);
    
    
            public void StartImport()
            {
                //lots of DB-Code...
                
    
                ThrownEvent(this, new MyEventArgs("Instead of Console.WriteLine"));
    
            }
    
        }
    
    
        class MyEventArgs : EventArgs
        {
            public readonly string EventString;
    
            public MyEventArgs(string s) { this.EventString = s; }
        }
    
    
    }

    Montag, 6. August 2012 14:35
  • Hallo,

    das liegt daran, das die Methode

    ShowNotification

    public ist und ein Objekt von

    MyEventArgs 

    braucht, welche außerhalb des Namespaces nicht sichbar ist, also deklariere deine Klasse als public:

    public class MyEventArgs 
    {
    


    Koopakiller - http://koopakiller.ko.ohost.de/

    Montag, 6. August 2012 14:42
    Moderator
  • Danke!

    Wenn du mir jetzt noch verraten könntest, warum listBox1.Items.Add(e.EventString) in ShowNotification nicht aufrufbar ist ?

    Der Versuch Form1 zu instanziieren, um dann auf listBox1 zuzugreifen bringt zwar keinen Fehler, aber listBox1 bleibt nach button1.Click trotzdem leer

    MfG Flux


    • Bearbeitet Flux1989 Montag, 6. August 2012 14:52
    Montag, 6. August 2012 14:50
  • Weil du deine Form benutzen musst, keine neue Instanz:

    this.listBox1.Items.Add(notificationStr);

    das "this." kannst du auch weglassen, dient aber der übersichtlichkeit.

    In deinem Auskommentierten Codestück hättest du es zu einer ListBox hinzugefügt die in einer anderen Instanz liegt, die aber noch nicht angezeigt wurden ist und somit gabs auch deine listBox1 noch nicht und somit kam die Exception.


    Koopakiller - http://koopakiller.ko.ohost.de/

    Montag, 6. August 2012 14:57
    Moderator
  • das "this." kannst du auch weglassen, dient aber der übersichtlichkeit.

    Ohne this zeigt IntelliSense keine listBox1, daher war das der Schlüssel, um die Instanz, welche in Program.cs erzeugt wird ("Application.Run(new Form1());") zu referenzieren.

    Danke nochmals!

    Montag, 6. August 2012 15:05