0

The goal of my program is to load a directory into my treeview node, which takes more 10 sec(since the directory resides in a remote PC). During this time I shall add a pop up image of waiting. I copied the answer of this post: How can i show an image while my application is loading to my code, like:

   private void treeView2_AfterSelect(object sender, TreeViewEventArgs e)
   {
        Form f = new Form();
        f.Size = new Size(20, 25);
        Image im = Image.FromFile(@"C:\Documents and Settings\JiangC\Documenti\Immagini\loader.gif");
        f.FormBorderStyle = FormBorderStyle.None;
        f.MinimizeBox = false;
        f.MaximizeBox = false;
        f.AutoSize = true;
        PictureBox pb = new PictureBox();

        //pb.Dock = DockStyle.Fill;
        pb.SizeMode = PictureBoxSizeMode.AutoSize;
        pb.Image = im;
        pb.Location = new Point(50, 50);
        f.Controls.Add(pb);
        f.Show();
       // Application.DoEvents();
        BuildTree(directory, treeView2.Nodes, treeView2.SelectedNode);            
        f.Close();
    }

What I want to do is that during the loading (when Buildtree() method is implementing) the form f with the picturebox pb will be shown, and after loading they just disappear.

The first problem is in "pb.Dock = DockStyle.Fill;" If I uncomment it, this form f will not be shown during the loading(but I can see its minimized window in the bottom of the screen). Only if I take off this line the form f could be shown out. The second problem is "f.Show();" When the form f is shown, the picturebox pb isn't shown at all(just an empty hole). If I modify it into "f.ShowDialog();", the form f with the picture could be shown. However here comes the third problem, which is the form f will be always there, my function "BuildTree();" isn't implemented at all!
The forth problem is if I add the line "Application.DoEvents();", it works quite fine, during the loading the form f with the picturebox will be shown and after the loading the f will disappear, but the picture in f is a gif, in this case the gif doesn't animate at all!

So anybody could help me solve the problem?

Here's my code of BuildTree function:

private void BuildTree(DirectoryInfo[] directoryInfo, TreeNodeCollection addInMe, TreeNode clicked)
{

    for (int i = 0; i < directoryInfo.Length; i++)
    {              
        var files = directoryInfo[i].GetFiles();
        int length = files.Length;
        clicked.Nodes.Add(directoryInfo[i].Name);
        List<TreeNode> dateNode = new List<TreeNode>();
        string[] allDates = new string[length];

        try
        {
            for (int j = 0; j < length; j++)
            {
                allDates[j] = files[j].Name.Substring(11, 6);
            }

        }
        catch (Exception e)
        { }


        string[] date = allDates.Distinct().ToArray();

        for (int k = 0; k < date.Length; k++) //From here to the end
        {
            // is my code loading file
            dateNode.Add(clicked.Nodes.Add(date[i]));
            for (int j = 0; j < length; j++)
            {
                // curNode.Nodes.Add(file.FullName, file.Name);               

                if (files[j].Name.Substring(11, 6) == date[i])
                {
                    dateNode[i].Nodes.Add(files[j].FullName, files[j].Name);
                }

            }

        }

        foreach (DirectoryInfo subdir in directoryInfo[i].GetDirectories())
        {
            BuildTree(subdir, clicked.Nodes);
        }

    }

}
Community
  • 1
  • 1
jcraffael
  • 81
  • 3
  • 12

2 Answers2

0

It depends on whether your BuildTree() method can be executed on background thread or not. since you are passing references to properties of TreeView control I'm assuming that that code has to be executed on UI thread. In this case th only thing I can suggest to you is call Application.DoEvents(); from within BuildTree() method, for example in a loop.

Also if you are making any long term calls to get data inside BuildTree() method, you should make these on background thread and synchronize with UI in any convenient way.

[EDIT] I had a quick look at your BuildTree() method. I think in this case the best approach for you would be:

  1. declare additional data structures to hold all data recuired to fill the TreeView.
  2. Fill all the data on background (You can use BackgroundWorker for this) here is a good question highlighting how get results from BackgroundWorker
  3. Once all data is ready, create and add all nodes to TreeView. This also will take some time, but it has to be done on UI thread. in order to keep UI responsive you can call Application.DoEvents() inside a loop where you will be adding new nodes to the tree

Code in treeView2_AfterSelect() would looks like this:

//...
f.Show();
BackgroundWorker bw = new BackgroundWorker();

bw.DoWork += (ss, ee) =>
{
    ee.Result = PrepareData(directory); // prepare all data required to build the Tree
}

