Ergebnis 1 bis 7 von 7
  1. #1
    Mitglied Avatar von Fettoni
    Registriert seit
    Apr 2007
    Beiträge
    152
    Danksagungen
    0

    Standard [C#] WPF Multithreading

    Hallo zusammen,

    ich arbeite mich zur Zeit spaßeshalber ein bisschen in die Programmierung mit der Windows Presentation Foundation ein!
    Hier meine Situation:

    Was soll meine kleine Anwendung machen?


    Ich möchte ein kleines Tool programmieren, das mir ermöglicht einen Service zu starten/stoppen und dabei den Servicestatus in einem Statuslabel anzeigt, bzw. den Start/Stop Button sperrt, während der Service startet/stoppt. Der Service der gestartet wird, erzeugt mir in einem bestimmten Verzeichnis mehrere Logfiles, die mit dem Tool ausgelesen werden sollen.
    Dazu soll das Programm ein TabControl besitzen. Für jede Datei in dem Verzeichnis wird ein eigener Tab erzeugt. Für den Inhalt der Dateien soll für den jeweiligen Tab eine ListBox dynamisch erzeugt werden und jede Zeile der Textdatei als ListBox Item hinzugefügt werden. Ein Timer soll laufen, der steuert, wie oft die Dateiinhalte abgerufen, also aktualisiert dargestellt werden.

    (Anbei mal ein Screenshot rein mit der Funktionalität der Logfiles ausleserei -> http://www.imagebanana.com/view/jspmwrfg/tool.png

    Mein Problem:

    Also ich bin mit der Threadprogrammierung noch nicht ganz auf der sicheren Seite. Es ist wohl klar, dass die GUI blockiert, wenn ich diese Logaktualisierung nicht in einen Thread auslagere. Den Service zu starten und zu stoppen als auch den Status des Services über einen anderen Thread beim Label zu aktualisieren ist absolut kein Problem.

    Mein Problem besteht nur darin, dass ich IN meinem GUI-externen Thread für die Logauswertung dynamisch sowohl TabItems, als auch ListBox Controls erstellen muss.
    Es sieht so aus:

    Ich habe eine TabControl im Designer angelegt. Die gehört wohl dem GUI-Thread.
    Ich lege ein TabItem in meinem externen Thread an.
    Ich lege ein ListBox Control in meinem externen Thread an.
    Ich weiße meine ListBox dem TabItem Content zu.
    Nun möchte ich das TabItem meiner TabControl zuweisen und genau hier kracht es, weil - so wie ich das verstehe - der GUI Thread, sprich das TabControl nicht auf die in meinem Thread angelegten Controls zugreifen darf?

    Mein Versuch:

    Zugegebenermaßen war das ein etwas blöder Versuch, aber ich habe versucht, ein TabItem und eine ListBox schon im GUI-Thread zu erstellen und mit denen dann zu arbeiten. Logischerweise wurde dann immer das gleiche Control verwendet, das heißt es stand bei jedem Tab das gleiche und jede ListBox sah gleich aus.

    Meine Frage:

    Wie kann ich das realisieren? Mir wäre schon geholfen, wenn mir jemand einen Lösungsansatz gibt. Die Frage nach einem minimalistischen Codebeispiel traue ich mich schon fast garnicht zu stellen

    Mein (zugegebenermaßen hundsmiserabler) Code:

    Spoiler: 


    Code:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    using System.Threading;
    
    namespace Test
    {
        /// <summary>
        /// Interaktionslogik für MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            private string path = @"C:\test";
    
            public MainWindow()
            {
                InitializeComponent();
            }
    
            private void readFiles()
            {
                TabItem TabItem_temp;
                ListBox listbox_temp;
                System.IO.FileInfo[] fi = new System.IO.DirectoryInfo(path).GetFiles();
                string fileContent = "";
    
                Tab_Control.UpdateCS(new Action(() => Tab_Control.Items.Clear()));
    
                foreach (var fileInfo in fi)
                {
                    System.IO.StreamReader file = new System.IO.StreamReader(path + "\\" + fileInfo.Name);
    
                    TabItem_temp = new TabItem();
                    TabItem_temp.Header = fileInfo.Name;
    
                    listbox_temp = new ListBox();
    
                    while ((fileContent = file.ReadLine()) != null)
                    {
                        listbox_temp.Items.Add(fileContent); 
                    }
    
                    TabItem_temp.Content = listbox_temp;
    
                    Tab_Control.UpdateCS(new Action(() => Tab_Control.Items.Add(TabItem_temp)));
    // Hier der Fehler: "Der aufrufende Thread kann nicht auf dieses Objekt zugreifen, da sich das Objekt im Besitz eines anderen Threads befindet."
                }
            }
    
            private void test_Click(object sender, RoutedEventArgs e)
            {
                Thread a = new Thread(readFiles);
                a.TrySetApartmentState(ApartmentState.STA);
                a.Start();
              //readFiles();
            }
    
    
        }
    
        public static class ControlExtension
        {
            public static void UpdateCS(this Control control, Action code)
            {
                if (!control.Dispatcher.CheckAccess())
                {
                    control.Dispatcher.BeginInvoke(code);
                }
                else
                {
                    code.Invoke();
                }
            }
        }
    
    }


    Vielen Dank schonmal fürs reinlesen!
    Ich hoffe mir kann jemand helfen

    Lg
    Toni
    Geändert von Fettoni (21. 01. 2013 um 08:38 Uhr)

  2. #2
    Mitglied

    (Threadstarter)

    Avatar von Fettoni
    Registriert seit
    Apr 2007
    Beiträge
    152
    Danksagungen
    0

    Standard Re: [C#] WPF Multithreading

    Habe mein Problem mittlerweile selbst gelöst, indem ich die anzulegenden Controls in der Methode readFiles() komplett in das BeginInvoke gezogen habe.

    Spoiler: 


    Code:
     private void readFiles()
            {
                TabItem TabItem_temp;
                ListBox listbox_temp;
                System.IO.FileInfo[] fi = new System.IO.DirectoryInfo(path).GetFiles();
                string fileContent = "";
    
                Tab_Control.UpdateCS(new Action(() => Tab_Control.Items.Clear()));
    
                foreach (var fileInfo in fi)
                {
                    System.IO.StreamReader file = new System.IO.StreamReader(path + "\\" + fileInfo.Name);
    
                    Tab_Control.Dispatcher.BeginInvoke(new Action(() =>
                    {
                        TabItem_temp = new TabItem();
                        TabItem_temp.Header = fileInfo.Name;
    
                        listbox_temp = new ListBox();
    
                        while ((fileContent = file.ReadLine()) != null)
                        {
                            listbox_temp.Items.Add(fileContent);
                        }
    
                        TabItem_temp.Content = listbox_temp;
    
                        Tab_Control.Items.Add(TabItem_temp);
                    }
                    ));
                }
            }


    lg
    Toni
    Geändert von Fettoni (21. 01. 2013 um 08:39 Uhr)

  3. #3
    Mitglied Avatar von Kyler
    Registriert seit
    Jan 2008
    Beiträge
    151
    Danksagungen
    8

    Standard Re: [C#] WPF Multithreading

    Ja, deine Lösung ist wohl hier die einfachste.
    Aber ich denke das Invoke in der Schleife aufzurufen ist keine gute Idee. Der GUI externe Thread läuft viel schneller durch und der GUI Thread kommt kaum hinterher, ich bin mir nicht sicher ob es da zu Problemen kommen könnte.

    Ich würds so machen:

    Code:
            private void readFiles()
            {
                List<string> names = new List<string>();
                List<string[]> items = new List<string[]>();
                System.IO.FileInfo[] fi = new System.IO.DirectoryInfo(path).GetFiles();
    
                foreach (var fileInfo in fi)
                {
                    names.Add(fileInfo.Name);
                    items.Add(System.IO.File.ReadAllLines(path + "\\" + fileInfo.Name));
                }
    
                this.Dispatcher.BeginInvoke(new Action(() =>
                {
                    Tab_Control.Items.Clear();
    
                    for (int i = 0; i < names.Count; i++)
                    {
                        TabItem TabItem_temp = new TabItem();
                        TabItem_temp.Header = names[i];
                        ListBox listbox_temp = new ListBox();
                        listbox_temp.ItemsSource = items[i];
    
                        TabItem_temp.Content = listbox_temp;
                        Tab_Control.Items.Add(TabItem_temp);
                    }
                }
                ));
            }
    Hier wird der GUI Thread nur einmal aufgerufen. Nebenbei: Die Listbox über die ItemsSource zu füllen ist viel schneller als die Items einzeln hinzuzufügen.

    Wenn du noch mehr Code hast und du den Code des externen Threads in eine andere Klasse auslagerst, dann würde ich empfehlen eigene Events zu erstellen. Aber in deinem Fall hier würde ichs auch bei einem Invoke belassen.

    mfg Kyler

  4. #4
    Mitglied

    (Threadstarter)

    Avatar von Fettoni
    Registriert seit
    Apr 2007
    Beiträge
    152
    Danksagungen
    0

    Standard Re: [C#] WPF Multithreading

    Guten Morgen,

    vielen lieben Dank für deine Tips Kyler! Mit Performance hab ich mich bei der Mini-Anwendung noch garnicht so beschäftigt, aber du hast natürlich Recht.
    Ich habe das mal so übernommen, weil es doch um einiges schöner/eleganter ist, als meine "Mir ist es scheiß egal wie der Code aussieht, hauptsache er funktioniert erstmal" - Lösung

    lg
    toni

  5. #5
    Mitglied Avatar von sumisumi
    Registriert seit
    Jan 2008
    Beiträge
    4.296
    Danksagungen
    8

    Standard Re: [C#] WPF Multithreading

    Es geht noch schöner und eleganter, "the real WPFy way" sozusagen.
    Da muss man sich aber mit Bindings und ObservableCollections usw beschäftigen, lohnt sich aber. Du wirst nie wieder ein Steuerelement im Code erstellen, das lässt sich alles mit Templates machen.

  6. #6
    Mitglied

    (Threadstarter)

    Avatar von Fettoni
    Registriert seit
    Apr 2007
    Beiträge
    152
    Danksagungen
    0

    Standard Re: [C#] WPF Multithreading

    Mit Bindings usw. wollte ich mich auch noch beschäftigen, allerdings erstmal bissl mit Grundlegendem anfangen
    Bin noch nicht soooo fit mit C# bzw. WPF, deswegen erstmal Schritt für Schritt.

    Wenn hier jemand ein gutes Buch empfehlen kann wäre ich auch sehr dankbar. Ich habe mich schon bissl auf Amazon umgesehen, allerdings war ich noch nicht in ner Buchhandlung (vor allem deshalb, weil es dort meistens sowieso keine große Auswahl gibt, zumindest nicht bei den mini-Buchhändlern hier in meiner Nähe), darum ist es schwer zu beurteilen ob da was brauchbares dabei ist.

    lg
    Toni

  7. #7
    Mitglied Avatar von Kyler
    Registriert seit
    Jan 2008
    Beiträge
    151
    Danksagungen
    8

    Standard Re: [C#] WPF Multithreading

    Ich will noch einen Link dazu liefern was sumisumi meint mit "the real WPFy way":
    Model-View-ViewModel-Entwurfsmuster

    Das ist das gängigste Design, das bei WPF verwendet wird. Aber bevor man sich das durchliest sollte man sich die Grundlagen von Bindings, Converter, Commands etc. anschauen.
    Beim Design ist mit WPF auch viel möglich, ich habe meinen Universal Manager damit gemacht.
    Einen Buchtipp kann ich dir leider nicht geben, ich habs mit dem Internet gelernt.

    mfg Kyler

  8.  
     
     

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •