Andrew
Andrew

Reputation: 163

Can't expand nodes in TreeView (WinForms)

I'm filling TreeView programmatically (in different thread if it matters).

I want the first level of nodes to be expanded when TreeView loads to window. I've tried almost everywhere (in worker thread, in main thread, in event handlers of Form.Load, Form.Shown etc.), but TreeView is still collapsed.

What am I doing wrong ?

UPDATE

treeView.UpdateTree((object tree) => {
    treeView.Nodes[0].Nodes.Add(text);
});

public static void UpdateTree(this Control ctrl, Action<object> code) {
    if (ctrl.InvokeRequired) {
        ctrl.BeginInvoke(code, (TreeView)ctrl);
    }
    else {
        code.Invoke((TreeView)ctrl);
    }
}

UPDATE 2

private void btnFillTree_Click(object sender, EventArgs e) {
    ......
    treeDirectoryContents.Nodes.Add("GeneralFolder");
    ......
    //there I create Thread() that fills treeDirectoryContents
    ......
    treeDirectoryContents.ExpandAll();
}

Upvotes: 0

Views: 3492

Answers (1)

firda
firda

Reputation: 3338

As far I know (.NET 3.5) you cannot acces GUI elements from different thread (you can prepare some data but must access TreeView.Nodes from main thread only - use Control.BeginInvoke for that ... you can as well check Control.InvokeRequired).

After filling all the nodes you can just do

foreach (TreeNode node in treeView) node.Expand()

EDIT after UPDATE2:

  1. Nodes can only be expanded when they have children.
  2. Controls can only be accessed from main thread (check Control.InvokeRequired)
  3. BeginInvoke() is asynchronous (does not wait) while Invoke() is synchronous (like BeginInvoke + EndInvoke)
  4. Never call Thread.Join() from main thread (use BackgroundWorker.IsBusy or mimic that by some state variable e.g. bool done = false; thread.Start(); while(!done) Application.DoEvents()

Example from MSDN:

// Start the download operation in the background. 
this.backgroundWorker1.RunWorkerAsync();

// Disable the button for the duration of the download. 
this.downloadButton.Enabled = false;

// Once you have started the background thread you  
// can exit the handler and the application will  
// wait until the RunWorkerCompleted event is raised. 

// Or if you want to do something else in the main thread, 
// such as update a progress bar, you can do so in a loop  
// while checking IsBusy to see if the background task is 
// still running. 

while (this.backgroundWorker1.IsBusy)
{
    progressBar1.Increment(1);
    // Keep UI messages moving, so the form remains  
    // responsive during the asynchronous operation.
    Application.DoEvents();
}

EDIT - How I thing it should be done (using Threads)

using System;
using System.Threading;
using System.Windows.Forms;

class MyForm : Form {
    public static void Main() {
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new MyForm());
    }

    TreeView tree = new TreeView() { Dock = DockStyle.Fill };
    MyForm() {
        Controls.Add(tree);
        tree.Nodes.Add("Loading...");
    }
    protected override void OnLoad(EventArgs e) {
        new Thread(Fill).Start();
        base.OnLoad(e);
    }
    void Create(string text) {
        if (InvokeRequired) Invoke(new Action<string>(this.Create), text);
        else tree.Nodes[0].Nodes.Add(text);
    }
    void Finish() {
        if (InvokeRequired) Invoke(new Action(this.Finish));
        else {
            tree.Nodes[0].Text = "The Nodes";
            tree.ExpandAll();
        }
    }
    void Fill() {
        for (int i = 0; i < 10; i++) {
            Create("Node #" + i.ToString());
            Thread.Sleep(100);
        }
        Finish();
    }
}

Upvotes: 5

Related Questions