Click here to Skip to main content
15,897,891 members
Articles / Desktop Programming / WPF

A Toy Tabbed File Explorer for Prototype and MVVM Exercise

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
13 Jun 2012CPOL23 min read 29.3K   1.4K   16  
This article describes a Tabbed File Explorer with minimal functionality using only basic MVVM techniques and some attached properties. In a first article I described a MVVM Tabbed Navigation Tree, in this article I add a Tabbed Folderplane.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Utils;
using System.Windows.Input;
using WpfApplication1.Model;
using MVVM;
using System.Collections.ObjectModel;
using System.Collections;
using System.IO;

namespace WpfApplication1.ViewModel
{
    // All commands here, except 

    // Note: Not very "DRY". Use MVVM framework to bind to functions by convention
    // Note: Some System.IO used here, could be moved to model
    // Note: Using cosale operator "??" saves some characters, but formatting not standard/ a little awkward

    public partial class MainVm
    {
        private int GetIndexFolderPlanes(string path)
        {
            int indexInPlanes = -1;
            for (int i = 0; i <= FolderPlanes.Count - 1; i++)
            {
                if (FolderPlanes[i].FullPathName == path) { indexInPlanes = i; }
            }
            return indexInPlanes;
        }

        // For now SelectedPath common to all trees
        // to do? set OnClickCommand as attribute to NavTreeView and TabbedNavTrees
        RelayCommand selectedPathFromTreeCommand;
        public ICommand SelectedPathFromTreeCommand
        {

            get
            {
                return selectedPathFromTreeCommand ??
                       (selectedPathFromTreeCommand =
                              new RelayCommand(x =>  SelectedPath = (x as string) ));
            }
        }

        RelayCommand folderPlaneItemDoubleClickCommand;
        public ICommand FolderPlaneItemDoubleClickCommand
        {
            get
            {
                return folderPlaneItemDoubleClickCommand ??
                       (folderPlaneItemDoubleClickCommand =
                              new RelayCommand(x => OnFolderDownClick(x), x => true));
            }
        }

        // In case of doubleclick on FolderItem we choose/want to change current selected folder
        // We do not know the source of the Set/change event in SelectedPath
        // For now we use a boolean for this, however I am not sure if this completely (thread???) safe
        private bool UseCurrentPlane = false;

        public void OnFolderDownClick(object p)
        {
            if (p == null) return;
            string path = (p as FolderPlaneItem).FullPathName;

            bool isDrive = FolderPlaneUtils.IsDrive(path);
            bool isFolder = FolderPlaneUtils.IsFolder(path);
            if (isDrive || isFolder)
            {
                UseCurrentPlane = true;
                try
                {
                    SelectedPath = path;
                }
                finally { UseCurrentPlane = false; }
            }
            else
            {
                // Execute
                try
                {
                    // Console.WriteLine("Execute: "+ path);
                    System.Diagnostics.Process.Start(path);
                }
                catch
                { }
            }
        }

        RelayCommand closeTabCommand;
        public ICommand CloseTabCommand
        {
            get { return closeTabCommand ?? (closeTabCommand = new RelayCommand(CloseTab, x => FolderPlanes.Count > 0)); }
        }

        //public int teller = 0;
        public void CloseTab(object p)
        {
            //teller++;
            //Console.WriteLine("*********** OnCloseCommand called ************" + teller.ToString());

            if (SelectedFolderPlane != null)
            {
                int i = GetIndexFolderPlanes(SelectedFolderPlane.FullPathName);

                if (i != -1)
                {
                    FolderPlanes.RemoveAt(i);
                    if (FolderPlanes.Count != 0)
                    {
                        i = i - 1;
                        if (i < 0) i = i + 1;
                        SelectedPath = FolderPlanes[i].FullPathName;
                    }
                    else
                    {
                        SelectedFolderPlane = null;
                        SelectedPath = "";  
                    }
                }
            }
        }

