/// <summary> /// Creates View/ViewModel instances for the specified <paramref name="viewType"/> if they /// haven't been created yet in the current flow /// </summary> /// <param name="viewType"></param> /// <returns>true if the View/ViewModel didn't exist and had to be created</returns> bool CreateViewAndViewModel(UIViewType viewType, [AllowNull] ViewWithData data = null) { var list = GetObjectsForFlow(activeFlow); if (viewType == UIViewType.Login) { if (!list.ContainsKey(viewType)) { var d = factory.CreateViewAndViewModel(UIViewType.TwoFactor); list.Add(UIViewType.TwoFactor, d); } } // 2fa view/viewmodel is created when login is created 'cause login needs the 2fa viewmodel // so the only thing we want to do is connect the viewmodel to the view when it's showing else if (viewType == UIViewType.TwoFactor) { var d = list[viewType]; if (d.View.ViewModel == null) { d.ViewModel.Initialize(data); d.View.DataContext = d.ViewModel; } } if (!list.ContainsKey(viewType)) { var d = factory.CreateViewAndViewModel(viewType); d.ViewModel.Initialize(data); d.View.DataContext = d.ViewModel; list.Add(viewType, d); return(true); } return(false); }
void RunView(UIViewType viewType, ViewWithData arg = null) { if (requestedTarget?.ViewType == viewType || (requestedTarget?.ViewType == UIViewType.None && requestedTarget?.MainFlow == CurrentFlow)) { arg = requestedTarget; } if (arg == null) { arg = new ViewWithData { ActiveFlow = activeFlow, MainFlow = selectedFlow, ViewType = viewType } } ; bool firstTime = CreateViewAndViewModel(viewType, arg); var view = GetObjectsForFlow(activeFlow)[viewType].View; transition.OnNext(new LoadData { Flow = activeFlow, View = view, Data = arg }); // controller might have been stopped in the OnNext above if (IsStopped) { return; } // if it's not the first time we've shown this view, no need // to set it up if (!firstTime) { return; } SetupView(viewType, view.ViewModel); } void SetupView(UIViewType viewType, IViewModel viewModel) { var list = GetObjectsForFlow(activeFlow); var pair = list[viewType]; var hasDone = viewModel as IHasDone; var hasCancel = viewModel as IHasCancel; // 2FA is set up when login is set up, so nothing to do if (viewType == UIViewType.TwoFactor) { return; } // we're setting up the login dialog, we need to setup the 2fa as // well to continue the flow if it's needed, since the // authenticationresult callback won't happen until // everything is done if (viewType == UIViewType.Login) { var pair2fa = list[UIViewType.TwoFactor]; pair2fa.AddHandler(((IDialogViewModel)pair2fa.ViewModel).WhenAny(x => x.IsShowing, x => x.Value) .Where(x => x) .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(_ => Fire(Trigger.Next))); pair2fa.AddHandler(((IHasCancel)pair2fa.ViewModel).Cancel .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(_ => Fire(uiStateMachine.CanFire(Trigger.Cancel) ? Trigger.Cancel : Trigger.Finish))); if (hasDone != null) { pair.AddHandler(hasDone.Done .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(_ => Fire(Trigger.Finish))); } } else if (hasDone != null) { pair.AddHandler(hasDone.Done .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(_ => Fire(uiStateMachine.CanFire(Trigger.Next) ? Trigger.Next : Trigger.Finish))); } if (hasCancel != null) { pair.AddHandler(hasCancel.Cancel .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(_ => Fire(uiStateMachine.CanFire(Trigger.Cancel) ? Trigger.Cancel : Trigger.Finish))); } } /// <summary> /// Creates View/ViewModel instances for the specified <paramref name="viewType"/> if they /// haven't been created yet in the current flow /// </summary> /// <param name="viewType"></param> /// <returns>true if the View/ViewModel didn't exist and had to be created</returns> bool CreateViewAndViewModel(UIViewType viewType, ViewWithData data = null) { var list = GetObjectsForFlow(activeFlow); if (viewType == UIViewType.Login) { if (!list.ContainsKey(viewType)) { var d = factory.CreateViewAndViewModel(UIViewType.TwoFactor); list.Add(UIViewType.TwoFactor, d); } } // 2fa view/viewmodel is created when login is created 'cause login needs the 2fa viewmodel // so the only thing we want to do is connect the viewmodel to the view when it's showing else if (viewType == UIViewType.TwoFactor) { var d = list[viewType]; if (d.View.ViewModel == null) { d.ViewModel.Initialize(data); d.View.DataContext = d.ViewModel; } } IUIPair pair = null; var firstTime = !list.TryGetValue(viewType, out pair); if (firstTime) { pair = factory.CreateViewAndViewModel(viewType); } pair.ViewModel.Initialize(data); if (firstTime) { pair.View.DataContext = pair.ViewModel; list.Add(viewType, pair); } return(firstTime); } /// <summary> /// Returns the view/viewmodel pair for a given flow /// </summary> Dictionary <UIViewType, IUIPair> GetObjectsForFlow(UIControllerFlow flow) { Dictionary <UIViewType, IUIPair> list; if (!uiObjects.TryGetValue(flow, out list)) { list = new Dictionary <UIViewType, IUIPair>(); uiObjects.Add(flow, list); } return(list); } void Fire(Trigger next, ViewWithData arg = null) { Debug.WriteLine("Firing {0} from {1} ({2})", next, uiStateMachine.State, GetHashCode()); if (triggers.ContainsKey(next)) { uiStateMachine.Fire(triggers[next], arg); } else { uiStateMachine.Fire(next); } } UIViewType Go(Trigger trigger) { return(Go(trigger, activeFlow)); } UIViewType Go(Trigger trigger, UIControllerFlow flow) { var m = machines[flow]; Debug.WriteLine("Firing {0} from {1} for flow {2} ({3})", trigger, m.State, flow, GetHashCode()); m.Fire(trigger); return(m.State); } void Reset() { if (connectionAdded != null) { connectionManager.Connections.CollectionChanged -= connectionAdded; } connectionAdded = null; var tr = transition; var cmp = completion; transition = null; completion = null; disposables.Clear(); tr?.Dispose(); cmp?.Dispose(); stopping = false; connection = null; } bool disposed; // To detect redundant calls