bw.RunWorkerCompleted += (ss, ee) => 
{
    BuildTreeByData(ee.Result, addInMe, clicked);
    f.Close();
}

bw.RunWorkerAsync();
Community
  • 1
  • 1
Woodman
  • 1,108
  • 9
  • 11
  • Hi Woodman thank you for your reply. It took me 2 days of study of background worker to get the idea of your comment. You're right since my BuildTree() method works on UI thread since it shall load files and show them on the treeview. if I use a backgroundworker and put it in the _DoWork method, an "Cross thread operation not valid" shows up, and I know that's a conflict between background thread and UI thread. I'm quite convinced that your suggestion "synchronize the background thread with UI" would be the solution. Could you show me how? I already posted my BuildTree() method. Thank you! – jcraffael May 30 '14 at 09:21
  • Thank you for the suggestion! I agree with you that to split my BuildTree() into 2 parts, one for bw.Dowork one for bw.Runworkercompleted is the solution. And with days of work I made it. But now the problem is to pass the result of dowork to runworkercompleted. Your way isn't working for me unfortunately. The "ee" in runworkercompleted has the type RunWorkerCompletedEventArgs while "ee" in dowork has the type DoWorkEventArgs. And a simple convert.changetype doesn't work. From the link I have no idea how to apply to my case. Do you have any idea? thank you! – jcraffael Jun 04 '14 at 10:07
  • OK now I solved that, by making a casting like "List direct = (List)ee.Result" and using direct in runworkercompleted. That's it! Thanks again for the solution – jcraffael Jun 04 '14 at 10:27
0

Add this line to your code, before calling f.Show():

f.Activated += (s, e) => BuildTree(directory, treeView2.Nodes, treeView2.SelectedNode);
// it's f.Shown for Windows Forms, Activated is for WPF

It subscribes an anonymous event handler to the OnShow event of your f form. In this handler you simply call your method.

If it doesn't work, you will have to replace the call to BuildTree here for a BackgroundWorker call, since it is possible that your BuildTree method access UI controls, and that is a big no-no. Call this code instead of f.ShowDialog:

BackgroundWorker bw = new BackgroundWorker();

bw.DoWork += (ss, ee) =>
{
    f.ShowDialog(this);
    BuildTree(directory, treeView2.Nodes, treeView2.SelectedNode);
}

bw.RunWorkerCompleted += (ss, ee) => f.Close();
bw.RunWorkerAsync();

And change every access and modification to your controls in BuildTree like this:

this.Invoke((MethodInvoker) (() =>
{
    // access you control here
}));
Kilazur
  • 3,089
  • 1
  • 22
  • 48
  • Thanks for the reply. Before trying your second approach, I shall ask you that the line you proposed, in my program an error message is shown saying that OnShow isn't contained in System.Windows.Forms.Form or the arguments aren't correct. I don't know if I shall add another using statement at the begining of the program? – jcraffael May 28 '14 at 13:21
  • Yeah, that was a mistake, I think you're searching for Shown instead of OnShow. – Kilazur May 28 '14 at 13:45
  • Yeah "Shown" works! However the result is odd. If I still use f.Show() nothing changes(the f form and picture don't show off), if instead I use f.ShowDialog(), form f and the pic at first aren't shown, when the loading ends they just show off and don't disappear any more. I wonder if there's a way to close f afterwards. (f.Close() doesn't work) – jcraffael May 28 '14 at 13:57
  • Updated, try the code I added about BackgroundWorker and tell me. – Kilazur May 28 '14 at 14:16
  • Thank you for the backgroundworker method. I haven't figure out how to use that according to your list yet. However unfortunately this exception got thrown "Cross-thread operation not valid: Control 'treeView2' accessed from a thread other than the thread it was created on." A problem of multi thread right? – jcraffael May 28 '14 at 14:29
  • Oh yeah, my bad, you have to invoke UI thread modification in BuildTree, so you may have to show us its code. I'm adding the way to invoke in my answer. And in fact, you may not need the BackgroundWorker if that alone works. – Kilazur May 28 '14 at 14:32
  • I tried the new line in my Buildtree method, what happens is exactly the same as the forth case I posted in the question, namely the form f works fine, appearing at the begining and disappear at the end, but the gif picture doesn't animate (it stays still), which is neither what I want unfortunately – jcraffael May 28 '14 at 15:19
  • Ok, that gets tougher than it looks... I did that already, but it was ages ago. You have to give me some time to find this damn answer x) Or hope someone more clever than me pass by before – Kilazur May 28 '14 at 17:17
  • Well take it easy. You've already offered much help. No matter what I appreciate that very much :) – jcraffael May 29 '14 at 06:47