        RelayCommand folderUpCommand;
        public ICommand FolderUpCommand
        {
            get { return folderUpCommand ?? (folderUpCommand = new RelayCommand(FolderUp, x => FolderPlanes.Count > 0)); }
        }

        public void FolderUp(object p)
        {
            if (SelectedFolderPlane != null)
            {
                string path = FolderPlaneUtils.FolderUp(SelectedFolderPlane.FullPathName);
                UseCurrentPlane = true;
                try
                {
                    SelectedPath = path; 
                }
                finally { UseCurrentPlane = false; }

            }
        }

        // Note: we now use drag and drop for ordering Folders and SavedTabs

        //RelayCommand tabToLeftCommand;
        //public ICommand TabToLeftCommand
        //{
        //    get { return tabToLeftCommand ?? (tabToLeftCommand = new RelayCommand(TabToLeft)); }
        //}

        //public void TabToLeft(object p)
        //{
        //    int index = GetIndexFolderPlanes(selectedPath);
        //    if (index > 0)
        //    {
        //        FolderPlanes.Move(index, index - 1);
        //    }
        //}

        //RelayCommand tabToRightCommand;
        //public ICommand TabToRightCommand
        //{
        //    get { return tabToRightCommand ?? (tabToRightCommand = new RelayCommand(TabToRight)); }
        //}

        //public void TabToRight(object p)
        //{
        //    int index = GetIndexFolderPlanes(selectedPath);
        //    if ((index != -1) && (index < FolderPlanes.Count - 1))
        //    {
        //        FolderPlanes.Move(index, index + 1);
        //    }
        //}

        RelayCommand toggleOpenPopup1Command;
        public ICommand ToggleOpenPopup1Command
        {
            get { return toggleOpenPopup1Command ?? (toggleOpenPopup1Command = new RelayCommand(x => Popup1IsOpen = !Popup1IsOpen)); }
        }

        RelayCommand toggleOpenPopup2Command;
        public ICommand ToggleOpenPopup2Command
        {
            get { return toggleOpenPopup2Command ?? (toggleOpenPopup2Command = new RelayCommand(x => Popup2IsOpen = !Popup2IsOpen)); }
        }

        RelayCommand toggleOpenPopup3Command;
        public ICommand ToggleOpenPopup3Command
        {
            get
            {
                return toggleOpenPopup3Command ??
                      (toggleOpenPopup3Command = new RelayCommand
                             (x => Popup3IsOpen = (!Popup3IsOpen && (SelectedFolderItems.Count == 1)),
                                                     x => SelectedFolderItem != ""));
            }
        }

        RelayCommand addSavedTabsCommand;
        public ICommand AddSavedTabsCommand
        {
            get { return addSavedTabsCommand ?? (addSavedTabsCommand = new RelayCommand(AddSavedTabs)); }
        }

        public void AddSavedTabs(object p)
        {
            string name = (p as String);

            var saveTheseTabs = new SavedFolderTabsItem() { };
            saveTheseTabs.FriendlyName = name;
            saveTheseTabs.TabFullPathName = new System.Collections.ObjectModel.Collection<string>() { };
            for (int i = 0; i <= FolderPlanes.Count - 1; i++)
            {
                saveTheseTabs.TabFullPathName.Add(FolderPlanes[i].FullPathName);
            }


            SavedFolderTabs.Add(saveTheseTabs);
            // to do: .Add does not set and save ???
            SavedFolderTabsUtils.Save(savedFolderTabs);
            Popup2IsOpen = !Popup2IsOpen;
        }

        RelayCommand deleteSavedTabsCommand;
        public ICommand DeleteSavedTabsCommand
        {
            get { return deleteSavedTabsCommand ?? (deleteSavedTabsCommand = new RelayCommand(DeleteSavedTabs)); }
        }

        public void DeleteSavedTabs(object p)
        {
            int index = (int)p;
            if (index != -1) { SavedFolderTabs.RemoveAt(index); }
            SavedFolderTabsUtils.Save(savedFolderTabs);
            Popup1IsOpen = !Popup1IsOpen;
        }

