|
Hi Jim,
I changed the SetTreeViewImageList to be in the VisibleChanged event and everything is working great in the IDE and the compiled <release> version... including icons. However, I made a significant change to your sample in that I don't have a treeview sited on a usercontrol. I have a class that inherits from Treeview. I wanted to be able to have your functionality but still use the control as a regular treeview. I use an event sink to capture and respond to events from the base treeview:
'In declarations
Private WithEvents EventSink As ExTreeView
'In Sub New
EventSink = Me
Private Sub EventSink_VisibleChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles EventSink.VisibleChanged
If MyBase.Visible Then
SystemImageListManager.SetTreeViewImageList(Me, False)
If Not Root Is Nothing Then
Root.Expand()
If Not IsNothing(MyBase.SelectedNode) Then
MyBase.SelectedNode.Expand()
Else
MyBase.SelectedNode = Me.Root
End If
End If
End If
I'm not using a usercontrol at all. I simply declare a module-level variable (withevents, of course) and add it to my controls on Sub New of the form. I would imagine that one could also load the derived class into the usercontrol and the problem may be fixed there as well, but I havent tried that.
Let me know if you'd like additional info, and thanks very much for a great control.
BTW, another thing I'd like to look into is using, and responding to, the Shell's context menu in the treeview. I believe this can be done using IContextMenu. Have you ever investigated this functionality?
Thanks,
Jeff Beem
|
|
|
|
|
Hmmm, Very interesting. It is interesting in it's own right and also in terms of its implications for what I have been spending my time on, Drag&Drop.
I have to think this over, and will reply off-forum in a little while.
There is some overall commonality between Drag&Drop and IContextMenu, and, although I haven't thought much beyond that, it seems that a coordinated approach will be useful. The main issue is that my current implementation of Drag&Drop requires ExpTree to be a user control rather than an inherited one. However, it seems possible that this could be changed. I will be in touch.
Jim Parsells
|
|
|
|
|
Hi Jim,
I made a mistake. I guess I'm still used to some of the VB6 terminologies my peers and I used to use where "in the IDE" also meant "while running in debug mode". I was debugging, seeing the icons, and that's what I was talking about when I said I had icons "in the IDE". I just compiled my dll and loaded the class into a usercontrol, compiled that control, and then placed the control on a form and there were no icons "in the IDE". However, to me that's not such a big deal. If it works correctly at runtime (including tooltips") that's as much as one can really expect, IMHO.
Thanks for taking all my questions
-Jeff Beem
|
|
|
|
|
Jeff,
OK -- that's the choice you get to make. I just wish someone could figure out how to fix it. I do it one way, but this thread shows how to do it the other way.
I still will get back to you off-forum about IContext. Of course, you can use .Net's context menus with ExpTree, but if you wish to see the proper Shell context menu for a folder, you will have to use IContext and do some mildly tricky stuff (he says wryly) to get the desired effect. It probably is not as bad as the code I had to use to get the Shell IDataObject to drop on a Shell Folder's IDropTarget. That actually wasn't too bad, though the devil lives in the details. My problems with Drag&Drop have more to do with updating ExpTree to reflect the operation.
Jim Parsells
|
|
|
|
|
The component success to get the right icon for any file or folder and that's not trivial at all.
Though, the code is a mess. More documentary on the code itself and the main algorithm could be a bless.
|
|
|
|
|
Thanks for the feedback. A little more clarification of your comment would be nice though.
Jim Parsells
|
|
|
|
|
The whole code is not documented at all, it's a bunch of code line without explaining what are you doing and why?
For example, as far as I know when you have a PIDL you can get an icon with SHGetFileInfo without any LinkOverlays flags but you do check if the file is a link, system file etc.
I'm not saying it's not good and everything seem to work but I couldn't understood what you are trying to do.
I think that you should wrap some of the functionality in wrapper classes. (For example: FileDataPath that gets file path and handle files only and performing all the shells manipulations inside it).
I think that if you had seperated directories and files handling to different classes and try to make some sense in the code flow it could be perfect.
|
|
|
|
|
The article is the documentation. I assume that people would read & understand that, before going on to the code. I specifically did not devote much of the article to the CShItem class -- leaving that to a separate article "if there is enough interest shown". There hasn't been.
The article DOES explain -- in terms of SystemListManager -- why ShGetFileInfo is called the way it is called.
Dividing the CShItem class between File related and Directory related simply complicates the using code's task, and does not, by definition, give a good representation of System Folders, Folder Shortcuts, and other non-Filesystem entities --- exactly the problem with the .Net File and Directory classes.
Jim Parsells
|
|
|
|
|
Most of the shell calls lies in the CShItem.
Leaving it undocumented causes this main class to be uncovered on both - article and code.
Here is a typical code piece:
Private Function GetContents(ByVal flags As SHCONTF) As ArrayList<br />
.....<br />
<br />
If Not XPorAbove Then<br />
If CBool(attrFlag And SFGAO.FOLDER) Then 'Don't need it<br />
GoTo SKIPONE<br />
End If<br />
Else 'XP or above<br />
If CBool(attrFlag And SFGAO.FOLDER) AndAlso _<br />
Not CBool(attrFlag And SFGAO.STREAM) Then<br />
GoTo SKIPONE<br />
End If<br />
End If<br />
End If<br />
rVal.Add(New CShItem(m_Folder, item, Me))<br />
SKIPONE: Marshal.FreeCoTaskMem(item) 'if New kept it, it kept a copy<br />
item = IntPtr.Zero<br />
itemCnt = 0<br />
Application.DoEvents()<br />
HR = IEnum.GetNext(1, item, itemCnt)<br />
Loop<br />
Else<br />
If HR <> 1 Then GoTo HRError '1 means no more<br />
End If<br />
Else : GoTo HRError<br />
End If<br />
'Normal Exit<br />
NORMAL: If Not IsNothing(IEnum) Then<br />
Marshal.ReleaseComObject(IEnum)<br />
End If<br />
rVal.TrimToSize()<br />
Return rVal<br />
<br />
' Error Exit for all Com errors<br />
HRError: 'not ready disks will return the following error<br />
If HR = &HFFFFFFFF800704C7 Then<br />
GoTo NORMAL<br />
ElseIf HR = &HFFFFFFFF80070015 Then<br />
GoTo NORMAL<br />
'unavailable net resources will return these<br />
ElseIf HR = &HFFFFFFFF80040E96 Or HR = &HFFFFFFFF80040E19 Then<br />
GoTo NORMAL<br />
ElseIf HR = &HFFFFFFFF80004001 Then 'Certain "Not Implemented" features will return this<br />
GoTo NORMAL<br />
End If<br />
If Not IsNothing(IEnum) Then Marshal.ReleaseComObject(IEnum)<br />
#If Debug Then<br />
Marshal.ThrowExceptionForHR(HR)<br />
#End If<br />
Return New ArrayList() 'sometimes it is a non-fatal error,ignored<br />
End Function
GOTO, explicit values for HR are all what is mainly called "spaghetti code". (Function based with many GOTO clauses and without OO design).
Jim, you succeed where others have failed but you have to understand that this is a friendly critics.
Open source is all about sharing knowledge and the code in it's current condition doesn't do that.
|
|
|
|
|
Ok,now w're getting somewhere. You are expressing an interest in knowing more about the CShItem class -- you are the first to directly do so.
My thoughts on GOTO: I try to use GOTO only where it ADDs to clarity. The GOTO debate has long since been beaten to death, and my position is that a GOTO can add more clarity than a page or two of terminating End Ifs or (worse) close brackets "}". Some functions, and GetContents is one of them, are not expressable in a single screen of code. My typical approach to this is to place all error exits at the end of the function, immediately preceeded by the normal exit -- unless a Try-Catch block will do the job -- which it won't in this case.
The long list of explicit error testing, as frequently discussed in the forum, is there to allow programmers to investigate OTHER errors that may arise. In my current version of GetContents (not yet published) the entire section is replaced by code that just ignores any errors and returns an empty Arraylist on error. The change is simple.
NORMAL: If Not IsNothing(IEnum) Then
Marshal.ReleaseComObject(IEnum)
End If
rVal.TrimToSize()
Return rVal
' Error Exit for all Com errors
HRError: rVal = New ArrayList()
GoTo NORMAL
End Function
Note that I still use GOTO to get to HRError and to NORMAL - the exit points.
The code that starts "If Not XPorAbove Then"
was a late addition, also discussed in the Forum, to compensate for the fact that, in XP, Zip files are Folders.
For some additional clarity, see:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/programmersguide/shell_basics/shell_basics_programming/folder_info.asp
and look at the sample code. Of course that sample is a bit less complex since it starts from a fixed base whereas GetContents allows the caller to specify whether or not to return non-Folders only (all those GOTO SKIPONE statements) or to return all items in the Folder. Unlike the sample, I do take explicit action on Error conditions.
Jim Parsells
|
|
|
|
|
Hi,
your component is awesome. But I'm facing one problem:
when I call ExpandANode("\\computer\share")
it comes through domain (NetworkNeigb\\MSNetwork\\Domain)
then if it searches computers in domain it does'n match any computer
I found that it is difference in PIDL in one byte (8th byte of PIDL after domain - computer PIDL)
20,0,31,88,96,44,141,32,234,58,105,16,162,215,8,0,43,48,48,157,20,0,71,0,2,69,110,116,105,114,101,32,78,101,116,119,111,114,107,0,51,0,70,0,130,77,105,99,114,111,115,111,102,116,32,87,105,110,100,111,119,115,32,78,101,116,119,111,114,107,0,77,105,99,114,111,115,111,102,116,32,78,101,116,119,111,114,107,0,2,0,31,0,65,0,130,70,101,108,105,115,0,77,105,99,114,111,115,111,102,116,32,78,101,116,119,111,114,107,0,2,0,33,0,66,0,130,92,92,
98,
114,99,107,111,0,77,105,99,114,111,115,111,102,116,32,78,101,116,119,111,114,107,0,2,0,40,0,195,1,197,92,92,98,114,99,107,111,92,102,101,108,105,115,0,77,105,99,114,111,115,111,102,116,32,78,101,116,119,111,114,107,0,0,2,0,0,0, - \\computer\share
20,0,31,88,96,44,141,32,234,58,105,16,162,215,8,0,43,48,48,157,0,0, - My Network Places MATCH
20,0,31,88,96,44,141,32,234,58,105,16,162,215,8,0,43,48,48,157,20,0,71,0,2,69,110,116,105,114,101,32,78,101,116,119,111,114,107,0,0,0, - Entire Network MATCH
20,0,31,88,96,44,141,32,234,58,105,16,162,215,8,0,43,48,48,157,20,0,71,0,2,69,110,116,105,114,101,32,78,101,116,119,111,114,107,0,51,0,70,0,130,77,105,99,114,111,115,111,102,116,32,87,105,110,100,111,119,115,32,78,101,116,119,111,114,107,0,77,105,99,114,111,115,111,102,116,32,78,101,116,119,111,114,107,0,2,0,0,0, - Microsoft Windows Network MATCH
20,0,31,88,96,44,141,32,234,58,105,16,162,215,8,0,43,48,48,157,20,0,71,0,2,69,110,116,105,114,101,32,78,101,116,119,111,114,107,0,51,0,70,0,130,77,105,99,114,111,115,111,102,116,32,87,105,110,100,111,119,115,32,78,101,116,119,111,114,107,0,77,105,99,114,111,115,111,102,116,32,78,101,116,119,111,114,107,0,2,0,31,0,65,0,130,70,101,108,105,115,0,77,105,99,114,111,115,111,102,116,32,78,101,116,119,111,114,107,0,2,0,0,0, - domain MATCH
20,0,31,88,96,44,141,32,234,58,105,16,162,215,8,0,43,48,48,157,20,0,71,0,2,69,110,116,105,114,101,32,78,101,116,119,111,114,107,0,51,0,70,0,130,77,105,99,114,111,115,111,102,116,32,87,105,110,100,111,119,115,32,78,101,116,119,111,114,107,0,77,105,99,114,111,115,111,102,116,32,78,101,116,119,111,114,107,0,2,0,31,0,65,0,130,70,101,108,105,115,0,77,105,99,114,111,115,111,102,116,32,78,101,116,119,111,114,107,0,2,0,33,0,66,0,130,92,92,
66,
114,99,107,111,0,77,105,99,114,111,115,111,102,116,32,78,101,116,119,111,114,107,0,2,0,0,0, - computer - DON'T MATCH
I put the difference on extra line
Any help?
if (Day.IsMidnight){GrBo = null; }
|
|
|
|
|
I have seen this before, but only with XP and only with the Control Panel. What OS are you using? The code does assume that a PIDL representing the same object will have the same content. This seems to be a false assumption for some objects on some systems. I am not using the appropriate API calls to manipulate & otherwise examine PIDLs because doing so would limit the control to Win2K and above. At this point, I don't event know whether the API would fix the problem. I will now have to look at that. Since the problem seems to occur only on XP, and the APIs are limited to Win2K and above, there is some hope that the API will do the right thing. If so, I am left to doing some conditional code based on OS. Oh Well.
Jim Parsells
|
|
|
|
|
Win XP English SP2 also Win XP German SP2
if (Day.IsMidnight){GrBo = null; }
|
|
|
|
|
Hi,
Have you tried to make your control work on VB 2005 ?
I did. I had to modify some things because I had several warnings and errors.
Everything is almost Ok but the multithreaded part does not work properly. It says at excution time that a control is being acceded from a thread that did not created it.
As I do not know (not yet at least ) how to program threads, I don't know what to do.
An idea ?
Thanks,
Patrick
|
|
|
|
|
Patrick,
I have not tried the control with VB2005. The actual control does not use threading at all. The DEMO does. As I have stated many times, the Demo is so tailored to be a demo that it does no useful work.
The download Demo uses a separate thread to load the Icons for the ListView. The Article description of this does not, and the AfterNodeSelect code from the article shows the loading of the Icons in a non-threaded fashion. Therefore, you can modify the Demo using the code from the Article to avoid using Threads at all. Of course it runs a bit less responsivly without the threads.
Given that I do not have VS 2005, I cannot duplicate your error. I can only speculate that there might be a change in VB2005 from defaulting to a STA model to a MTA, with additional restrictions imposed because of that. There is not doubt that the Demo's separate Thread is accessing a control not created on that Thread. That is the entire purpose of the separate Thread.
I am interested in hearing more about this, should you, or anyone else, find out more.
Thanks,
Jim Parsells
|
|
|
|
|
Hi Author of Explorer Tree,
I have VB .Net Professionals and I am not able to run the demo project as is.
I am getting more than 5 errors at compile/build.
What could be doing wrong, should I be inlcuding any libraries or DLLs.
I am trying to understand your code to develop a tool to orgainze photos/music across drives.
Any and all help I can get to understand your code will be appreciated.
Thanks & Regards
Ajay Kalidindi
|
|
|
|
|
Ajay,
What errors? The demo project as supplied in the download should work as is with VS2002 and should automatically convert with no errors to VS2003.
It you are getting errors on your own project, then it is probable that you did not add the appropriate reference to the ExpTreeLib dll. Extract the demo project and observe the references. Model your references accordingly.
If none of that applies to your problem, then let me know the details of the errors you are experiencing. Also, what version of Windows are you using? What version of Internet Explorer?
Jim Parsells
|
|
|
|
|
Your control in fantastic. Thanks to share it.
I am a beginner in VB.net and I was wandering if it would be difficult to add a thumbnails view style to the ImageList ???
Thanks again
Patrick
|
|
|
|
|
Note: The article and the associated ExpTreeLibrary does not contain the ListView component, which is where Thumbnail View would be relevant. The ListView is part of the Demo and is very tailored to show the capabilites of the Library, as opposed to doing anything else.
The short answer to your question is: I don't know, but probably not so much difficult as tricky. SystemImageListManager deals with the small and large System image list and assumes that no other element of the application will be modifying either, since it assumes/requires that an Icon will have the same IconIndex in both lists.
Adding Thumbnails clearly hss the potential of directly or indirectly violating this requirement.
There is also only one ListViewItem.Index property. The Demo takes advantage of only having one Index to worry about.
It may be possible to build a .Net ImageList with Thumbnails without causing bad side effects for SystemImageListManager, but I have not tried this and really do not know what the implications are.
If it can be done, then you would need code to change the ListViewItem.Index property as you switch between ThumbnailView and any other view.
If your app will only show Thumbnail View, then you could dispense with using SystemImageListManager for the ListView -- however, I do not guarantee the SystemImageListManager, as used by the TreeView would survive the extra Image processing your would be doing. Try it and see.
Jim Parsells
|
|
|
|
|
Message Closed
modified 24-Dec-20 6:48am.
|
|
|
|
|
The currently available version of the CShItem class has a limited filter capability. An alternate signature version of CShItem.GetFiles accepts a filter string...
Dim xxx as New CShItem(someFileSystemPath)
dim filtList as ArrayList=xxx.GetFiles("*.wav")
will return only CShItems representing .wav files to filtList. Since the underlying code is actually:
Public Function GetFiles(ByVal Filter As String) As ArrayList
If m_IsFolder Then
Dim dummy As New ArrayList()
Dim fileentries() As String
fileentries = Directory.GetFiles(m_Path, Filter)
Dim vFile As String
For Each vFile In fileentries
dummy.Add(New CShItem(vFile))
Next
Return dummy
Else
Return New ArrayList()
End If
End Function
it is obvious that any and all limitations of Directory.GetFiles apply.
Since the Demo DOES NO USEFUL WORK, it is also obvious that it order to do anything useful, one must modify the Demo code as it relates to the ListView.
Applying a Filter to displayed files is really of no interest to the ExpTree
control since the control only deals with Shell FolderItems that are themselves Folders. ExpTree wouldn't know what to do with a file if is saw one. This will change in a version of ExpTree that handles Drag/Drop, but that is not relevant to this question.
If you wished to have an OpenFileDialog replacement that returned a Filtered list of FileNames, it would be easy enough to write one given the ExpTreeLib routines. Calum McLellan's Browser control at: http://www.codeproject.com/article.asp?tag=17995122998338489
Gets you most of the way, with room on the form to add the filter spec.
Calum's control has some very nice visual enhancements and features.
Jim Parsells
|
|
|
|
|
Hi,
The ListView in my article has an Extensions property which will only allow certain extensions - I haven't used the CShItem function described by Jim because I wanted to add a bit of flexibility to how the filter strings are written, it will accept all of the following: jpg .jpg *.jpg test.jpg (test.jpg will return ALL jpg's not just a file called test.jpg). The other reason I didn't use the CShItem function was to avoid multiple calls (one for each file type) I figured that just getting all the files and then filtering them afterwards would be quicker in most cases - Thinking about it now I could be very wrong here... I think there could probably be a few perfomance improvements made to the AddItem sub, any opinions would be welcome.
The Extensions property is a string array and can be set in the designer.
Cheers
Calum
|
|
|
|
|
This is the way Filters should be handled ... just as Calum has done in his control. Gettting all files and then filtering them is not only more efficient, but maintains the proper division of labor between the various components.
Jim Parsells
|
|
|
|
|
Hi
>>>I have just finished the filter for files and I also added two properties where you can choose if you want to show hiddenfolders and hiddenfiles.<<<
When did I say that? I must have been talking about the ListView...
I have added a boolean ShowHiddenFolders property to the ExpTree . This just does a simple check in the BeforeExpand Sub as follows:
<code>If D.Count > 0 Then
D.Sort() 'uses the class comparer
Dim item As CShItem
For Each item In D
If Not (item.IsHidden And Not m_showHiddenFolders) Then 'Added Code
Dim newNode As TreeNode = MakeNode(item)</code>
I agree that the best place for a file filter is on the host form (or ListView). I will look into a more efficient way than what I have used in my ListView...
Cheers
Calum
|
|
|
|
|
[Refers to a message I mis-directed ... since deleted ....JP]
Whoops ... my mistake. I was replying to someone else ... in a message not actually posted on the Forum. Sorrry for the mis-directed message.
Jim Parsells
|
|
|
|
|