Author Topic: FindFiler B2  (Read 5601 times)

0 Members and 1 Guest are viewing this topic.

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
FindFiler B2
« on: December 26, 2008, 04:23:31 AM »
FindFiler
carrying on from here
http://www.theswamp.org/index.php?topic=26486.0

Still has a bit to go .. definitely beta .....
comments and severe criticism appreciated.



Code: [Select]
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace WindowsFormsApplication104
{
    public partial class Form1 : Form
    {
        private string substringFolder;
        private string selectedNodePath;     
        private string rootPath = @"F:\_kdub_testing";

        private string fileFilterText = "*.CSV";

        public Form1()
        {
            InitializeComponent();
            //
            fileFilterComboBox.Items.Add(fileFilterText);
            fileFilterComboBox.Text = fileFilterText;
            //           
            InitialiseTreeview();
        }
        /// <summary>
        ///
        /// </summary>
        public void InitialiseTreeview()
        {
            folderTreeView.Nodes.Clear();
            folderTreeView.Nodes.Add(rootPath);
            //
            PopulateTreeView(rootPath, folderTreeView.Nodes[0]);
            folderTreeView.ExpandAll();
            //
           
        }
        public void PopulateListView(string folderName)
        {


        }


        /// <summary>
        ///
        /// </summary>
        /// <param name="folderName"></param>
        /// <param name="parentNode"></param>
        public void PopulateTreeView(string folderName, TreeNode parentNode)
        {
            if (! Directory.Exists(folderName)) return;

            string[] folderArray = Directory.GetDirectories(folderName);
            try
            {
                if (folderArray.Length != 0)
                {
                    foreach (string folder in folderArray)
                    {
                        substringFolder = folder.Substring(
                                        folder.LastIndexOf('\\') + 1,
                                        folder.Length - folder.LastIndexOf('\\') - 1);

                        TreeNode myNode = new TreeNode(substringFolder);
                        parentNode.Nodes.Add(myNode);

                        PopulateTreeView(folder, myNode);
                    }
                }
            }
            catch (UnauthorizedAccessException)
            {
                parentNode.Nodes.Add("Access denied");
            }
        }
        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void folderTreeView_AfterSelect(object sender, TreeViewEventArgs e)
        {
            selectedNodePath = ((TreeView)sender).SelectedNode.FullPath;
            //  MessageBox.Show(selectedNodePath);
            //  pathTextBox.Clear();
            pathTextBox.Text = selectedNodePath;
            folderTextBox.Text = ((TreeView)sender).SelectedNode.Text;

        }

        private void folderBrowserDialog1_HelpRequest(object sender, EventArgs e)
        {

        }

        private void folderButton_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog browsePathDialog = new FolderBrowserDialog();
            browsePathDialog.Description = "Select a Root folder to search";
            browsePathDialog.ShowDialog();
            rootPath = browsePathDialog.SelectedPath;
            // MessageBox.Show(rootPath);
            InitialiseTreeview();
        }
    }
}

« Last Edit: December 26, 2008, 04:27:10 AM by Kerry Brown »
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

TonyT

  • Guest
Re: FindFiler B2
« Reply #1 on: December 27, 2008, 09:22:01 AM »
FindFiler
carrying on from here
http://www.theswamp.org/index.php?topic=26486.0

Still has a bit to go .. definitely beta .....
comments and severe criticism appreciated.


Nothing wrong with doing it that way, but there's also other
approaches that can make some other related things easier
(like for example, showing a context menu for each node with
content specific to the associated folder), and 'lazy-loading'
tree nodes (which is quite necessary).

See attached project

« Last Edit: December 27, 2008, 12:00:35 PM by TonyT »

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: FindFiler B2
« Reply #2 on: December 27, 2008, 07:23:03 PM »
Thanks Tony,
yes, I can see the advantages of 'lazy-loading'.

For this particular situation a full exposure of branches is required. The tree is relatively small and the branching is simple.
The preference here is for the category folder to be visible,  obviating clicking down to the nested location. I think in this case the users will learn the category options by osmosis, rather than rely on 'a hunt and peck' of a hidden structure.

For future endeavors where the requirements are for a more 'explorer' based structure, I would definitely consider selective loading.
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

TonyT

  • Guest
Re: FindFiler B2
« Reply #3 on: December 28, 2008, 04:04:55 AM »
Thanks Tony,
yes, I can see the advantages of 'lazy-loading'.

For this particular situation a full exposure of branches is required. The tree is relatively small and the branching is simple.
The preference here is for the category folder to be visible,  obviating clicking down to the nested location. I think in this case the users will learn the category options by osmosis, rather than rely on 'a hunt and peck' of a hidden structure.