        RelayCommand closeAllFolderTabsCommand;
        public ICommand CloseAllFolderTabsCommand
        {
            get
            {
                return closeAllFolderTabsCommand ??
                       (
                        closeAllFolderTabsCommand = new RelayCommand(x =>
                          {
                              SelectedPath = "";
                              SelectedFolderPlane = null;
                              FolderPlanes.Clear();
                              SelectedIndexSavedFolderTabs = -1;
                          })
                       );
            }
        }

        RelayCommand selectedItemsChangedCommand;
        public ICommand SelectedItemsChangedCommand
        {
            get { return selectedItemsChangedCommand ?? (selectedItemsChangedCommand = new RelayCommand(SelectedItemsChanged, x => true)); }
        }

        public void SelectedItemsChanged(object p)
        {
            SelectedFolderItems.Clear();

            IList selectedRecords = p as IList;
            foreach (FolderPlaneItem item in selectedRecords)
            {
                SelectedFolderItems.Add(item.FullPathName);
            }

            if (SelectedFolderItems.Count == 1) { SelectedFolderItem = Path.GetFileName(SelectedFolderItems[0]); } else { SelectedFolderItem = ""; }
        }

        RelayCommand snapShotSelectedCommand;
        public ICommand SnapShotSelectedCommand
        {
            get
            {
                return snapShotSelectedCommand ??
                       (snapShotSelectedCommand = new RelayCommand(SnapShotSelected, x => SelectedFolderItems.Count != 0));
            }
        }

        public void SnapShotSelected(object p)
        {
            SnappedSelectedItems.Clear();
            foreach (string item in SelectedFolderItems)
            {
                SnappedSelectedItems.Add(item);
            }
        }

        RelayCommand copySnapShotCommand;
        public ICommand CopySnapShotCommand
        {
            get
            {
                return copySnapShotCommand ??
                       (copySnapShotCommand = new RelayCommand(CopySnapShot, x => SnappedSelectedItems.Count != 0));
            }

        }
        public void CopySnapShot(object p) { CopyMoveSnapShot(p); }

        RelayCommand moveSnapShotCommand;
        public ICommand MoveSnapShotCommand
        {
            get
            {
                return moveSnapShotCommand ??
                       (moveSnapShotCommand = new RelayCommand(MoveSnapShot, x => SnappedSelectedItems.Count != 0));
            }

        }
        public void MoveSnapShot(object p) { CopyMoveSnapShot(p, false); }
        
        RelayCommand copySnapShotAddDateCommand;
        public ICommand CopySnapShotAddDateCommand
        {
            get
            {
                return copySnapShotAddDateCommand ??
                       (copySnapShotAddDateCommand = new RelayCommand(CopySnapShotAddDate, x => SnappedSelectedItems.Count == 1));
            }

        }
        public void CopySnapShotAddDate(object p) { CopyMoveSnapShot("Date"); }

        // http://stackoverflow.com/questions/627504/what-is-the-best-way-to-recursively-copy-contents-in-c
        // Note filemanagement not ready. No feedback. UI hangs on large files, must in other process (Backgroundworker?)
        // to do: to model
        public void CopyDirAndChildren(DirectoryInfo source, DirectoryInfo target)
        {
            // Saw once error during heavy copying, not reproducable, new folder/copy to .exe folder 
            if ((target.Parent == null) || (target.Parent.FullName == "")) return;
            
            
            // Deny action if target is a (indirect) child of target, recursive loops
            // Test target startswith sourcefullname
            // can be longer folder/file name, so extra test on common parent
            if ((target.FullName.StartsWith(source.FullName)) && (target.Parent.FullName != source.Parent.FullName)) return;

            try
            {
                //check if the target directory exists
                if (Directory.Exists(target.FullName) == false)
                {
                    Directory.CreateDirectory(target.FullName);
                }

                //copy all the files into the new directory

                foreach (FileInfo fi in source.GetFiles())
                {
                    fi.CopyTo(Path.Combine(target.ToString(), fi.Name), true);
                }


                //copy all the sub directories using recursion

                foreach (DirectoryInfo diSourceDir in source.GetDirectories())
                {
                    DirectoryInfo nextTargetDir = target.CreateSubdirectory(diSourceDir.Name);
                    CopyDirAndChildren(diSourceDir, nextTargetDir);
                }
                //success here
            }
            catch //(IOException ie) 
            {
                //handle it here
            }

        }

