using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using Tango.Core.Commands; using Tango.Core.IO; namespace Tango.FileSystem { public class FileExplorerControl : Control { private ListBox _listBox; private DataGrid _datagrid; private bool _preventSynchronization; private Point _dragOutStartPoint; private bool _isMouseDown; private List _selectedItemsBeforeDrag; private List _copyItems; private bool _isCut; private bool _isAfterContextMenu; #region IsCut Attached Property /// /// Determines whether the draggable element is currently being dragged. /// public static readonly DependencyProperty IsCutProperty = DependencyProperty.RegisterAttached("IsCut", typeof(bool), typeof(FileExplorerControl), new FrameworkPropertyMetadata(false)); /// /// Sets the IsCut attached property. /// /// The element. /// if set to true [value]. public static void SetIsCut(FrameworkElement element, bool value) { element.SetValue(IsCutProperty, value); } /// /// Gets the is dragging attached property. /// /// The element. /// public static bool GetIsCut(FrameworkElement element) { return (bool)element.GetValue(IsCutProperty); } #endregion public IFileSystemContainer CurrentItem { get { return (IFileSystemContainer)GetValue(CurrentItemProperty); } set { SetValue(CurrentItemProperty, value); } } public static readonly DependencyProperty CurrentItemProperty = DependencyProperty.Register("CurrentItem", typeof(IFileSystemContainer), typeof(FileExplorerControl), new PropertyMetadata(null, (d, e) => (d as FileExplorerControl).OnCurrentItemChanged())); public FileSystemItem SelectedItem { get { return (FileSystemItem)GetValue(SelectedItemProperty); } set { SetValue(SelectedItemProperty, value); } } public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem", typeof(FileSystemItem), typeof(FileExplorerControl), new PropertyMetadata(null)); public ICommand ItemDoubleClickedCommand { get { return (ICommand)GetValue(ItemDoubleClickedCommandProperty); } set { SetValue(ItemDoubleClickedCommandProperty, value); } } public static readonly DependencyProperty ItemDoubleClickedCommandProperty = DependencyProperty.Register("ItemDoubleClickedCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); public ICommand DeleteCommand { get { return (ICommand)GetValue(DeleteCommandProperty); } set { SetValue(DeleteCommandProperty, value); } } public static readonly DependencyProperty DeleteCommandProperty = DependencyProperty.Register("DeleteCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); public ICommand DeleteCommandInternal { get { return (ICommand)GetValue(DeleteCommandInternalProperty); } set { SetValue(DeleteCommandInternalProperty, value); } } public static readonly DependencyProperty DeleteCommandInternalProperty = DependencyProperty.Register("DeleteCommandInternal", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); public ICommand DropCommand { get { return (ICommand)GetValue(DropCommandProperty); } set { SetValue(DropCommandProperty, value); } } public static readonly DependencyProperty DropCommandProperty = DependencyProperty.Register("DropCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); public ICommand DragCommand { get { return (ICommand)GetValue(DragCommandProperty); } set { SetValue(DragCommandProperty, value); } } public static readonly DependencyProperty DragCommandProperty = DependencyProperty.Register("DragCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); public ICommand CopyCommand { get { return (ICommand)GetValue(CopyCommandProperty); } set { SetValue(CopyCommandProperty, value); } } public static readonly DependencyProperty CopyCommandProperty = DependencyProperty.Register("CopyCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); public ICommand CutCommand { get { return (ICommand)GetValue(CutCommandProperty); } set { SetValue(CutCommandProperty, value); } } public static readonly DependencyProperty CutCommandProperty = DependencyProperty.Register("CutCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); public ICommand CopyPasteCommand { get { return (ICommand)GetValue(CopyPasteCommandProperty); } set { SetValue(CopyPasteCommandProperty, value); } } public static readonly DependencyProperty CopyPasteCommandProperty = DependencyProperty.Register("CopyPasteCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); public ICommand CutPasteCommand { get { return (ICommand)GetValue(CutPasteCommandProperty); } set { SetValue(CutPasteCommandProperty, value); } } public static readonly DependencyProperty CutPasteCommandProperty = DependencyProperty.Register("CutPasteCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); public ICommand PasteCommandInternal { get { return (ICommand)GetValue(PasteCommandInternalProperty); } set { SetValue(PasteCommandInternalProperty, value); } } public static readonly DependencyProperty PasteCommandInternalProperty = DependencyProperty.Register("PasteCommandInternal", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); public ICommand SelectAllCommand { get { return (ICommand)GetValue(SelectAllCommandProperty); } set { SetValue(SelectAllCommandProperty, value); } } public static readonly DependencyProperty SelectAllCommandProperty = DependencyProperty.Register("SelectAllCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); public ICommand DownloadCommandInternal { get { return (ICommand)GetValue(DownloadCommandInternalProperty); } set { SetValue(DownloadCommandInternalProperty, value); } } public static readonly DependencyProperty DownloadCommandInternalProperty = DependencyProperty.Register("DownloadCommandInternal", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); public ICommand DownloadCommand { get { return (ICommand)GetValue(DownloadCommandProperty); } set { SetValue(DownloadCommandProperty, value); } } public static readonly DependencyProperty DownloadCommandProperty = DependencyProperty.Register("DownloadCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); public ICommand OpenCommand { get { return (ICommand)GetValue(OpenCommandProperty); } set { SetValue(OpenCommandProperty, value); } } public static readonly DependencyProperty OpenCommandProperty = DependencyProperty.Register("OpenCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); public ICommand BackCommand { get { return (ICommand)GetValue(BackCommandProperty); } set { SetValue(BackCommandProperty, value); } } public static readonly DependencyProperty BackCommandProperty = DependencyProperty.Register("BackCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); public ICommand RenameCommand { get { return (ICommand)GetValue(RenameCommandProperty); } set { SetValue(RenameCommandProperty, value); } } public static readonly DependencyProperty RenameCommandProperty = DependencyProperty.Register("RenameCommand", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); public ICommand RenameCommandInternal { get { return (ICommand)GetValue(RenameCommandInternalProperty); } set { SetValue(RenameCommandInternalProperty, value); } } public static readonly DependencyProperty RenameCommandInternalProperty = DependencyProperty.Register("RenameCommandInternal", typeof(ICommand), typeof(FileExplorerControl), new PropertyMetadata(null)); public bool IsContextMenuOpened { get { return (bool)GetValue(IsContextMenuOpenedProperty); } set { SetValue(IsContextMenuOpenedProperty, value); } } public static readonly DependencyProperty IsContextMenuOpenedProperty = DependencyProperty.Register("IsContextMenuOpened", typeof(bool), typeof(FileExplorerControl), new PropertyMetadata(false, (d, e) => (d as FileExplorerControl).OnIsContextMenuOpenedChanged())); public ImageSource DriveIcon { get { return (ImageSource)GetValue(DriveIconProperty); } set { SetValue(DriveIconProperty, value); } } public static readonly DependencyProperty DriveIconProperty = DependencyProperty.Register("DriveIcon", typeof(ImageSource), typeof(FileExplorerControl), new PropertyMetadata(null)); public ImageSource FolderIcon { get { return (ImageSource)GetValue(FolderIconProperty); } set { SetValue(FolderIconProperty, value); } } public static readonly DependencyProperty FolderIconProperty = DependencyProperty.Register("FolderIcon", typeof(ImageSource), typeof(FileExplorerControl), new PropertyMetadata(null)); public FileExplorerControlMode Mode { get { return (FileExplorerControlMode)GetValue(ModeProperty); } set { SetValue(ModeProperty, value); } } public static readonly DependencyProperty ModeProperty = DependencyProperty.Register("Mode", typeof(FileExplorerControlMode), typeof(FileExplorerControl), new PropertyMetadata(FileExplorerControlMode.Large)); public ObservableCollection SelectedItems { get { return (ObservableCollection)GetValue(SelectedItemsProperty); } set { SetValue(SelectedItemsProperty, value); } } public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.Register("SelectedItems", typeof(ObservableCollection), typeof(FileExplorerControl), new PropertyMetadata(null, (d, e) => (d as FileExplorerControl).OnSelectedItemsChanged())); public bool AllowDrag { get { return (bool)GetValue(AllowDragProperty); } set { SetValue(AllowDragProperty, value); } } public static readonly DependencyProperty AllowDragProperty = DependencyProperty.Register("AllowDrag", typeof(bool), typeof(FileExplorerControl), new PropertyMetadata(null)); static FileExplorerControl() { DefaultStyleKeyProperty.OverrideMetadata(typeof(FileExplorerControl), new FrameworkPropertyMetadata(typeof(FileExplorerControl))); } public FileExplorerControl() { Focusable = true; _copyItems = new List(); _selectedItemsBeforeDrag = new List(); CopyCommand = new RelayCommand(() => { ResetItemsCut(); _copyItems.Clear(); _copyItems.AddRange(SelectedItems.ToList()); _isCut = false; }, () => SelectedItems != null && SelectedItems.Count > 0); CutCommand = new RelayCommand(() => { _copyItems.Clear(); _copyItems.AddRange(SelectedItems.ToList()); CutSelectedItems(); _isCut = true; }, () => SelectedItems != null && SelectedItems.Count > 0); PasteCommandInternal = new RelayCommand(() => { if (_isCut) { CutPasteCommand?.Execute(_copyItems.ToList()); } else { CopyPasteCommand?.Execute(_copyItems.ToList()); } ResetItemsCut(); _copyItems.Clear(); }, () => _copyItems.Count > 0); SelectAllCommand = new RelayCommand(() => { ResetItemsCut(); _copyItems.Clear(); SelectedItems.Clear(); if (CurrentItem != null) { foreach (var item in CurrentItem.Items) { SelectedItems.Add(item); } } }); DownloadCommandInternal = new RelayCommand(() => { DownloadCommand?.Execute(SelectedItems.ToList()); }, () => SelectedItems != null && SelectedItems.Count > 0 && SelectedItems.All(x => x.Type != FileSystemItemType.Drive)); OpenCommand = new RelayCommand(() => { ItemDoubleClickedCommand?.Execute(SelectedItems.FirstOrDefault()); }, () => SelectedItems != null && SelectedItems.Count == 1); DeleteCommandInternal = new RelayCommand(() => { DeleteCommand?.Execute(SelectedItems.ToList()); }, () => SelectedItems != null && SelectedItems.Count > 1 && SelectedItems.All(x => x.Type != FileSystemItemType.Drive)); RenameCommandInternal = new RelayCommand(() => { RenameCommand?.Execute(SelectedItems.FirstOrDefault()); }, () => SelectedItems != null && SelectedItems.Count == 1 && SelectedItems.All(x => x.Type != FileSystemItemType.Drive)); } private void OnIsContextMenuOpenedChanged() { _isMouseDown = false; (PasteCommandInternal as RelayCommand)?.RaiseCanExecuteChanged(); (CutCommand as RelayCommand)?.RaiseCanExecuteChanged(); (CopyCommand as RelayCommand)?.RaiseCanExecuteChanged(); (DownloadCommandInternal as RelayCommand)?.RaiseCanExecuteChanged(); (OpenCommand as RelayCommand)?.RaiseCanExecuteChanged(); (DeleteCommandInternal as RelayCommand)?.RaiseCanExecuteChanged(); (RenameCommandInternal as RelayCommand)?.RaiseCanExecuteChanged(); if (IsContextMenuOpened) { _isAfterContextMenu = true; } } private void ResetItemsCut() { foreach (var item in _listBox.Items) { var element = _listBox.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement; if (element != null) { SetIsCut(element, false); } element = _datagrid.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement; if (element != null) { SetIsCut(element, false); } } } private void CutSelectedItems() { ResetItemsCut(); foreach (var item in SelectedItems.ToList()) { var element = _listBox.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement; if (element != null) { SetIsCut(element, true); } element = _datagrid.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement; if (element != null) { SetIsCut(element, true); } } } public override void OnApplyTemplate() { base.OnApplyTemplate(); _listBox = GetTemplateChild("PART_listbox") as ListBox; _datagrid = GetTemplateChild("PART_datagrid") as DataGrid; _listBox.SelectionChanged += _listBox_SelectionChanged; _datagrid.SelectionChanged += _datagrid_SelectionChanged; } protected override void OnPreviewKeyDown(KeyEventArgs e) { base.OnPreviewKeyDown(e); if (e.Key == Key.Delete) { if (DeleteCommandInternal != null && DeleteCommandInternal.CanExecute(null)) { DeleteCommandInternal?.Execute(null); } } else if (e.Key == Key.Return) { if (OpenCommand != null && OpenCommand.CanExecute(null)) { OpenCommand?.Execute(null); } } else if (e.Key == Key.C && Keyboard.IsKeyDown(Key.LeftCtrl)) { if (CopyCommand != null && CopyCommand.CanExecute(null)) { CopyCommand.Execute(null); } } else if (e.Key == Key.X && Keyboard.IsKeyDown(Key.LeftCtrl)) { if (CutCommand != null && CutCommand.CanExecute(null)) { CutCommand.Execute(null); } } else if (e.Key == Key.V && Keyboard.IsKeyDown(Key.LeftCtrl)) { if (PasteCommandInternal != null && PasteCommandInternal.CanExecute(null)) { PasteCommandInternal.Execute(null); } } else if (e.Key == Key.A && Keyboard.IsKeyDown(Key.LeftCtrl)) { if (SelectAllCommand != null && SelectAllCommand.CanExecute(null)) { SelectAllCommand.Execute(null); } } else if (e.Key == Key.D && Keyboard.IsKeyDown(Key.LeftCtrl)) { if (DownloadCommandInternal != null && DownloadCommandInternal.CanExecute(null)) { DownloadCommandInternal.Execute(null); } } else if (e.Key == Key.F2) { if (RenameCommandInternal != null && RenameCommandInternal.CanExecute(null)) { RenameCommandInternal.Execute(null); } } else if (e.Key == Key.Down) { if (SelectedItems != null && SelectedItems.Count == 0 && CurrentItem != null && CurrentItem.Items.Count > 0) { SelectedItems.Add(CurrentItem.Items.FirstOrDefault()); } } else if (e.Key == Key.Back) { BackCommand?.Execute(null); } } private void _datagrid_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (!_preventSynchronization) { _preventSynchronization = true; _listBox.SelectedItems.Clear(); foreach (var item in _datagrid.SelectedItems) { _listBox.SelectedItems.Add(item); } SynchronizeSelectedItems(_listBox.SelectedItems); _preventSynchronization = false; } } private void _listBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (!_preventSynchronization) { _preventSynchronization = true; _datagrid.SelectedItems.Clear(); foreach (var item in _listBox.SelectedItems) { _datagrid.SelectedItems.Add(item); } SynchronizeSelectedItems(_listBox.SelectedItems); _preventSynchronization = false; } } private void SynchronizeSelectedItems(IList items) { if (SelectedItems == null) { SelectedItems = items.Cast().ToObservableCollection(); } else { SelectedItems.Clear(); foreach (var item in items.Cast().ToList()) { SelectedItems.Add(item); } } } private void OnSelectedItemsChanged() { if (SelectedItems != null) { SelectedItems.CollectionChanged -= SelectedItems_CollectionChanged; SelectedItems.CollectionChanged += SelectedItems_CollectionChanged; SynchronizeControls(); } } private void SelectedItems_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { SynchronizeControls(); } private void SynchronizeControls() { if (_listBox == null) return; if (!_preventSynchronization) { _preventSynchronization = true; _listBox.SelectedItems.Clear(); _datagrid.SelectedItems.Clear(); foreach (var item in SelectedItems) { _listBox.SelectedItems.Add(item); _datagrid.SelectedItems.Add(item); } _preventSynchronization = false; } } protected override void OnDrop(DragEventArgs e) { base.OnDrop(e); try { string[] items = (string[])e.Data.GetData(DataFormats.FileDrop, false); if (items != null && items.Length > 0) { List fItems = new List(); foreach (var item in items) { if (Directory.Exists(item)) { fItems.Add(new FolderItem() { Path = item }); } else if (File.Exists(item)) { fItems.Add(new FileItem() { Path = item }); } } if (fItems.Count > 0) { DropCommand?.Execute(fItems); } } } catch { } //Ignore } protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e) { base.OnPreviewMouseLeftButtonDown(e); if (_isAfterContextMenu) { _isAfterContextMenu = false; return; } if (!AllowDrag) return; if (e.OriginalSource is FrameworkElement) { var listBoxItem = (e.OriginalSource as FrameworkElement).FindAncestor(); var dataGridRow = (e.OriginalSource as FrameworkElement).FindAncestor(); if (listBoxItem == null && dataGridRow == null) { return; } } _selectedItemsBeforeDrag.Clear(); _selectedItemsBeforeDrag.AddRange(SelectedItems); _isMouseDown = true; AllowDrop = false; _dragOutStartPoint = e.GetPosition(null); } protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e) { base.OnPreviewMouseLeftButtonUp(e); AllowDrop = true; _isMouseDown = false; } protected async override void OnPreviewMouseMove(MouseEventArgs e) { base.OnPreviewMouseMove(e); if (_isMouseDown && !IsContextMenuOpened) { Point mpos = e.GetPosition(null); Vector diff = this._dragOutStartPoint - mpos; if (e.LeftButton == MouseButtonState.Pressed && Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance || Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance) { if (SelectedItems.Count == 0) { return; } SelectedItems.Clear(); foreach (var item in _selectedItemsBeforeDrag) { SelectedItems.Add(item); } List> dropItems = new List>(); foreach (var item in SelectedItems) { var tempFile = TemporaryManager.Default.CreateFile(".remote"); dropItems.Add(new Tuple(item, tempFile)); } List notifyItems = new List(); List watchers = new List(); FileSystemEventHandler handler = (x, args) => { var detectedDropItem = dropItems.SingleOrDefault(y => y.Item2.FileName == System.IO.Path.GetFileName(args.FullPath)); Debug.WriteLine($"File Created: {args.FullPath}"); if (detectedDropItem != null) { try { detectedDropItem.Item2.Delete(); //Delete temp file. if (File.Exists(args.FullPath)) { File.Delete(args.FullPath); //Delete dropped fake file. dropItems.Remove(detectedDropItem); notifyItems.Add(new DragItem() { FileSystemItem = detectedDropItem.Item1, Destination = System.IO.Path.GetDirectoryName(args.FullPath), }); if (dropItems.Count == 0) { foreach (var watcher in watchers) { watcher.Dispose(); } //Notify to user with all items! Dispatcher.BeginInvoke(new Action(() => { DragCommand?.Execute(notifyItems); })); } } } catch { } } else { Debug.WriteLine($"Not Found: {args.FullPath}"); } }; foreach (var drive in DriveInfo.GetDrives().Where(x => x.IsReady && (x.DriveType == DriveType.Fixed || x.DriveType == DriveType.Removable))) { FileSystemWatcher watcher = new FileSystemWatcher(drive.RootDirectory.FullName, "*.remote"); watcher.IncludeSubdirectories = true; watcher.EnableRaisingEvents = true; watcher.Created += handler; watchers.Add(watcher); } string[] files = dropItems.Select(x => x.Item2.Path).ToArray(); try { var ef = DragDrop.DoDragDrop(this, new DataObject(DataFormats.FileDrop, files, false), DragDropEffects.Copy); } catch (Exception ex) { Debug.WriteLine(ex); Debugger.Break(); } await Task.Delay(3000); foreach (var watcher in watchers) { watcher.Dispose(); } if (dropItems.Count > 0) { //Notify about problem! } _isMouseDown = false; AllowDrop = true; } } } private async void OnCurrentItemChanged() { if (IsVisible) { await Task.Delay(100); this.Focus(); Keyboard.Focus(this); } } } }