Laden...

Controls von Thread aktualisieren lassen (Invoke-/TreeView-Beispiel)

Erstellt von Z-80 vor 18 Jahren Letzter Beitrag vor 18 Jahren 49.171 Views
Thema geschlossen
Information von herbivore vor 17 Jahren

Dies ist ein Thread, auf den aus der FAQ verwiesen wird. Deshalb ist er geschlossen.

Z
Z-80 Themenstarter:in
29 Beiträge seit 2004
vor 18 Jahren
Controls von Thread aktualisieren lassen (Invoke-/TreeView-Beispiel)

Hallo,

ich habe ein kleines Threadproblem. Ich lasse in einem TreeView einen Baum erstellen, was gut und gerne 30 Sekunden dauert. Deshalb habe ich dafür einen extra Thread laufen. Allerdings aktualisiert sich der TreeView nicht.
Zuerst habe ich es mit:


                Thread t = new Thread(new ThreadStart(funktion));
                t.Start();

probiert aber immer wenn ich dann die TreeView aktualiseren will, gibt es eine Exception.
Nun habe ich es mit:


private void AssemblyTreeBox_Load(object sender, EventArgs e)
{
   ct ctd = new ct(CreateTree);
   ctd.BeginInvoke(null, null);
}

TreeView tmpTreeview = new TreeView();

public delegate void Updatetreedelegate();
void UpdateTree()
{
   if (treeView1.InvokeRequired == false)
   {
      treeView1 = tmpTreeview;
      treeView1.Refresh();
   }
   else
   {
      Updatetreedelegate upd = new Updatetreedelegate(UpdateTree);
      this.BeginInvoke(upd);
   }
}

private void CreateTree()
{
   // ....
   //Baummanipulation
   UpdateTree();
   // ....
}

probiert. Stand so auf einer Microsoftseite; da war aber statt dem TreeView eine Progressbar.
In tempTreeView steht auch immer der richtige aktuelle Baum, nur leider wird beim sichtbaren treeView1 nichts angezeigt.
Wie bekomme ich es nun hin das sich der Baum stückchenweise aufbaut, und das auch angezeigt wird?

Wenn jemand einen Grund verstanden hat, werden sich viele weitere vor seinen Augen auftun. - Tsunetomo Yamamoto um 1680

www.blog.zachariasoft.de

49.485 Beiträge seit 2005
vor 18 Jahren

Hallo Z-80,

ich habe mal meine Code von TreeView abhängig vom Verzeichnislevel füllen so umgestellt, dass das Füllen in einem Thread passiert. Manchmal sagt ja Code mehr als 1000 Worte.

Zusätzlich benötigt, wird jetzt natürlich das Starten des Threads:


Thread t = new Thread (new ThreadStart (PopulateTreeView));
t.Start();

und zwei Delegaten, die das eigentlich Hinzufügen übernehmen, denn das muss ja im GUI-Thread passieren:


protected delegate void MethodTreeViewTreeNode (TreeView tv, TreeNode tn);
protected delegate void MethodTreeNodeTreeNode (TreeNode tnParent, TreeNode tn);

void AddRootNode (TreeView tv, TreeNode tn)
{
   tv.Nodes.Add (tn);
   tv.ExpandAll();
}

void AddChildNode (TreeNode tnParent, TreeNode tn)
{
   tnParent.Nodes.Add (tn);
   tnParent.TreeView.ExpandAll();
}

Dort habe ich jeweils noch ExpandAll hineingeschrieben, damit man immer gleich sieht, was passiert.

Im eigentlichen Algorithmus habe ich nur das direkte Hinzufügen der Knoten/Unterknoten durch die entsprechenden Invokes ersetzt (und außerdem das Füllen durch Thread.Sleep künstlich verzögert):