For future endeavors where the requirements are for a more 'explorer' based structure, I would definitely consider selective loading.

Oh, sorry. I saw a button on your piccy for picking the
root folder and assumed it was a more general purpose
thing.

Unfortunately, the lazy-loading business in that example
also obsfucates the more basic concept I was trying
to get across (using a custom TreeNode as a 'worker').

For example, given only a standard TreeView control,
just add one of these to its Nodes collection, and yer
done:

Code: [Select]

   public class FolderTreeNode : TreeNode
   {
      private string folder = null;

      public FolderTreeNode( string path ) : base( GetNodeName( path ) )
      {
         if( string.IsNullOrEmpty( path ) )
            throw new ArgumentException( path );
         if( !Directory.Exists( path ) )
            throw new DirectoryNotFoundException( path );
         this.folder = path;
         try
         {
            foreach( string subfolder in Directory.GetDirectories( this.folder ) )
               this.Nodes.Add( new FolderTreeNode( subfolder ) );
         }
         catch( System.Exception ex )
         {
            this.Text += string.Format( " (error: {0})", ex.Message );
         }
      }

      public string Folder
      {
         get
         {
            return folder;
         }
      }

      private static string GetNodeName( string path )
      {
         string name = Path.GetFileName( path );
         return string.IsNullOrEmpty( name ) ? path : name;
      }
   }



« Last Edit: December 28, 2008, 04:28:48 AM by TonyT »

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: FindFiler B2
« Reply #4 on: December 28, 2008, 04:24:59 AM »
Oh, sorry. I saw a button on your piccy for picking the
root folder and from that assumed it was a more general
purpose tool.

actually the Rootfolder button was just so I could get the code straight in my head for the FolderBrowserDialog .. but I can understand your deduction


Even discounting the lazy-loading business, a custom
TreeNode based approach is IMO, a cleaner and simpler
way to get the job done because it becomes simply a
matter of having each custom TreeNode create/add its
child nodes from its constructor.


I'll do some investigation in that regard, thanks - the input is appreciated.
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

TonyT

  • Guest
Re: FindFiler B2
« Reply #5 on: December 28, 2008, 04:31:39 AM »
No problem, See my edit for a simpler example.

Oh, sorry. I saw a button on your piccy for picking the
root folder and from that assumed it was a more general
purpose tool.

actually the Rootfolder button was just so I could get the code straight in my head for the FolderBrowserDialog .. but I can understand your deduction


Even discounting the lazy-loading business, a custom
TreeNode based approach is IMO, a cleaner and simpler
way to get the job done because it becomes simply a
matter of having each custom TreeNode create/add its
child nodes from its constructor.


I'll do some investigation in that regard, thanks - the input is appreciated.


Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: FindFiler B2
« Reply #6 on: December 28, 2008, 09:02:05 AM »
No problem, See my edit for a simpler example.
......

Thanks Tony , that looks pretty simple to implement (on the face of it).

much appreciated.

kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

Ken Alexander

  • Newt
  • Posts: 61
Re: FindFiler B2
« Reply #7 on: December 29, 2008, 02:50:37 PM »
You could also subclass the treeview control itself and give it its own private node classes (as Tony suggested).  For brevity I took Tony's code, removed exception handling a little modifying and added his class as private to mine.  For fun, I added icon support.  I wrote this in VB and converted to C#.

Code: [Select]

public class FolderTree : TreeView
{
   
    private ImageList _MyImageList;
   
    public FolderTree()
    {
        _MyImageList = new ImageList();
        //Set default icons
        _MyImageList.Images.Add("Default", SystemIcons.Shield);
        _MyImageList.Images.Add("OpenedFolder", SystemIcons.Exclamation);
        _MyImageList.Images.Add("ClosedFolder", SystemIcons.Question);
        this.ImageList = _MyImageList;
    }
   
    public void SetRootPath(DirectoryInfo Directory)
    {
        if (Directory.Exists) {
            FolderTreeNode Node = new FolderTreeNode(Directory.FullName, this);
            this.Nodes.Add(Node);
        }
    }
   
    public void SetRootPath(string Directory)
    {
        DirectoryInfo Dir = new DirectoryInfo(Directory);
        if (Dir.Exists) {
            FolderTreeNode Node = new FolderTreeNode(Dir.FullName, this);
            this.Nodes.Add(Node);
        }
    }
   
    public ImageList MyImageList {
        get { return _MyImageList; }
        set { _MyImageList = value; }
    }
   
