Hello,
I am building a tree from a background worker, so that the user sees the tree build up "live". For nodes that are not completely filled, I want to temporarily change their default image for a "loading" image. When a node gets all its child nodes, its image is reset to default.
Here is my code :
// initialize node
node.Override.NodeAppearance.Image = index;
...
// now child nodes are to be added : change image to "loading"
node.Override.NodeAppearance.Image = (Image)Properties.Resources.ResourceManager.GetObject("loading"));
treeView.Refresh();
// tree loaded : reset initial image index
However, this does not work : all nodes keep their initial image. Even with the tree refresh I cannot see my "loading" picture.
Thank you for your help !
It is difficult to speculate without additional details. One thing to note is that you cannot update the tree's images (or anything else for that matter) from the BackgroundWorker's DoWork event, so if you are doing it there that would explain the problem. Documentation for the BackgroundWorker class explains how the properly implement this kind of thing.
If you are confident that you are handling the multithreading aspect of this correctly, and you are able, attach a sample project here and we can take a look.
Hello Brian,
you are right, I have to update the tree from the BackgroundWorker in order to have the tree usable during its loading.
Since I am using delegates to do this properly (I have to because otherwise my application would crash), I think the multithreading aspect is handled properly. Or maybe a delegate is not enough ?
My project is quite big so it is not possible that I send it to you as a sample, but my code is very simple on this aspect. Here is the code called from the BackgroundWorker :
int imageIndex = _treeComponent.TreeSetNodeImage(parentNode, (Image)Properties.Resources.ResourceManager.GetObject("loading"));
Where _treeComponent is a component which contains my UltraTree.
The method TreeSetNodeImage is implemented like this :
/// <summary> /// Delegate for setting the image of a node /// </summary> /// <returns>Old image index</returns> private delegate int TreeSetNodeImageDel(UltraTreeNode pNode, Image image); /// <summary> /// Sets the image of a node threadsave /// </summary> /// <param name="pNode">The node</param> /// <param name="image">The image</param> /// <returns>Old image index</returns> internal int TreeSetNodeImage(UltraTreeNode pNode, Image image) { if (this.InvokeRequired) { TreeSetNodeImageDel treeSetNodeImageImpl = TreeSetNodeImageImpl; return treeSetNodeImageImpl.Invoke(pNode,image); } return TreeSetNodeImageImpl(pNode,image); } /// <summary> /// Sets the image of a node /// </summary> /// <param name="pNode">The node</param> /// <param name="image">The image</param> /// <returns>Old image index</returns> internal int TreeSetNodeImageImpl(UltraTreeNode pNode, Image image) { int imageIndex = (int)pNode.Override.NodeAppearance.Image; pNode.Override.NodeAppearance.Image = image; TreeRefresh(); return imageIndex; }
I hope this will help you understand what is going on.
Hi,
It's hard to read just a code snippet. This code looks okay to me at first glance.
I'm not sure about: return treeSetNodeImageImpl.Invoke(pNode,image);
I usually use this.Invoke(delegate). I'm not familiar with the syntax you used there, but it looks like it probably does the same thing.
One thing that seems like it might be a problem here is the node. Your background worker has a reference to the node? That seems very dangerous, because that node might not exist or may have been modified on the main thread while the background thread was running. But in such a case, you'd be getting an exception. I can't see how that could cause the image not to show up. Unless the reference to the node has somehow become corrupt.
Well, I can't say for sure that it was hovering the mouse over the node that caused this, but my point is that there are a lot of unknowns here. You cannot control everything that happens to the tree, so passing a tree node into another thread seems like a bad idea to me. It would be a lot safer to use the node's Key, since it's just a string. Then you could use GetNodeByKey on the UI Thread to find the right node and perform whatever action you want on it.
Ok now I think that I understand... Sometimes my node reference was "null" although the node exists and is visible. So the cause may be that I hovered over that node with the mouse pointer and thus my BackgroundWorker could not access the node anymore. Is this what you mean ?
Juscher said:As long as the BackgroundWorker is active, I disable actions of the main thread that may modify nodes.
Well... like I said, I doubt that this is anything to do with the problem you are having and I don't see how it could cause the image not to update. But... I don't see how you could possibly disable everything that might possibly alter a node. You don't have access to everything that might affect the node - unless maybe you are making the tree invisible. As long as the tree is visible, the mouse could move over the node, which might cause the tree to attempt to access that node. Even if the node is not visible on the screen, when it is DataBound, you cannot ensure that the DotNet BindingManager won't try to access it's data. Of course, that's not a problem if your tree is not bound.But the point I am trying to make is that you really have to be extremely careful when using threading. There are probably a dozen other ways the node could be accessed or changed that I haven't even thought of and you cannot control.
Actually my BackgroundWorker calls a method of the treeComponent (which holds the tree) to get an UltraTreeNode Then it calls TreeSetNodeImage on this node.
As long as the BackgroundWorker is active, I disable actions of the main thread that may modify nodes.