protected void PopulateTreeView ()
{
   Hashtable htNode = new Hashtable ();

   using (StreamReader sr = new StreamReader ("data.txt", Encoding.Default)) {
      String strDir;
      while ((strDir = sr.ReadLine ()) != null) {
         String [] astrPart = strDir.Split ('\\');
         String strBegin = "";
         TreeNode tnParent = null;
         foreach (String strPart in astrPart) {
            if (strPart == "") {
               continue;
            }
            strBegin += "\\" + strPart;
            if (htNode.ContainsKey (strBegin)) {
               tnParent = (TreeNode)htNode [strBegin];
               continue;
            }
            htNode [strBegin] = new TreeNode (strPart);
            if (tnParent == null) {
               Invoke (new MethodTreeViewTreeNode (AddRootNode),
                       _tvDir, htNode [strBegin]);
            } else {
               Invoke (new MethodTreeNodeTreeNode (AddChildNode),
                       tnParent, htNode [strBegin]);
            }
            tnParent = (TreeNode)htNode [strBegin];
         }
         Thread.Sleep (500);
      }
   }
}

Zum Ausprobieren kannst du die data.txt aus dem anderen Thread herunterladen.

herbivore

Suchhilfe: 1000 Worte

Z
Z-80 Themenstarter:in
29 Beiträge seit 2004
vor 18 Jahren

Danke, das hat mir sehr geholfen. Jetzt ist mir die ganze Angelegenheit auch viel klarer und logischer geworden.

Wenn jemand einen Grund verstanden hat, werden sich viele weitere vor seinen Augen auftun. - Tsunetomo Yamamoto um 1680

www.blog.zachariasoft.de

4.221 Beiträge seit 2005
vor 18 Jahren

@herbivore

Wie immer von Dir saubere Arbeit 🙂

Der Vollständigkeit halber würde ich den Code aber noch ein bisserl erweitern mit if InvokeRequired.... Du weisst sicher was ich meine.... Der Code kann ja auch problemlos aus dem UI-Thread aufgerufen werden... dann wäre der Invoke nicht notwendig..... dann wäre dies hier wieder ein guter Artikel auf welchen man Threading-Newbies verweisen kann...

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

49.485 Beiträge seit 2005
vor 18 Jahren

Hallo Programmierhans,

danke! Ja, ich weiß, was du meinst. Ich hatte das gestern auch überlegt, aber da ja gerade das Ziel war, dass das Füllen durch einen extra Thread geschehen sollte, ist ja das InvokeRequiere überflüssig und und ich fand es sogar vom Kern der Sache ablenkend.

Dafür spricht jedoch, dass man in dem eigentlichen Füllalgorithmus um die aufwändige Invoke-Syntax herumkommt und dass man Invoke und "invokte" Methode schön verheiraten kann.

Deshalb komme ich deinem Hinweis auch gerne nach und liefere eine Version mit InvokeRequired:

Der Füllalgorithmus wird wie gesagt übersichtlicher (hier nur der entscheidende Auschnitt am Ende der inneren Schleife):


htNode [strBegin] = new TreeNode (strPart);
if (tnParent == null) {
   AddRootNode (_tvDir, (TreeNode)htNode [strBegin]);
} else {
   AddChildNode (tnParent, (TreeNode)htNode [strBegin]);
}
tnParent = (TreeNode)htNode [strBegin];

Die beiden Add-Methoden kümmern sich jetzt selbst um das in diesem Szenario auf jeden Fall benötigte Invoke, in dem sie sich selbst "invoken".


void AddRootNode (TreeView tv, TreeNode tn)
{
   if (tv.InvokeRequired) {
      Invoke (new MethodTreeViewTreeNode (AddRootNode),
              tv, tn);
      return;
   }
   tv.Nodes.Add (tn);
   tv.ExpandAll();
}

void AddChildNode (TreeNode tnParent, TreeNode tn)
{
   if (tnParent.TreeView.InvokeRequired) {
      Invoke (new MethodTreeNodeTreeNode (AddChildNode),
              tnParent, tn);
      return;
   }
   tnParent.Nodes.Add (tn);
   tnParent.TreeView.ExpandAll();
}

Der Aufruf des Thread und die Delegaten bleiben unverändert.

herbivore

Information von herbivore vor 17 Jahren

Früher stand dieser Thread direkt in der FAQ und an vielen Stellen wird direkt auf ihn verwiesen. Jetzt gibt es einen eigenständigen Beitrag [FAQ] Controls von Thread aktualisieren lassen (Invoke-/TreeView-Beispiel) in der FAQ, der auf diesen Thread hier verweist, weshalb dieser Thread hier auch geschlossen ist.

Thema geschlossen