    public string GetImageKey(string filename)
    {
        FileInfo File = new FileInfo(filename);
        string Key = "Default";
        if (!(File.Attributes == FileAttributes.Directory)) {
            if (!_MyImageList.Images.ContainsKey(File.Extension)) {
                Icon FileIcon = Icon.ExtractAssociatedIcon(filename);
                if (FileIcon != null) {
                    this.MyImageList.Images.Add(File.Extension, FileIcon);
                    Key = File.Extension;
                }
            }
            else {
                Key = File.Extension;
            }
        }
        else {
            Key = "ClosedFolder";
        }
        return Key;
    }
   
    protected override void OnAfterCollapse(System.Windows.Forms.TreeViewEventArgs e)
    {
        base.OnAfterCollapse(e);
       
        if (e.Node is FolderTreeNode) {
            e.Node.ImageKey = "ClosedFolder";
            e.Node.SelectedImageKey = e.Node.ImageKey;
        }
    }
   
    protected override void OnBeforeExpand(System.Windows.Forms.TreeViewCancelEventArgs e)
    {
        base.OnBeforeExpand(e);
       
        if (e.Node is FolderTreeNode) {
            e.Node.ImageKey = "OpenedFolder";
            e.Node.SelectedImageKey = e.Node.ImageKey;
        }
    }
   
    private class FileTreeNode : TreeNode
    {
       
        private string _FileName = string.Empty;
        private FolderTree _MyFolderTree;
       
        public string FileName {
            get { return _FileName; }
        }
       
        public FolderTree MyFolderTree {
            get { return _MyFolderTree; }
        }
       
        public FileTreeNode(string path, TreeView MyFolderTree) : base(GetNodeName(path))
        {
            _MyFolderTree = MyFolderTree;
            this._FileName = path;
            this.ImageKey = this._MyFolderTree.GetImageKey(this._FileName);
            this.SelectedImageKey = this.ImageKey;
        }
       
        private static string GetNodeName(string path__1)
        {
            string name = Path.GetFileName(path__1);
            return string.IsNullOrEmpty(name) ? path__1 : name;
        }
    }
   
    private class FolderTreeNode : TreeNode
    {
        private string m_folder = null;
        private FolderTree _MyFolderTree;
       
        public FolderTreeNode(string path, TreeView MyFolderTree) : base(GetNodeName(path))
        {
            _MyFolderTree = MyFolderTree;
           
            this.m_folder = path;
            this.ImageKey = "ClosedFolder";
            this.SelectedImageKey = this.ImageKey;
            GetFiles(this.m_folder, this);
            FolderTreeNode TheNode = null;
            foreach (string subfolder in Directory.GetDirectories(this.m_folder)) {
                TheNode = new FolderTreeNode(subfolder, this.MyFolderTree);
                this.Nodes.Add(TheNode);
                GetFiles(subfolder, TheNode);
            }
        }
       
        private void GetFiles(string FolderPath, ref FolderTreeNode ParentNode)
        {
            foreach (string FileName in Directory.GetFiles(FolderPath)) {
                ParentNode.Nodes.Add(new FileTreeNode(FileName, this.MyFolderTree));
            }
        }
       
        public string Folder {
            get { return m_folder; }
        }
       
        public FolderTree MyFolderTree {
            get { return _MyFolderTree; }
        }
       
        private static string GetNodeName(string path__1)
        {
            string name = Path.GetFileName(path__1);
            return string.IsNullOrEmpty(name) ? path__1 : name;
        }
    }
}

Ken Alexander

TonyT

  • Guest
Re: FindFiler B2
« Reply #8 on: December 30, 2008, 06:43:49 AM »
You could also subclass the treeview control itself and give it its own private node classes (as Tony suggested).  For brevity I took Tony's code, removed exception handling a little modifying and added his class as private to mine.  For fun, I added icon support.  I wrote this in VB and converted to C#.


You might want to take a look at the sample project I attached
to an earlier post.

First, the exception handling isn't optional, it's necessary if
you don't want your app to crash. Second, if your intent is
to use the code you posted as a general purpose explorer-
style treeview, it won't work very well without lazy-loading
(see the sample project I posted for that).

If you don't mind my offering some tips, your code takes a few
unnecessary detours. For example, you are caching the owner
TreeView in a private field in the TreeNode class, and passing it
into the constructor.

The TreeNode class already has a TreeView property that returns
the owning treeview. If it's a derived type, you just cast it to that.

From within the custom TreeNode class, you can do this to get
the owner TreeView as a derived type:

