[SOLVED] Treeview, possible to late set Node.HasChildren (plus sign)? lazarus / 31-LCL ---------------------------------------------------------------------- d7_2_laz · 2021-07-08 12:46 For to keep the story short: i found that (in my use case) i could save A LOT of file-IO and, so, could achieve a tremendous performance benefit if i were able to set  ANode.HasChildren := true/false  especially within customtreeview OnGetImageIndex. The one and only problem is, that, setting it here, doesn't make the setting appear immediately. It comes into effect when a node is clicked, or the container scrolled, or another control activated. Presumably an ANode:Update (or treeview Invalidate or Refresh) comes into effect too late, at a later repaint. The more the operation is embedded in BeginUpdate/EndUpdate seequences. So, does anybody know if it's possible to enforce that for an particular node the painting (or removing)  of the belonging plus sign does happen?  (Best, without OwnerDraw) Or is this idea too far away from the standards? ---------------------------------------------------------------------- d7_2_laz · 2021-07-08 16:54 My finding ist, that, within treeview.inc, TCustomTreeView.DoPaint;,   the fetch of the imageindex simply is too late for that purpose. Does any reason speaks against, to place it earlier? HERE, just before Node.HasChildren is queried: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   // Right here, before the HasExpandSign definition is done (below), where Node.HasChildren  if (Images <> nil) then  begin      if FSelectedNode <> Node then  begin        GetImageIndex(Node);      end;  end;   VertMid := NodeRect.Top + (NodeRect.Bottom - NodeRect.Top) div 2;  HasExpandSign := ShowButtons and Node.HasChildren and ((tvoShowRoot in Options) or (Node.Parent <> nil)); Here it is too late, because the expand sign stuff had been already done: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i     // draw icon    if (Images <> nil) then    begin      if FSelectedNode <> Node then      begin      //  Please fetch that earlier      //    GetImageIndex(Node);        ImgIndex := Node.ImageIndex      end  Placing it earlier, weill give a chance that setting Node.HasChildren will have the desired effect. That will give the double benefit that looping a large amount of folders for subfolders, one could save those IOs and use, for instance, SHGetFileInfoW needed for to retrieve Icons from the file system. And, it would be done only for the items that are visible! The speed benefit, ie. fhor the ShellTreeView, could be tremendous! My test folder is C:\Windows\WinSxS.  What do you think? ---------------------------------------------------------------------- d7_2_laz · 2021-07-09 07:48 Hello Jamie, fully agree, it wouldn't be a good practice to do 50  Application.ProcessMessages for to update 50 visible tree nodes. And, at least in my case, it had not worked. But that is not needed, really. if i only could convince the maintainer of the treeview.inc to do this little change (-> to move the fetch of the imageindex some few lines above, just before Node.HasChildren is evaluated), then there are much better possibilites ready to use. Please do me a favour and try: in any Windows file explorer: open C:\Windows, navigate to the last node. Should be "WinSxS". Expand this node (via click on the plus icon). See how long it takes ... Why? Only because for thousands of subfolders each individual subfolder is opened for to identify in a for-loop if itself contains subfolders, Simply for to decide: should the plus sign be set before the treenode or not. Tons of IOs !! What do you think of to get rid of that easily, and to speedup the treeview drastically (more than doubled). So that the folder in speech now opens  *instantly**? Because simply some flags need to be correctly interpreted at the right place that are already available in OnGetImageIndex for a file object  orienteded treeview. But only if the change within treeview.inc will be done, sigh ... Of course i did the change already on my system, but with the next Lazarus release it will be away So i'm hoping much that the change (as described above) will go into the source, now allowing more possiblities. My argument is: for a treeview, the chances are high that it deals with file objects presented in the nodes. OnGetImageIndex typically does file access for to retrieve the icons of those file objects. Why not allow the possiblity of a reuse of this already existant information for to provide other relevant attriebutes That, otherwise, cost a lot of IOs? By a simple source line shift? Might a maaintainer of the treeview.inc take a look at this, please? ---------------------------------------------------------------------- d7_2_laz · 2021-07-09 17:11 For to be fair, for the ShellTreeView there are excellent examples from wp and GetMen (can be found in the thread about ShellTreeView and system icons), but they would require some minor modifications to take full advantage (should be integrated in 2.2, if i understood right). However: was ShellTreeView as well as my own app (and most comparable others) are doing when expanding a node, is: search for subfolders, and iterate each subfolder for to detect if this one contains subfolders. I was never happy with that, therefore my test of alternatives to the time-consuming HasSubFolders routine. I ended up with this result:  'Expanding took: 156 ms  for:  C:\Windows\WinSxS'. 16 ms for the Windows folder; 0ms for the C: drive .... What does that cost? - 2 lines of easy code within my app (ok, might be 5 lines for better readability), within OnGetImageIndex - Preocondition: a very small change in treeview.inc for to let it work   The change is described above: move the call of OnGetImageIndex just before the line referencing Node.HasChilden What's about the correctness of the alternative approach? Test: 20000 folders on my systems.  5 folders of them are differently interpreted than before.  And those 5 are mostly rather stuff for a philosophical debate ("should $Recycle.Bin indicate to have subfodlers"). Correct are 99,98% ... not a world-class science. but should be fairly good enough for a daily-work tool. Does this sound interesting enough for development to do the change in treeview.inc for to allow the app to take this benefit? ---------------------------------------------------------------------- d7_2_laz · 2021-07-10 11:51 To add: this approach would be virtual in the sense, that, when expanding a node, it would not be necessary to gather the information "HasSubfolders" for all objects at once. But only for those few ones, that initially become visible in the viewport / that are initially painted. What do you mean? ---------------------------------------------------------------------- d7_2_laz · 2021-07-10 15:09 If i understand right (not quite sure): it's similar to:  for to decide whether a child node just created should have a plus sign or not,  look into the directory that is represented by the child node's name for to see if it contains at at least _one element_, file or folder doesn't matter. Right? That wouldn't be the way i'd like to go, because: - the result might be wrong, because an 'IsDirectoryEmpty' (= contains at least one file or one folder) or an "HasSubfolders" (= contains at least one folder) is really not the same. So there are too many cases where you'll indicate with a plus sign that is is expandable where in fact it does not contain folders and so, shouldn't indicate to be expandable. - as you still need to dig phyically within each child node's directory, there is no real performance win. - you need to do that for _each_  subfolder within the node that is just being in progress to be expanded. Result of a test using the method "Node,HasChildren := directory_is_ not_empty": Expanding took: 2671 ms  for:  C:\Windows\WinSx If i don't decide by "is not empty". but by "contains definitely at least one folder": the result had been 3204 ms. This is not a very big difference. Tre result for the method i'd  prefer had been: 'Expanding took: 156 ms  for:  C:\Windows\WinSx' So i would still vote for this really small change in treeview,inc allowing an alternative approach. In ShellTreeView, OnGetImageIndex, the file icons are only retrieved when first painted. Why not allow to do the same with the plus sign, simply by shiftig the call of OnGetImageIndex some lines above, without harm, but allowing so to do a little bit more with the plus indicator here too? What's the problem to accept this proposal? ---------------------------------------------------------------------- JuhaManninen · 2021-07-10 17:56 Quote from: d7_2_laz on July 10, 2021, 05:09:41 pmWhat's the problem to accept this proposal? Please upload a patch here and I will take a look. A patch tells more than thousand words. ---------------------------------------------------------------------- d7_2_laz · 2021-07-11 08:50 Hello Juha, thanks for the offer! I attach the original version as retrieved from sourceforge (lazarus-2.0.12-fpc-3.2.0-win64.exe) this march as well as the patched version below. You'll see the change easily via a diff. The change itself is very trivial and for itself it doesn't do anything. It only gives the chance to make things possible, that otherwise won't directly reflect in the node's current paint (re-use of already existent API calls for to adapt HasChildren). ---------------------------------------------------------------------- JuhaManninen · 2021-07-11 09:55 Quote from: d7_2_laz on July 11, 2021, 10:50:15 amHello Juha, thanks for the offer! I attach the original version as retrieved from sourceforge (lazarus-2.0.12-fpc-3.2.0-win64.exe) this march as well as the patched version below. You'll see the change easily via a diff. Sorry but you should learn to create actual patch files. It is very easy:  https://wiki.lazarus.freepascal.org/Creating_A_Patch You can use either SVN or Git. A patch file has many advantages. There is no dangar of reverting other recent changes. Looking at a patch is often enough to see if it is good or not. The diff format + "patch" command are very handy tools. ---------------------------------------------------------------------- wp · 2021-07-11 10:16 Quote from: d7_2_laz on July 09, 2021, 07:11:37 pmHowever: was ShellTreeView as well as my own app (and most comparable others) are doing when expanding a node, is: search for subfolders, and iterate each subfolder for to detect if this one contains subfolders. I don't think so: The TShellListView starts with "PopulateWithBaseFiles" which only iterates the files/folders in the root. Then, when I open the "C:\" node, it calls "PopulateTreeNodewithFiles" which searches the files at the current level (C:\) and creates a node for each one of them. In order to decide whether a node has Children it calls the local function HasSubDir which scans the folder level below (but not deeper). When a node is clicked to expand the method CanExand is called which populates the child level of the clicked node by calling PopulateTreeNodeWithFiles again. So, in my opinion this is exactly how it should be done. This is for trunk, I did not check the releases. ---------------------------------------------------------------------- d7_2_laz · 2021-07-11 12:50 Hello wp, thanks for reply! please don't misunderstand - i didn't say anything against shellcontrols, i did follow a similar file access approach myself and wouldn't vote for any changes here, the more is has to be thought cross-platform so far ever possible. It's fine and fast! However, admittedly using only a single platform (windows), i thought about how to avoid something like this local function HasSubDir, which implies a lot of time expensive file iterations, needed only for to set NewNode.HasChildren. Special situation, special approach. There are alternatives (one might choose them or not), but they would require i could set HasChildren within OnGetImageIndex, so that this becomes part of the node paint. Solvable by a little code change, that's all, therefore i dis ask. The last months i got more familiar with Lazarus and love to work with it very much, digging sometimes into workarounds for specific situations. But the development admin system, installing trunk or versioning systems. is not my cosmos and scope, i know my limits here. What i can do is to explain the demand for a change as best as i can to the review by a developer, who knows the develop system and overall architecture best, and who might accept this demand or not. In the case here it's a bit hard, because we're not speaking about a reproducable bug (absolutely not!), but about  a little bit more flexibility for a specific system. The change itself is trivial: to move the line "GetImageIndex(Node);" some lines above, before HasExpandSign is set: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  // Do this call here and not later, so that a modification of Node.HasChildren might come into effect for HasExpandSign assignment  if (Images <> nil) then  begin      if FSelectedNode <> Node then  begin           GetImageIndex(Node);      end;  end;   VertMid := NodeRect.Top + (NodeRect.Bottom - NodeRect.Top) div 2;  HasExpandSign := ShowButtons and Node.HasChildren and ((tvoShowRoot in Options) or (Node.Parent <> nil)); ---------------------------------------------------------------------- wp · 2021-07-11 16:00 This change will disrupt the order of actions in DoPaintNode: draw background - draw tree lines - draw expand sign - draw state icon - draw icon - draw text - draw insert mark. It is an awfully complicated method already, and your change would make it even more confuse. When somebody refactors this method he will wonder why the "draw icon" part is split into two parts - maybe he will bring it back in order, and your problem is back. Your solution is a hack. Why don't you set HasChildren when the node is created? ---------------------------------------------------------------------- d7_2_laz · 2021-07-12 09:11 Hello wp, why not directly when the node is created?  Because for to decide about the "Haschildren" prpperty, typically here long during file iterations are needed. I did a short test usingone of the demos from the thread "ShellTreeview and System Icons", Iterating a heavy populated folder like Windows}WinSxS" (18000 subfolders) it takes 2563 ms. Replacing the call of "HasSubDir" testwise by a hard-coded value, it takes 141 ms! So most of the time is spent here, and so i thought, from time to time. could this be improved. Probably it could be improved using another already existent system call, with a small loss of accuracy. Namely via SHGetFileInfoW as already placed within the OnGetImageIndex callback. I renewed my test using the shell tree demo now:  result: 156 ms. Does it make sense i prepare a modified version of the shell tree demo for you so that you can see yourself (A simple proof of concept only. nothing stable or finalized) The functional accuracy, in my app, is 99,975 % (only 5 of 20000 system folders are found to be differently interpreted, eg. "$Recycle.Bin"). So i think it's a legimate question if it might be a usable option for an app ... maximal speed costing minimal quality loss. Not as standard behaviour (the more it is os-specific), but as an optional decision for certain use cases. For now i spared out the specifc technique yet, because it would need some explanation and would surely give room for specific discussions; too much for here! Of course this needed change in treeview.inc will disrupt the natural order of actions in DoPaintNode ... in so far, as the assignment of Node.ImageIndex will be shifted a bit above. Admittedly, that does not look nice. But in fact, regarding variables or functional interference, i cannot see any real impact. You see one? I see it so, that  simply a variable assignment is done a bit earlier. After working with the change a couple of days, i did not encounter any side effects. If it doesn't sound comfortable, i could imagine an alternative that is completely transparent to / separated from the normal flow (a kind of purely optional to use procedure, like FOnPreGetImageIndex or, maybe better: more generic, FOnCustomGetPreAssigns or whatsoever). One could use it on own risc alternatively to FOnGetImageIndex). Normally it would be an unused slot. Does those ideas sound crazy? ---------------------------------------------------------------------- wp · 2021-07-12 10:39 OK, let met put this in my own words: suppose I have the TShellTreeView, open the c:\Windows node - this is fast. One of the subnodes is c:\windows\winsxs. I click on the '+' in front of the winsxs folder - it takes 1.5 s on my system until this folder opens - this is long... Looking at the folder in the explorer I see that there are many directories in this folder (more than 15,000). For each of these sub-folders the ShellTreeView inserts a node, and after inserting the node it has to check whether the node has children. It checks whether the node is a directory (which almost certainly it is), scans through this directory until it finds another directory or file (if option NonFolders is true) - if yes the node would have children, otherwise not. Since there are many folders in winsxs this will take some time. Your idea is to move the check for "HasChildren" into the painting cycle (DoPaintNode) because only the visible nodes are painted, and this can be at most only some dozens - so there only some dozens of directories to be checked (winxsx has 15.000...). Sounds reasonable. What I don't like why you want to abuse the OnGetImageIndex event for this? DoPaintNode is awfully long and a good candidate for refactoring. Therefore moving OnGetImageIndex some lines higher in the code probably will be reverted in the refactored version and you will have the same issue again. Why not introduce a new event, OnHasChildren, which is called somewhere in the beginning of DoPaintNode? This probably will overwrite an already assigned Node.HasChildren. How to protect this? And Node.HasChildren is used at many locations in the CustomTreeView (DoCanExpand, InternalMove, DeleteChildren, GetInternalMarkAt). Since in your solution HasChildren is valid only after painting, what if an undrawn node is processed and its HasChildren is requested (e.g. expand c:\windows\winsxs when only c:\ is open -- this will not work...!)? Maybe there should be a new TTreeNodeState tsValidHasChildren. Querying Node.HasChildren should check if the HasChildren option is valid; if not it should fire the OnHasChildren event. This would decouple HasChildren from the painting cycle. However, this will be a non-trivial change, and there will be some chance for errors... ---------------------------------------------------------------------- d7_2_laz · 2021-07-12 12:30 Hello wp, you got the idea, exactly! And the good news is: it does work fine (tested with shelltree demo too). With the only specification: the idea behind is not to move the check for "HasChildren" into the painting cycle, but to move it there, where the SHGetFileInfoW is already doing it's job (here: for getImageIndex);. With the pleasant accompanying effect, that it's only for the node that come into the view (maybe 30 instad of 18000 at once). And what we surely don't want is that SHGetFileInfoW (we know it's time intensive) should be called again twice, elsewhere.That won't speedup, but slow down the overall process noticeably and kill the desired effect. About the abuse the OnGetImageIndex: fully agree! I don't like it either! (unfortunately the expensive SHGetFileInfoW is already herein ...  and i want to reuse it's results). So my preferred option would be, just as you proposed, a new event - that might be, purely optional, used or not. A purely generic optional slot, transparent and nothing changing for existing apps. Whatever it is named ....  so i thought about, in the alternative proposal, something like that: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i if FSelectedNode <> Node then  begin    if Assigned(FOnCustomGetSettings) then    // but that requires a variable def. outside of treeview.inc (commctrls)       FOnCustomGetSettings(Self, Node);......HasExpandSign := ShowButtons and Node.HasChildren and ((tvoShowRoot in Options) or (Node.Parent <> nil)); The only reason i didn't use that, had been that i tried to avoid to touch another file (commctrls.pp) for to place the definitions here. Whatever this custom event is, it should meet the twofold requirements; - can fetch the imageindex here as efficient as before (= only when needed).  Instead of using OnGetImageIndex of course - can reuse SHGetFileInfoW for to fetch other attributes and allow to set (and later paint) HasChildren here.   SHGetFileInfoW shouldn't be forced to be called elsewhere again (the need for multiple calls should be avoided) So probably within PaintNode might stay the best place though. ---------------------------------------------------------------------- d7_2_laz · 2021-07-12 15:24 I need to do a correcton in one point (wrong assumption): as far as duplicate calls to SHGetFileInfoW are not done along all subfolders of the test folders (18000 in the test case(, but only for those that are part to be painted at once (let's say 30 as an example), it should NOT harm the performance. So a dedicated event slot within PainNode called separated from OnGetImageIndex, would make sense too. I tested this by adding a second dummy SHGetFileInfoW call within OnGetImageIndex and had no noticeable impact. I assume the test could be representative. Sorry! ---------------------------------------------------------------------- wp · 2021-07-12 20:44 Quote from: wp on July 12, 2021, 12:39:08 pmNode.HasChildren is used at many locations in the CustomTreeView (DoCanExpand, InternalMove, DeleteChildren, GetInternalMarkAt). Since in your solution HasChildren is valid only after painting, what if an undrawn node is processed and its HasChildren is requested (e.g. expand c:\windows\winsxs when only c:\ is open -- this will not work...!)? I added a TShellTreeView to a form and a button to open c:\windows\winsxs in the tree: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.Button1Click(Sender: TObject);begin  ShellTreeView1.Path := 'C:\Windows\WinSxS';end; When I run this program the requested folder is opened in the treeview (after some time, of course...). Now I tried to simulate your optimization in the case when the node to be opened has not yet been painted (because this is where the node gets the HasChildren value in your optimization): In shellctrls I commented the lines in TCustomShellTreeView.PopulateTreeNodeWithFiles which assign a value of HasChildren to the NewNode. So, the nodes do not yet know HasChildren upon creation. Then, in TCustomTreeView.DoPaintNode, I added "Node.HasChildren" at the top - this is a simplification, but corresponds to what you want to do: Assign the HasChildren value somewhere in this procedure. When I run the program now and click the button, the ShellTreeView opens only the folder C:\Windows - it cannot open its WinSxS subfolder because the nodes have not yet been painted. Therefore, I am convinced that your idea must be decoupled from the painting cycle. What I try to implement next is as follows: - Add a new NodeState, nsValidHasChildren - When a new node is created the nsValidHasChildren is not yet included in the NodeStates, i.e. it is unknown whether the node has children. - The setter TTreeNode.SetHasChildren addes nsValidHasChildren to the NodeStates - now it is known whether the node has children. - The getter TTreeNode.GetHasChildren checks whether nsvalidHasChildren already has been added -- in this case it return the usual value. If it has not yet been added it causes the treeview to call a virtual funtion NodeHasChildren which fires an event OnHasChildren. The ShellTreeview overrides this method to scans the file system and looks for children. The result is stored as nsHasChildren in the NodeStates, and the nsValidHasChildren is added to signal that the HasChildren value is known now as well. This architecture avoids setting the HasChildren value at creation of the nodes. But via the method/event the true value can determined at the first time when it is needed. I am attaching preliminarily patched version of the related units. The side cases with hidden and non-folder objects are not yet checked. Copy the files over the original ones (make a backup copy first!) and recompile the LCL. With these modification the folder C:\Windows\WinSxS opens instantly. ---------------------------------------------------------------------- d7_2_laz · 2021-07-13 09:07 Quote"Since in your solution HasChildren is valid only after painting ... " Uh, really, i better had mentioned that, in my approach i initialized "HasChildren" anyway at node's creation time by an explicit assignment, before revisiting the property when the node first becomes visible. Would be a bad idea to let it undefined. wp, I think i understood how your changed approach should work, and indeed it's a more proper and clean strategy, generic, world class A, pretty cool  But, couldn't compile so far for to test and verify, due to other release-specific dependencies; i'm still on 2.0.12. Need to wait until i have the 2.2 installed or maybe an RC (not quite sure yet). So i set this topic to something like "waittest 2.2" and come back to it later. Would that be ok? Great thanks for all of your inputs and interest, and especially for this code change proposal! At the end it could power up a folder tree app again a lot and let it work extremely fast even for (admittedly:) maybe rarely occuring use cases (highly populated folders). ---------------------------------------------------------------------- wp · 2021-07-13 09:39 Quote from: d7_2_laz on July 13, 2021, 11:07:03 amNeed to wait until i have the 2.2 installed or maybe an RC (not quite sure yet). No risk with 2.2RC1 (which is available now) when you select the "secondary installation" in the installer (also: specify a new directory for the config data, and do not register any file types to this version) - this way your current installation is left untouched. ---------------------------------------------------------------------- d7_2_laz · 2021-07-13 12:07 wp, as Info: parallel install RC1 with a separate install dir and config leaded me to a problem; i'll address that in the RC1 thread and need to have it sorted out before. ---------------------------------------------------------------------- d7_2_laz · 2021-07-13 15:51 wp, the first compile  of the project (here: the modifeied shwellview demo) with RC1 without the 3 changed files was ok. That lets me assume that the right lazarus dir (= from RC1) is used, as within the 2.0.12 folder the 3 changed files are still herein, which leaded fo the compile errors as mentioned yesterday. But, now, if i copy the tree files on top of the RC1, the same compile error does occur. So i'm not sure whether there are other dependcies that i'm not yet aware of? They are starting from a missing PageClass (from customnotebook.inc), then are about mismatch of definitions within listitem.inc, and thre are some others (from customlistview.inc, comctrls.pp ,, in total 12 errors) Sure that the installed RC1 is sufficient to let the changed files work? PS: there's also a strange thing thing with a Dwarf-setting dialog (new for me), but i spare that out for now ---------------------------------------------------------------------- wp · 2021-07-13 16:26 Sorry. There is a week between tagging of v2.2 and my changes, and since comctrls contains many components there is a chance that some other changes may have crept in. Ideally you should use svn or, in a few days, git to get the full trunk version - then everything would fit together. Or you must extract the differences manually and copy them into the original files. You can use a diff utility (e.g. WinMerge, or TextDiff) to find the differences. I think these are my modifications: - At the end of TNodeState I added "nsValidHasChildren" - I added an event type, TTVHasChildrenEvent - I modified TTreeNode.GetHasChildren, SetHasChildren - In TCustomTreeView, I added a private variable FOnHasChildren: TTVHasChildrenEvent, a function NodeHasChildren and a "property OnHasChildren: TTVHasChildrenEvent read FOnHasChildren write FOnHasChildren" which is published in TTreeView and TShellTreeView. When you search for these items in my modified files you can probably see the differences and copy the new code over the original files. ---------------------------------------------------------------------- d7_2_laz · 2021-07-13 18:10 Thanks wp, no problem, i thought so. I'll choose the latter method (integrate manually), but it will takes a bit, due to some interrupts for a day or so. Sorry for a bit delay! ---------------------------------------------------------------------- d7_2_laz · 2021-07-14 11:35 Integrated the changes (hopefully correct) within RC files and could compile again. I noticed that, when opening a drive's node (in shellview demo), only the root folder is listed, and i don't know if it's by intention. I would have expected that all root folders are listed, awaiting now i could set the property HasChildren for those of them that become visible, with the help of the newly introducted event callback "OnHasChildren" (that one would be a function; right? As something like: function TfMain.STVOnHasChildren(Sender: TCustomTreeView; Node: TTreeNode): Boolean;) Or would major parts or the demo to be rewritten? ---------------------------------------------------------------------- wp · 2021-07-14 12:14 Call me stupid, but where is shellviewdemo? Please don't hunt me through all previous posts related to this topic. I place a TShellTreeView on the form, compile and run, and I see all the drives on my system, all of them with a "+" so that they can be opened. I open C:\, then Windows, then WinSxS, and WinSxS opens instantly (after the modifications, of course). When otHidden is included in ObjectTypes, then I see C:\$Recycle.Bin. What worries me is that this folder cannot be expanded (has no "+") - although in Windows Explorer it can. But this must be an old issue because the same test program compiled with Laz 2.0.12 behaves the same way. It could be that I do not have the permission to open the objects in c:\$Recycle.Bin (at least, this is what Windows Explorer tells me) When otNonFolders is included in ObjectTypes (in addition to otHidden) c:\$Recycle.Bin can be opened. And I see individual files, and even hidden files such as pagefile.sys or hyberfil.sys. So, my impression is that the modified TShellTreeView behaves correctly. If you don't think so, give me exact steps how to reproduce this. When a TShellListview is attached to the ShellTreeView, however, WinSxS opens about as slow as before because now the ListView opens all the 15000 directories, so all this effort was rather useless... ---------------------------------------------------------------------- d7_2_laz · 2021-07-14 14:23 Hello wp, oops ... this demo (from GetMem) was from the thread about  ShellTreeView and system icons [page 3] , in which you hand been involved, so i assumed you remembered; sorry, my fault! I used it for some tests and cross checks here. Testing with TShellTreeView in the IDE: yes, i'm seeing only _one_ folder when opening a drive node. Independently from the setting of otHidden or otNonFolders. I simply had placed a shelltreeview on the forms and tried with various object types. Only one subfolder per expanded node  (cannot exclude that one of my 3 adapted files not yet fully correct). And i think that should not be. When expanding a node, it should show all subfolders (including even hidden ones depending on an option). This is not time expensive at all!  But deciding for all of them (maybe 18000) whether they appear to be expanded, by digging on file- IO base / loop within each single one of them, that doess cost the major part of the time. Btw, testing within the IDE: the event "OnHasChildren" does not appear here in the event list. Yoe see it in the inspector? How did you set it?  About $Recycle.Bin: as told, had been one of the 5 deviants in my own alternative approach where i said: one might ignore that. Because maybe it's a philosophical question if Recycle.Bin show up as to have subfolders or not ... My intuition is telling me: maybe this new approach might be a bit hard to understand and to reimplement within an app. My own approach (last version:  simply provide a, so to say, generic, neutral, multi-purpose event slot before HasChildren is used, and then it is super-easy to use it for the eg. for the HasChildren stuff. Not more than 3 lines within treeview.inc (see above an example as "OnCustomGetSettings") and the processing time in a folder tree might shrink drastically. The cost is: conceptaally it does not look very nice or straight. But it may be harder to make the shellview demo concept-ready ... Please ignore  the listview in this context, that's another story (it has something to do if it's virtual or not). ---------------------------------------------------------------------- wp · 2021-07-14 15:05 I don't want to use this demo because there were so many experiments and tests. Starting point must be an empty form with a TShellTreeView. Without changing anything the tree is empty in the IDE, this is different from Delphi but has been like this all the time here because PopulateWithBaseFiles exits immediately at the start when in design mode. When I run this simple project, however, I see all the drives as described in the previous mail. Returning to the Object Inspector, when I set Root to "C:" I see only the root node at designtime, again because the tree is not populated at designtime. Therefore I do not understand when you say "Testing with TShellTreeView in the IDE: yes, i'm seeing only _one_ folder when opening a drive node." Again, when the program runs, all the subnodes of c: are added. Quote from: d7_2_laz on July 14, 2021, 04:23:37 pmtesting within the IDE: the event "OnHasChildren" does not appear here in the event list. Yoe see it in the inspector? How did you set it?  Did you publish the event for the TShellTreeView in unit ShellCtrls? But the event is only needed when you want to use your own method to detect child nodes. The event is fired by the virtual function "NodeHasChildren". TShellTreeView overrides this function such that, when no event handler is assigned, the old HasSubDir function is called. ---------------------------------------------------------------------- d7_2_laz · 2021-07-14 16:05 Yes, OnHasChildren is published. "Root" stayed empty, for to start with the drives. "Within the IDE" did mean, not the demo is used, but the thing is newly created in the IDE by placing the control etc. . -  Running the app within the IDE, or by clicking the executable, didn't make a difference. Maybe we would can shorten it if you try to run my "mini-project", only containing the shelltreeview, and see, if you result in more than one folder beyond C:\. ? If yes, it's eighter a mistake in my exchanged files (I'll add them here, maybe could a diff against yours for to assure), or something else in the envrionment. ---------------------------------------------------------------------- wp · 2021-07-14 16:32 I see all drives - see attached screenshot. Your ShellCtrls is lacking the loop over the found files in TCustomShellTreeView.PopulateTreeNodeWithFiles. Here is the correct version (with the old commented stuff removed): Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i { Returns true if at least one item was added, false otherwise }function TCustomShellTreeView.PopulateTreeNodeWithFiles(  ANode: TTreeNode; ANodePath: string): Boolean;var  i: Integer;  Files: TStringList;  NewNode: TTreeNode;  CanAdd: Boolean;begin  Result := False;  // avoids crashes in the IDE by not populating during design  if (csDesigning in ComponentState) then Exit;   Files := TStringList.Create;  Items.BeginUpdate;  try    Files.OwnsObjects := True;    GetFilesInDir(ANodePath, AllFilesMask, FObjectTypes, Files, FFileSortType);    Result := Files.Count > 0;     for i := 0 to Files.Count - 1 do    begin      CanAdd := True;      with TFileItem(Files.Objects[i]) do DoAddItem(FBasePath, FileInfo, CanAdd);      if CanAdd then      begin        NewNode := Items.AddChildObject(ANode, Files[i], nil);        TShellTreeNode(NewNode).FFileInfo := TFileItem(Files.Objects[i]).FileInfo;        TShellTreeNode(NewNode).S etBasePath(TFileItem(Files.Objects[i]).FBasePath);      end;    end;  finally    Files.Free;    Items.EndUpdate;  end;end; ---------------------------------------------------------------------- d7_2_laz · 2021-07-14 18:46 Yes, right, the missing line gots a victim of a misplaced edit action late evening  - thanks! Much better now, even heavy populated folders are opened lightning fast    So i hope that for you it had been a well spent time though. The second good news is: the change is directly usable by the shellview demo mentioned too, Which benefits without further changes. - One little side node (only an observation): surprisingly the first open is faster than subsequent ones (folder expand -> collapse --> expand); not severe at all, but a bit curious. - For me it's now the task to retransport the principle of the ShellControls changes into my own app, that is working generically without the ShellControls .. ---------------------------------------------------------------------- d7_2_laz · 2021-07-15 08:09 The outcome is: the pinciple is nicely applicable to apps too that don't use the ShellControls itself: it works very fine and fast! Tremendous speedup .. perfectly solved! ---------------------------------------------------------------------- wp · 2021-07-15 08:58 Committed to trunk and requested back-porting to v2.2RC2. The largest part of the slow-down when a heavily populated node is opened a second time is due to a missing BeginUpdate/EndUpdate pair around Node.DeleteChildren in the ShellTreeView's CanExpand method (about 20 s for c:\Windows\WinSxS) - but this was already included in the pre-version that you used. With BeginUpdate/EndUpdate the delay shortens to about 1 second. I checked the involved procedures and found nothing suspicious. The problem seems to be that all subnodes have to be iterated when their parent node is closed. ---------------------------------------------------------------------- d7_2_laz · 2021-07-15 12:26 Had guessed such a direction, as after transfer of the change to my app, here the effect did _not_ occur. I had already noticed this difference in CanExpand, but had not been sure if it was directly related to your recent change / this specific topic. Btw, it is good to see that the fix from the thread "Trouble with CustomTreeView (OnExpand)" (SIGSEGV at CanExpand when inside Begin-/End-Update) indeed is part of 2.2 - the problem did not reoccur here. A lot of thanks for having involved so deeply! Great experience.