using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Tango.Core; using Tango.Core.Commands; using Tango.Core.DI; using Tango.MachineStudio.Common; using Tango.MachineStudio.Common.Modules; using Tango.MachineStudio.Common.Navigation; using Tango.MachineStudio.Common.Threading; using Tango.SharedUI.Controls; namespace Tango.MachineStudio.UI.Navigation { /// /// Represents the Machine Studio default Navigation Manager. /// /// public class DefaultNavigationManager : ExtendedObject, INavigationManager { private event Action NavigationCycleCompleted; private IDispatcherProvider _dispatcherProvider; private IStudioModuleLoader _moduleLoader; private Object _currentVM; private String _lastFullPath; private bool _preventHistory; private bool _navigating_back; private Stack _navigationHistory; /// /// Gets the current view model. /// public StudioViewModel CurrentVM { get { return _currentVM as StudioViewModel; } } private IStudioModule _currentModule; /// /// Gets or sets the current module. /// public IStudioModule CurrentModule { get { return _currentModule; } private set { _currentModule = value; RaisePropertyChangedAuto(); } } /// /// Navigates to the previous view. /// public RelayCommand NavigateBackCommand { get; private set; } /// /// Navigates to the specified full path in command parameter. /// public RelayCommand NavigateToCommand { get; private set; } /// /// Initializes a new instance of the class. /// /// The module loader. public DefaultNavigationManager(IStudioModuleLoader moduleLoader, IDispatcherProvider dispatcherProvider) { _navigationHistory = new Stack(); _moduleLoader = moduleLoader; NavigateToCommand = new RelayCommand(async (x) => await NavigateTo(x)); NavigateBackCommand = new RelayCommand(async () => await NavigateBack()); _dispatcherProvider = dispatcherProvider; } /// /// Navigates to the specified PPC view. /// /// The view. public Task NavigateTo(NavigationView view, bool pushToHistory = true) { LogManager.Log($"Navigating to: {view.ToString()}..."); _dispatcherProvider.Invoke(() => { MainWindow.Instance.NavigationControl.NavigateTo(view.ToString()); }); return Task.FromResult(true); } /// /// Navigates to the specified PPC view with the specified receive object. /// /// The view. /// /// /// public Task NavigateWithObject(NavigationView view, TPass obj, bool pushToHistory = true) { LogManager.Log($"Navigating to: {view.ToString()}, with object {typeof(TPass).Name}..."); MainWindow.Instance.NavigationControl.NavigateTo(view.ToString()); INavigationObjectReceiver receiver = MainWindow.Instance.NavigationControl.Elements.FirstOrDefault(x => (x.GetType().Name == view.ToString() || NavigationControl.GetNavigationName(x) == view.ToString()) && x.DataContext is INavigationObjectReceiver).DataContext as INavigationObjectReceiver; if (receiver != null) { receiver.OnNavigatedToWithObject(obj); } return Task.FromResult(true); } /// /// Navigates to the specified module. /// /// public Task NavigateTo(bool pushToHistory = true) where T : IStudioModule { return NavigateTo(typeof(T)); } /// /// Navigates to the specified module using the view path (e.g MainView.JobsView). /// /// /// The view path. public Task NavigateTo(string viewPath, bool pushToHistory = true) where T : IStudioModule { return NavigateTo(pushToHistory, viewPath.Split('.')); } /// /// Navigates to the specified module using the view path (e.g MainView,JobsView). /// This method makes it easy to do stuff like NavigateTo(nameof(MainView),nameof(JobsView)); /// /// /// The view path. public Task NavigateTo(bool pushToHistory = true, params String[] viewPath) where T : IStudioModule { return NavigateTo(typeof(T), pushToHistory, viewPath); } /// /// Navigates to the specified module and view by full path (e.g Jobs.JobsView). /// /// The full path. public async Task NavigateTo(String fullPath, bool pushToHistory = true) { String[] path = fullPath.Split('.'); var module = _moduleLoader.UserModules.SingleOrDefault(x => x.GetType().Name == path[0] || x.Name == path[0]); if (path.Length == 1 && path[0] == CurrentModule.Name) return true; LogManager.Log($"Navigating to: {fullPath}..."); var fromVM = _currentVM; if (_currentVM != null && _currentVM is INavigationBlocker) { if (_navigating_back) { if (!await (_currentVM as INavigationBlocker).OnNavigateBackRequest()) { return false; } } else { if (!await (_currentVM as INavigationBlocker).OnNavigateOutRequest()) { return false; } } } if (pushToHistory && _lastFullPath != null && !_preventHistory) { _navigationHistory.Push(_lastFullPath); RaisePropertyChanged(nameof(CanNavigateBack)); } _lastFullPath = fullPath; MainWindow.Instance.NavigationControl.NavigateTo(NavigationView.MainView.ToString()); var navigationControl = MachineStudio.UI.Views.MainView.Instance.NavigationControl; CurrentModule = module; var moduleView = navigationControl.NavigateTo(module.Name); _currentVM = moduleView.DataContext; if (path.Length > 1) { var moduleNavigation = moduleView.FindChildOffline(); moduleNavigation.RegisterForLoadedOrNow(async (x, e) => { foreach (var view in path.Skip(1)) { await Task.Delay(100); var v = moduleNavigation.NavigateTo(view); if (v != null) { _currentVM = v.DataContext; if (view != path.Last()) { moduleNavigation = v.FindChildOffline(); } } else { throw LogManager.Log(new ArgumentNullException("Could not navigate to " + fullPath)); } } NavigationCycleCompleted?.Invoke(fromVM, _currentVM); }); } return true; } /// /// Navigates for result. /// /// The type of the module. /// The type of the view. /// The type of the result. /// The type of the object. /// The object. /// if set to true [push to history]. /// public Task NavigateForResult(TObject obj, bool pushToHistory = true) where TModule : IStudioModule { TaskCompletionSource source = new TaskCompletionSource(); var fromVM = _currentVM; Object toVM = null; Action handler = null; handler = (from, to) => { if (toVM == null) { toVM = to; if (toVM is INavigationResultProvider) { (toVM as INavigationResultProvider).OnNavigationObjectReceived(obj); } } else { if (to == fromVM && from == toVM) { if (from is INavigationResultProvider) { source.SetResult((from as INavigationResultProvider).GetNavigationResult()); } } NavigationCycleCompleted -= handler; } }; NavigationCycleCompleted += handler; NavigateTo(typeof(TView).Name, pushToHistory); return source.Task; } /// /// Navigates to the specified module and view with the specified object. /// /// The type of the module. /// The type of the view. /// The type of the pass. /// The object. /// if set to true [push to history]. /// public Task NavigateWithObject(TPass obj, bool pushToHistory = true) where TModule : IStudioModule { TaskCompletionSource source = new TaskCompletionSource(); Action handler = null; handler = (from, to) => { if (to is INavigationObjectReceiver) { (to as INavigationObjectReceiver).OnNavigatedToWithObject(obj); } NavigationCycleCompleted -= handler; }; NavigationCycleCompleted += handler; NavigateTo(typeof(TView).Name, pushToHistory); return source.Task; } private Task NavigateTo(Type moduleType, bool pushToHistory = true, params String[] viewPath) { if (viewPath != null && viewPath.Length > 0) { return NavigateTo(moduleType.Name + "." + String.Join(".", viewPath), pushToHistory); } else { return NavigateTo(moduleType.Name, pushToHistory); } } /// /// Gets a value indicating whether the navigation system is able to navigate to the previous view. /// public bool CanNavigateBack { get { return _navigationHistory.Count > 0; } } /// /// Navigates to the previous view if is true. /// public async Task NavigateBack() { LogManager.Log("Navigating back..."); _navigating_back = true; String first = _navigationHistory.Pop(); _preventHistory = true; if (await NavigateTo(first)) { RaisePropertyChanged(nameof(CanNavigateBack)); _preventHistory = false; _navigating_back = false; return true; } else { _navigationHistory.Push(first); _preventHistory = false; _navigating_back = false; RaisePropertyChanged(nameof(CanNavigateBack)); return false; } } /// /// Clears the navigation back history. /// public void ClearHistory() { LogManager.Log("Navigation history cleared."); _navigationHistory.Clear(); RaisePropertyChanged(nameof(CanNavigateBack)); } /// /// Clears the navigation back history except the specified view type. /// /// public void ClearHistoryExcept() { LogManager.Log($"Navigation history cleared except for {typeof(T).Name}."); var history_list = _navigationHistory.ToList(); history_list = history_list.Where(x => x.Contains(typeof(T).Name)).Distinct().ToList(); _navigationHistory.Clear(); foreach (var item in history_list) { _navigationHistory.Push(item); } RaisePropertyChanged(nameof(CanNavigateBack)); } public void NavigateToModule() where T : IStudioModule { var loader = TangoIOC.Default.GetInstance(); var module = loader.UserModules.SingleOrDefault(x => x.GetType() == typeof(T)); if (module != null) { TangoIOC.Default.GetInstance().StartModule(module); } } } }