Code: [Select]

   FolderTreeView myFolderTreeView = this.TreeView as FolderTreeView;

   //  If the node hasn't been added to a TreeView yet, then
   //  myFolderTreeView will be null, so you have to test it:

   if( myFolderTreeView != null )
   {
       // then use it
   }


Also, this is an error:

Code: [Select]

     ( ! (File.Attributes == FileAttributes.Directory ) )


FileAttributes is a set of flags, that means the .Directory flag can
be set along with other flags, and hence, you must mask out
the other flags to test for its presence, like this:

Code: [Select]

   if( ( File.Attributes & FileAttributes.Directory ) != FileAttributes.Directory )
   {
       // this is not a directory
   }


While this may be the fault of the translator you used, in general,
you don't do use !( a == b ) to determine if two objects are not
equal, like this:

Code: [Select]

  if(  ! (a == b ) )
     Console.WriteLine( "a is not equal to b");


Instead, you just use the != operator:

Code: [Select]

   if( a != b )
     Console.WriteLine( "a is not equal to b");


If that code was generated by a translator, you might want to
look for another translator.  :lol:
« Last Edit: December 30, 2008, 07:01:45 AM by TonyT »

Ken Alexander

  • Newt
  • Posts: 61
Re: FindFiler B2
« Reply #9 on: December 30, 2008, 01:07:07 PM »
Hi Tony,
Thanks for the tips.  The intent wasn’t to say that exception handling was optional nor was it to say that lazy-loading wasn’t necessary.  The intent was to make the “Custom TreeNodes” private to the “Custom TreeView.”  Wrap everything up in a single self contained Class.  If the TreeNodes become customized to the point that they are useless to another TreeView then there is no need to expose them.  Then to show a technique for working with the icons.  I used the custom TreeView as a private field because in the Constructor the Node does not yet belong to a TreeView, which is a ReadOnly Property.  It probably was not a good idea to have the “Initializing” of the Node in the Constructor.
I’ve never translated to C# before, it has always been from C# to VB.  Here is a link to the translator that I used:
http://www.developerfusion.com/tools/convert/csharp-to-vb/
Ken Alexander

TonyT

  • Guest
Re: FindFiler B2
« Reply #10 on: December 30, 2008, 08:43:49 PM »
The intent was to make the “Custom TreeNodes” private to the “Custom TreeView.”  Wrap everything up in a single self contained Class. If the TreeNodes become customized to the point that they are useless to another TreeView then there is no need to expose them. 


Ummmm... sorry, but I think you missed the point to making the
FolderTreeNode class public.

The custom FolderTreeNode I show has a public Folder property,
and its purpose is to allow any consumer to read it to get the full path
of the folder associated with that node. 

Unfortunately, you can't use that property from the outside, if
you make the class private. With the public FolderTreeNode class,
getting the path to the selected folder requires only this:

Code: [Select]

   string path =  ((FolderTreeNode) myTreeView.SelectedNode).Folder;


Quote


Then to show a technique for working with the icons.  I used the custom TreeView as a private field because in the Constructor the Node does not yet belong to a TreeView, which is a ReadOnly Property.  It probably was not a good idea to have the “Initializing” of the Node in the Constructor.


Actually, it's not a good idea to have any dependence on a
treeview from the constructor of a treenode.  In this case,
the icons for folders are static. The imagelist that stores
them should not be in the TreeView to start with, and it
definitely shouldn't be a non-static instance member.

It should be a static member of a seperate class that
has no dependence on either the FolderTreeNode or a custom
treeview. A TreeView that uses them only needs to have its
stateimages property set to the imagelist. The TreeNode class
should only have to ask the static class that marshals the
imagelist for the key or index for a given file extension, and the
the class that stores the imagelist can do the work of getting
the image and caching it for subsequent use.

As written, your code does a lot of unnecessarily redundant work
by getting the images every time an instance of the treeview
is created. The images don't change, so they should be cached
in a static member of another class, allowing them to be loaded
only once, and then re-used for any purpose, not limited to just
treeviews that display folders structures.


Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: FindFiler B2
« Reply #11 on: December 30, 2008, 09:14:26 PM »
.....  and then re-used for any purpose, not limited to just
treeviews that display folders structures.

and

PostReply #3
Quote
Unfortunately, the lazy-loading business in that example
also obsfucates the more basic concept I was trying
to get across (using a custom TreeNode as a 'worker').

I think these are the 2 points ( actually the same point) that I'll take away from this discussion.
I initially had my brain wrapped up in just displaying the folder names ...



kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

Ken Alexander

  • Newt
  • Posts: 61