        public void CopyMoveSnapShot(object p, bool CopyNotMove = true)
        {
            // Ctrl+d pressed and 1 item in snapshot
            // to do: make a extra command for it, work with SelectedFolderItems (items=1)
            string extra = "";
            if ((p as string) == "Date")
            {
                if (SnappedSelectedItems.Count != 1) return;

                DateTime time = DateTime.Now;
                string format = " yyyy-M-d HH.mm -)";
                extra = time.ToString(format);
            }

            // Saw once error during heavy copying, not reproducable, new folder/copy to .exe folder 
            if ((SelectedFolderPlane == null) || (SelectedFolderPlane.FullPathName == "") || (SelectedPath == "") ) return;


            // Copy SelectedFolderItems to SelectedFolderPlane.FullName
            string targetPath = SelectedFolderPlane.FullPathName;
            if (!System.IO.Directory.Exists(targetPath)) return;

            string sourcePath = "";
            string fileName = "";

            foreach (string fullPathName in SnappedSelectedItems)
            {
                sourcePath = Path.GetDirectoryName(fullPathName);
                fileName = Path.GetFileName(fullPathName);

                if (System.IO.Directory.Exists(fullPathName))
                {
                    // construct targetItem, if exists try -copy ()
                    string targetItem = Path.Combine(targetPath, fileName + extra);

                    int i = 0;
                    string targetItemCopy = targetItem + " - copy(";
                    while (System.IO.Directory.Exists(targetItem) && (i < 100))
                    {
                        targetItem = targetItemCopy + i.ToString() + ")";
                        i++;
                    }

                    if (CopyNotMove)
                    {
                        CopyDirAndChildren(new DirectoryInfo(fullPathName), new DirectoryInfo(targetItem));
                    }
                    else
                    {
                        Directory.Move(fullPathName, targetItem);
                    }
                }
                else if (System.IO.File.Exists(fullPathName))
                {
                    // construct targetItem, if exists try -copy ()
                    string targetItem = Path.Combine(targetPath, fileName);

                    int i = 0;
                    string targetItemCopy = Path.Combine(targetPath, Path.GetFileNameWithoutExtension(fileName)) + " - copy(";
                    string targetExt = Path.GetExtension(fileName);

                    while (System.IO.File.Exists(targetItem) && (i < 100))
                    {
                        targetItem = targetItemCopy + i.ToString() + ")" + targetExt;
                        i++;
                    }

                    // error can happen
                    File.Copy(fullPathName, targetItem, true);
                    if (!CopyNotMove) File.Delete(fullPathName);
                }
            }

            if (!CopyNotMove) SnappedSelectedItems.Clear();
            RefreshFolderPlanesAndSelectedNavTree(null);
        }

        RelayCommand newFolderCommand;
        public ICommand NewFolderCommand
        {
            // test CanExecute not correct hack, to do?
            get { return newFolderCommand ?? (newFolderCommand = new RelayCommand(NewFolder, x => selectedFolderPlane.FullPathName != null)); }
        }

