using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Media; using Tango.Core; using Tango.Core.Commands; using Tango.PPC.Common; using Tango.PPC.Common.Modules; using Tango.PPC.Common.Navigation; using Tango.PPC.Common.Threading; using Tango.PPC.UI.Views; using Tango.SharedUI.Controls; namespace Tango.PPC.UI.Navigation { /// /// Represents the default PPC navigation manager. /// /// public class DefaultNavigationManager : ExtendedObject, INavigationManager { private event Action NavigationCycleCompleted; private IDispatcherProvider _dispatcherProvider; private IPPCModuleLoader _moduleLoader; private Object _currentVM; private String _lastFullPath; private bool _preventHistory; private bool _navigating_back; private Stack _navigationHistory; /// /// Gets the current view model. /// public PPCViewModel CurrentVM { get { return _currentVM as PPCViewModel; } } private IPPCModule _currentModule; /// /// Gets or sets the current module. /// public IPPCModule 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(IPPCModuleLoader 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) { if (view == NavigationView.HomeModule) { _navigationHistory.Clear(); _lastFullPath = null; var firstModule = _moduleLoader.UserModules.FirstOrDefault(); if (firstModule != null) { var moduleAtt = firstModule.GetType().GetCustomAttribute(); if (moduleAtt != null) { return NavigateTo(firstModule.GetType(), pushToHistory, moduleAtt.HomeViewName); } else { return NavigateTo(firstModule.GetType(), pushToHistory); } } else { return NavigateTo(NavigationView.NoPermissionsView); } } else { LogManager.Log($"Navigating to: {view.ToString()}..."); MainView.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}..."); MainView.Instance.NavigationControl.NavigateTo(view.ToString()); INavigationObjectReceiver receiver = MainView.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 : IPPCModule { 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 : IPPCModule { 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 : IPPCModule { 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; MainView.Instance.NavigationControl.NavigateTo(NavigationView.LayoutView.ToString()); var navigationControl = LayoutView.Instance.NavigationControl; CurrentModule = module; var moduleView = navigationControl.NavigateTo(module.Name); _currentVM = moduleView.DataContext; if (path.Length > 1) { var moduleNavigation = moduleView.FindChildOffline(); if (moduleNavigation != null) { 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); }); } else { 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 : IPPCModule { 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 : IPPCModule { 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)); } } }