Realisierungsproblem für Infodialog



  • Hallo,

    ich habe das Problem folgendes zu realisieren, also eine Weg zu finden, welcher auch code-technisch sauber ist:

    Ich habe eine Form, in der ich eine Listbox mit Verzeichnissen habe. Für jedes Verzeichnis in der Liste läuft ein SystemFileWatcher, welche in einer Arraylist gespeichert werden, was auch vorzüglich funktioniert. Sobald in einem Verzeichnis eine Änderung geschieht soll ein InfoDialog in der rechten unteren Ecke des Bildschirms in den Vordergrund kommen mit der Information in einer Listbox, welches Verzeichnis geändert wurde (was ebenfalls super funktioniert). Wenn nun in einem weiteren Verzeichnis etwas geändert wird, soll das hinzuzugefügt werden in die Liste, so das sich eine gewisse Historie ergibt.

    Problem welches sich daraus ergibt ist, dass ich den Dialog nicht mehr verändern kann, sobald er einmal initialisiert wurde, so dass das Programm abschmiert, sobald im zweiten Verzeichnis etwas passiert.

    Form 2 sieht so aus:

    public partial class DlgInfo : Form
    {
        public DlgInfo()
        {
            InitializeComponent();
            Rectangle rec = Screen.PrimaryScreen.WorkingArea;
            this.Location = new Point(rec.Width - this.Size.Width, rec.Height - this.Size.Height);
        }
    
        public void addInfo(List<string> infos)
        {
            foreach (string info in infos)
            {
                listBox1.Items.Add(info);
            }
        }
    }
    

    Form 2 soll jederzeit beendet werden können vom Anwender und sobald wieder eine Änderung auftritt natürlich wieder erscheinen.

    Ich habe diesbezüglich mal gegooglet, weil ich vermutet habe, dass ich hier über Threads arbeiten sollte. Dort kann man ja auch den Ablauf eines Forms an einen ProgressBar-Dialog über backgroundworker übermitteln um zur Laufzeit den zweiten Dialog mit dem Status zu versehen, so dass die Progressbar dort ablaufen kann. Ich dachte, dass das auch mit dem Füllen von Daten einer Listbox geht, lese nun aber zuhauf, dass eine Form niemals auf die Controlelemente einer anderen Form zugreifen sollte. Ist mein Lösungsansatz falsch?



  • Ich habe kürzlich festgestellt, wo meine Problematik liegt. In meiner Hauptklasse rufe ich die FileSystemWatcher auf:

    if (Directory.Exists(path))
    {
        FileSystemWatcher watcher = new FileSystemWatcher(path);
        watcher.Changed += new FileSystemEventHandler(OnChanged);
        watcher.Created += new FileSystemEventHandler(OnChanged);
        watcher.Deleted += new FileSystemEventHandler(OnChanged);
        watcher.Renamed += new RenamedEventHandler(OnChanged);
    
        watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
                        | NotifyFilters.FileName | NotifyFilters.DirectoryName;
                        watcher.Filter = "*.lock";
                        watcher.EnableRaisingEvents = true;
    
        FileWatcherList.Add(watcher);
    }
    

    In meiner Methode OnChanged wird nun der HinweisDialog geöffnet:

    protected void OnChanged(object source, FileSystemEventArgs e)
    {
        if (null == inf)
        {
            inf = new DlgInfo();
            inf.Click += new EventHandler(inf_Click);
        }
    
        inf.ShowDialog();
        inf.BringToFront();
    }
    

    Das Programm funktioniert bis zu einem Punkt sehr gut. Wenn in den Verzeichnissen Dateien mit einer von mir gewählten Endung entstehen öffnet er den Hinweisdialog. Wird in einem anderen Verzeichnis auch eine neu erstellt, schreibt er die Info wie gewünscht auch in die Listbox des hinweisdialogs. Wenn der Hinweisdialog durch den Anwender geschlossen wird, wird dieser neu initialisiert. Das funktioniert alles so, wie ich es mir wünsche.

    Nur, wenn eine Datei einer anderen Prozedur unterliegt (löschen beispielsweise) wird ein neuer Dialog erstellt. Das Objekt des Dialoges scheint also eine neue Instanz zu erfahren, obwohl ich es global deklariert habe. Gibt es einen besseren Weg oder hat jemand eine Idee, wie ich verhindern kann, dass das Obejkt erneut initialisiert wird? Ich hatte gedacht, dass die Zeile:

    if (null == inf)
        {
            inf = new DlgInfo();
            inf.Click += new EventHandler(inf_Click);
        }
    

    das verhindern.
    Das Objekt wurd im Klassenkopf wiefolgt deklariert:

    DlgInfo inf;
    


  • Hallo,

    m.E. mußt du auch "inf.ShowDialog();" innerhalb der if-Abfrage setzen, denn bei einem erneuten Aufruf davon, würde sich ein neuer Dialog öffnen (unabhängig von der Instanz). Und da dieser Aufruf ja blockierend ist (modal!), macht der anschließende Aufruf von "BringToFront()" keinen Sinn 😉

    Du willst wahrscheinlich:

    if (null == inf)
        {
            inf = new DlgInfo();
            inf.Click += new EventHandler(inf_Click);
            inf.ShowDialog();
            inf = null;
        }
        else
            inf.BringToFront();
    

    Evtl. solltest du diesen Code (Erzeugung des Dialogs) noch mittels des Schlüsselworts 'lock' schützen, damit nicht zeitgleich mehrmals dieser Code durchlaufen wird.

    Aber bist du denn sicher, daß diese Methode mehrfach aufgerufen werden kann? Nicht, daß der modale Dialog die Abarbeitung weiterer Events verhindert.



  • @TH69:
    Vielen lieben Dank für deine Antwort. Ich habe den Block nun gelockt und die Anpassungen mit der Dialoganzeige angepasst.

    lock (thisLock)
    {
        if (null == inf)
        {
            inf = new DlgInfo();
            inf.Click += new EventHandler(inf_Click);
            inf.addInfo(infos);
            inf.ShowDialog();
            inf = null;
        }
        else
        {
            inf.addInfo(infos);
            inf.BringToFront();
        }
    }
    

    Der lock, welchen du vorgeschlagen hast, hat die positive Nebenwirkung, dass das Programm nun nicht mehr mit der Meldung:

    Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement DlgInfo erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde.

    abstürzt, sondern nun kosnequent läuft.

    Der Dialog wird nun bei der ersten Initialisierung angezigt mt der gewünschten Option, nur wird die Liste nicht mehr wie gewünscht gefüllt, wenn eine zweite Änderung erkannt wird von den FileWatchern. Wenn ich den Dialog dann beende, öffnet sich sofort erneut der Dialog und hat DANN auch die Änderungen enthalten, die der FileWatcher zurückgemeldet hat.



  • Hallo, darum schrieb ich ja "(Erzeugung des Dialogs")", also nicht um die ganze Methode:

    if (null == inf)
        {
            lock (thisLock)
            {
                inf = new DlgInfo();
                inf.Click += new EventHandler(inf_Click);
                inf.addInfo(infos);
            }
            inf.ShowDialog();
            inf = null;
        }
        else
        {
            inf.addInfo(infos); // <- hier evtl. auch noch ein lock drum
            inf.BringToFront();
        }
    

    Und zu der von dir zitierten Fehlermeldung solltest du unbedingt Controls von Thread aktualisieren lassen (Control.Invoke/Dispatcher.Invoke) durchlesen.

    Wenn man mit Multithreading zu tun hat, sollte man unbedingt wissen, wie das funktioniert und die Stichworte Deadlock, Race Condition etc. kennen.


Log in to reply