        public void NewFolder(object p)
        {
            // Saw once error during heavy copying, not yet reproducable, new folder/copy to .exe folder 
            if ((SelectedFolderPlane == null) || (SelectedFolderPlane.FullPathName=="")  || (SelectedPath=="")) return;
            
            string targetPath = SelectedFolderPlane.FullPathName;
            if (!System.IO.Directory.Exists(targetPath)) return;

            string newPath = System.IO.Path.Combine(targetPath, "NewFolder");

            int i = 0;
            while ((System.IO.Directory.Exists(newPath)) && (i < 100))
            {
                i++;
                newPath = System.IO.Path.Combine(targetPath, "NewFolder(" + i.ToString() + ")");
            }
            System.IO.Directory.CreateDirectory(newPath);
            RefreshFolderPlanesAndSelectedNavTree(null);
        }

        RelayCommand renameCommand;
        public ICommand RenameCommand
        {
            get { return renameCommand ?? (renameCommand = new RelayCommand(x => Rename(x))); }
        }

        public void Rename(object p)
        {
            // Rename single selected folder or file
            if (SelectedFolderItems.Count != 1)
            {
                Popup3IsOpen = false;
                return;
            }

            string newname = Path.Combine(selectedFolderPlane.FullPathName, (p as String));
            string oldname = (SelectedFolderItems[0]);

            if (File.Exists(oldname) && !File.Exists(newname))
            {
                File.Copy(oldname, newname);
                File.Delete(oldname);
            }

            if (Directory.Exists(oldname) && !Directory.Exists(newname))
            {
                Directory.Move(oldname, newname);
            }

            RefreshFolderPlanesAndSelectedNavTree(null);
            Popup3IsOpen = false;
        }

        // Deletion from SelectedFolderItems. Alternative choice: use snapshot and delete SnappedSelectedItems //
        RelayCommand deleteSelectedCommand;
        public ICommand DeleteSelectedCommand
        {
            get
            {
                return deleteSelectedCommand ??
                       (deleteSelectedCommand = new RelayCommand(x => DeleteSelected(x), x => SelectedFolderItems.Count != 0));
            }

        }

        public void DeleteSelected(object p)
        {
            if (selectedFolderItems == null) return;
            if (selectedFolderItems.Count == 0) return;
            string str;

            // to do: test if folder can be deleted, send notification if not
            for (int i = 0; i <= SelectedFolderItems.Count - 1; i++)
            {
                str = SelectedFolderItems[i];
                try
                {
                    if (System.IO.Directory.Exists(str))
                    {
                        Directory.Delete(str, true);
                    }
                    else if (System.IO.File.Exists(str))
                    {
                        File.Delete(str);
                    }
                }
                catch
                {
                    // if no permissions we fail and continue
                    Console.WriteLine(" Failed to delete: " + str);
                }
            }
            // to do: refresh all FolderPlanes
            RefreshFolderPlanesAndSelectedNavTree(null);
        }

        RelayCommand refreshFolderPlanesCommand;
        public ICommand RefreshFolderPlanesCommand
        {
            get { return refreshFolderPlanesCommand ?? (refreshFolderPlanesCommand = new RelayCommand(RefreshFolderPlanesAndSelectedNavTree)); }
        }

        public void RefreshFolderPlanesAndSelectedNavTree(object p)
        {
            SelectedFolderPlane.RefreshFolderPlane();


            // In RefreshFolderPlane Directory.Exists(FullPathName) is checked. If false FullPathName set to ""
            // When a SelectedFolderPlane is removed, another existing SelectedFolderPlane is chosen
            for (int i = FolderPlanes.Count - 1; i >= 0; i--)
            {
                folderPlanes[i].RefreshFolderPlane();
                if (folderPlanes[i].FullPathName == "")
                {
                    var item = folderPlanes[i];
                    folderPlanes.Remove(item);
                }
            }

            // Commented Code for testing, you can see here what folders are expanded when pressing RefreshButton
            // List<string> SnapShot = TreeUtils.TakeSnapshot(TabbedNavTrees.SelectedNavTree.RootNode);

            TabbedNavTrees.SelectedNavTree.RebuildTree();

            // When no selectedFolderPlane is selected FullPathName is null
            SelectedPath= SelectedFolderPlane.FullPathName ?? "";

        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Netherlands Netherlands
Retired hobby programmer.

Comments and Discussions