Re: FindFiler B2
« Reply #12 on: January 05, 2009, 12:30:48 PM »
Hi Tony,
I do understand your decision to make the class Public.  I just don’t agree with it.  If the class is Public, it better “work” for any subscriber.  Add your TreeNode to the base TreeView, and it doesn’t “work.”  It seems to me that if the TreeNode is dependent on a custom TreeView, then that node should be contained within the custom TreeView.  The TreeView should be responsible for providing information to its client.  Otherwise it’s kind of like the horse before the cart isn’t it?  Otherwise, yes, I guess I am missing the point.

While the default Icon and the two folder Icons are static, the File icons are not.  Each time a folder is expanded, if there is a new file icon, it is added to the ImageList.  I could have used a separate class to handle the images I didn’t see the need in this particular case.
Ken Alexander

TonyT

  • Guest
Re: FindFiler B2
« Reply #13 on: January 07, 2009, 01:47:22 PM »

Hi Tony,
I do understand your decision to make the class Public.  I just don’t agree with it. 
If the class is Public, it better “work” for any subscriber. 


Sorry, I'm not sure what gave you that idea.

The fact that two classes are designed to work with each other,
or that one class is designed to work only in conjunction with
another class, does not preclude either from being public.

For example: A BlockTableRecord is a public class.

Does that mean that because you can't add an instance of
a BlockTableRecord to a LayerTable (or any SymbolTable),
that the BlockTableRecord class should not be public?

If the designer wants to restrict the types that a class
can be used with, they're free to do that, and of course,
must ensure that a consumer understands that, and/or
enforces that restriction through code.

I could for example, make the constructor of a class
derived from a TreeNode private or internal, but leave
the TreeNode-based class itself public, which prevents
anyone but me from creating an instance of it and
adding it to any TreeView.

But at the same time, your assertion is undermined by
the simple fact that anyone can remove any TreeNode
from any parent node, and add it to any other TreeNode
in the same or a different TreeView, including ones it was
not designed to work with, and making your TreeNode
class private doesn't prevent them from doing it, because
they can still manipulate the TreeNodes through the public
TreeNode base type's Nodes collection, and the owning
TreeView's Nodes collection.

So there really is nothing served by making the TreeNode
class private, because they are not private objects that
are unavailable to consumers, in the first place.

In order to achieve what making your TreeNode class private
only pretends to achieve, you would have to implement your
own private TreeView class with a private TreeNodeCollection
so that consumers cannot access the nodes at all, and you
would have to implement all of the functionality of that, and
in doing that, you would also prevent any consumer from
extending the functionality by deriving new classes from your
'private' TreeView and TreeNode classes.

So, what your assertion amounts to is that you don't believe
that any consumer should be able to extend or enhance the
functionality of a class, by deriving new classes from it, and
that is a common misconception of people coming from legacy
VB, where there is no concept of consuming and extending
existing classes by deriving new classes from them, and who
generally do not fully understand that very basic concept.

Quote

Add your TreeNode to the base TreeView, and it doesn’t “work.” 


Yes, That's correct. It doesn't work because it wasn't
designed to work with any TreeView. And that's because
in order for Lazy-loading to work, the tree node depends
on receiving a notification from the owning treeview (that
the node is expanding).  There's no other way to do that
(short of a major rewrite of the TreeNode class) and that's
the only reason my 'lazy' FolderTreeNode is dependent on
a specific TreeView-based class.

Quote

It seems to me that if the TreeNode is dependent on a custom
TreeView, then that node should be contained within the custom
TreeView.  The TreeView should be responsible for providing
information to its client.


A consumer can query the node for its folder, and can also
easily query its parent nodes for their folders, without having
to parse filenames. That's the point to making the class public
and making the Folder property public - to make things easy
for the consumer, rather than create illegitmate dependencies.

Another reason why the FolderTreeNode is public, is because
consumers are supposed to be able to specialize it further by
deriving new types from it. The derived types can override the
virtual methods of their base types, and change the behavior
to suit the consumer's needs. For example, one might want
to create a specialization of a FolderTreeNode that adds child
nodes that do not represent other folders, but instead can
represent any kind of object (like files for example, or nodes
that display different filtered views of files in another control).

Doing any of these things is considered routine in OOP, and
that's why we don't 'privatize' things when doing that serves
no useful purpose, and artificially limits how a consumer can
use the classes we provide.

Quote

While the default Icon and the two folder Icons are static,
the File icons are not.


Yes, but that doesn't preclude using statically-cached folder
icons for folders. The point was that you are redundantly
fetching the same icon many times, which is pointless in the
case of a folder icon.

« Last Edit: January 08, 2009, 01:22:04 PM by TonyT »