public PluginsModel(IPluginStateProvider pluginStateProvider, IObservable <Tuple <bool, bool, string, string> > pluginDependencies) : base(pluginStateProvider) { pluginDependencies.Subscribe(t => { this._includeSuggest = t.Item1; this.AlreadyInstalled = t.Item2; this.InstallDirectory = t.Item3; this.ConfigDirectory = t.Item4; this.Refresh(); }); this.AvailablePlugins.ItemChanged.Select(e => this.Plugins.Any()) .Subscribe(any => { if (!any) { this.HasInternetConnection = true; } }); Observable.Timer(TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(2)) .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(async _ => await SetInternetConnection()); }
protected PluginsModelBase(IPluginStateProvider pluginStateProvider, SemVersion version) { this.Header = "Plugins"; this.PluginStateProvider = pluginStateProvider; this._version = version; this.InstalledPlugins.Changed.Subscribe(e => this.InstalledPlugins.RaisePropertyChanged()); }
public PluginsModel(IPluginStateProvider pluginStateProvider, IObservable <Tuple <bool, string, string> > pluginDependencies) : base(pluginStateProvider) { pluginDependencies.Subscribe(t => { this._alreadyInstalled = t.Item1; this.InstallDirectory = t.Item2; this.ConfigDirectory = t.Item3; }); }
public PluginsModel(IPluginStateProvider pluginStateProvider, IObservable <Tuple <bool, bool, string, string> > pluginDependencies) { this.Header = "Plugins"; this.PluginStateProvider = pluginStateProvider; pluginDependencies.Subscribe((t) => { this._includeSuggest = t.Item1; this._alreadyInstalled = t.Item2; this._installDirectory = t.Item3; this._configDirectory = t.Item4; this.Refresh(); }); //this.Refresh(); }
public PluginsModel(IPluginStateProvider pluginStateProvider, IObservable <Tuple <bool, string, string> > pluginDependencies) : base(pluginStateProvider) { EnvironmentVariables = new Dictionary <string, string> { { ElasticsearchEnvironmentStateProvider.ConfDir, this.ConfigDirectory }, // might be listing plugins from a Elasticsearch 5.x installation { ElasticsearchEnvironmentStateProvider.ConfDirOld, this.ConfigDirectory } }; pluginDependencies.Subscribe(t => { this.AlreadyInstalled = t.Item1; this.PreviousInstallDirectory = t.Item2; this.ConfigDirectory = t.Item3; this.Refresh(); }); this.AvailablePlugins.ItemChanged.Select(e => this.Plugins.Any()) .Subscribe(any => { if (!any) { this.HasInternetConnection = true; } }); this.AvailablePlugins.ItemChanged .Where(x => x.PropertyName == nameof(Plugin.Selected) && x.Sender.PluginType == PluginType.XPack) .Select(x => x.Sender.Selected) .Subscribe(selected => { this.XPackEnabled = selected; this.Validate(); }); Observable.Timer(TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(2)) .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(async _ => await SetInternetConnection()); this.SetHttpsProxy = ReactiveCommand.CreateAsyncTask(async _ => await this.HttpsProxyUITask()); this.WhenAnyObservable(vm => vm.SetHttpsProxy) .Subscribe(x => { // handle the cancel dialog button. Don't remove the current values if (x == null) { return; } if (x.Length == 0) { HttpsProxyHost = null; HttpsProxyPort = null; return; } x = Scheme.Replace(x, "$1"); var proxyParts = x.Split(new [] { ':' }, 2); this.HttpsProxyHost = proxyParts[0]; this.HttpsProxyPort = proxyParts.Length == 2 && int.TryParse(proxyParts[1], out var port) ? (int?)port : null; }); }
public KibanaInstallationModel( IWixStateProvider wixStateProvider, IServiceStateProvider serviceStateProvider, IPluginStateProvider pluginStateProvider, IKibanaEnvironmentStateProvider environmentStateProvider, ISession session, string[] args ) : base(wixStateProvider, session, args) { var versionConfig = new VersionConfiguration(wixStateProvider, this.Session.IsInstalled); this.KibanaEnvironmentState = environmentStateProvider; this.LocationsModel = new LocationsModel(versionConfig); this.NoticeModel = new NoticeModel(versionConfig, serviceStateProvider, this.LocationsModel); this.ServiceModel = new ServiceModel(serviceStateProvider, versionConfig); this.ConfigurationModel = new ConfigurationModel(); this.ConnectingModel = new ConnectingModel(); var pluginDependencies = this.WhenAnyValue( vm => vm.NoticeModel.AlreadyInstalled, vm => vm.LocationsModel.InstallDir, vm => vm.LocationsModel.ConfigDirectory ); this.PluginsModel = new PluginsModel(pluginStateProvider, pluginDependencies); var isUpgrade = versionConfig.InstallationDirection == InstallationDirection.Up; var observeHost = this.WhenAnyValue(x => x.ConfigurationModel.HostName, x => x.ConfigurationModel.HttpPort, (h, p) => $"http://{(string.IsNullOrWhiteSpace(h) ? "localhost" : h)}:{p}"); var observeInstallationLog = this.WhenAnyValue(vm => vm.MsiLogFileLocation); var observeKibanaLog = this.WhenAnyValue(vm => vm.LocationsModel.KibanaLog); var observeInstallXPack = this.PluginsModel.AvailablePlugins.ItemChanged .Where(x => x.PropertyName == nameof(Plugin.Selected) && x.Sender.PluginType == PluginType.XPack) .Select(x => x.Sender.Selected); this.ClosingModel = new ClosingModel(wixStateProvider.CurrentVersion, isUpgrade, observeHost, observeInstallationLog, observeKibanaLog, observeInstallXPack, serviceStateProvider); this.AllSteps.AddRange(new List <IStep> { this.NoticeModel, this.LocationsModel, this.ServiceModel, this.ConfigurationModel, this.ConnectingModel, this.PluginsModel, this.ClosingModel }); var observeValidationChanges = this.WhenAny( vm => vm.NoticeModel.ValidationFailures, vm => vm.LocationsModel.ValidationFailures, vm => vm.PluginsModel.ValidationFailures, vm => vm.ServiceModel.ValidationFailures, vm => vm.ConfigurationModel.ValidationFailures, vm => vm.ConnectingModel.ValidationFailures, vm => vm.ClosingModel.ValidationFailures, vm => vm.TabSelectedIndex, (notice, locations, plugins, service, config, connecting, closing, index) => { var firstInvalidScreen = this.Steps.FirstOrDefault(s => !s.IsValid) ?? this.ClosingModel; return(firstInvalidScreen); }); observeValidationChanges .Subscribe(selected => { var step = this.Steps[this.TabSelectedIndex]; var failures = step.ValidationFailures; this.FirstInvalidStepValidationFailures = selected.ValidationFailures; }); this.WhenAny( vm => vm.NoticeModel.IsValid, vm => vm.LocationsModel.IsValid, vm => vm.PluginsModel.IsValid, vm => vm.ServiceModel.IsValid, vm => vm.ConfigurationModel.IsValid, vm => vm.ConnectingModel.IsValid, vm => vm.ClosingModel.IsValid, (notice, locations, plugins, service, config, connecting, closing) => { var firstInvalidScreen = this.Steps.Select((s, i) => new { s, i }).FirstOrDefault(s => !s.s.IsValid); return(firstInvalidScreen?.i ?? (this.Steps.Count - 1)); }) .Subscribe(selected => { this.TabSelectionMax = selected; //if one of the steps prior to the current selection is invalid jump back if (this.TabSelectedIndex > this.TabSelectionMax) { this.TabSelectedIndex = this.TabSelectionMax; } this.FirstInvalidStepValidationFailures = this.ActiveStep.ValidationFailures; }); this.Install = ReactiveCommand.CreateAsyncTask(observeValidationChanges.Select(s => s.IsValid), _ => { this.TabSelectedIndex += 1; return(this.InstallUITask()); }); this.Install.Subscribe(installationObservable => { installationObservable.Subscribe(installed => { this.ClosingModel.Installed = installed; }); }); this.Refresh(); //validate the first stab explicitly on constructing this //main viewmodel. WPF triggers a validation already this.ParsedArguments = new KibanaArgumentParser( this.AllSteps.Cast <IValidatableReactiveObject>().Concat(new[] { this }).ToList(), args); this.ActiveStep.Validate(); }
public InstallationModel( IWixStateProvider wixStateProvider, JavaConfiguration javaConfiguration, IElasticsearchEnvironmentStateProvider environmentStateProvider, IServiceStateProvider serviceStateProvider, IPluginStateProvider pluginStateProvider, ElasticsearchYamlConfiguration yamlConfiguration, LocalJvmOptionsConfiguration localJvmOptions, ISession session, string[] args ) { this.Session = session; if (wixStateProvider == null) { throw new ArgumentNullException(nameof(wixStateProvider)); } if (javaConfiguration == null) { throw new ArgumentNullException(nameof(javaConfiguration)); } this._wixStateProvider = wixStateProvider; this.JavaConfiguration = javaConfiguration; this.ElasticsearchEnvironmentState = environmentStateProvider; this._yamlConfiguration = yamlConfiguration; var versionConfig = new VersionConfiguration(wixStateProvider); this.SameVersionAlreadyInstalled = versionConfig.SameVersionAlreadyInstalled; this.HigherVersionAlreadyInstalled = versionConfig.HigherVersionAlreadyInstalled; this.LocationsModel = new LocationsModel(environmentStateProvider, yamlConfiguration, versionConfig); this.NoticeModel = new NoticeModel(versionConfig, serviceStateProvider, this.LocationsModel); this.ServiceModel = new ServiceModel(serviceStateProvider, versionConfig); this.ConfigurationModel = new ConfigurationModel(yamlConfiguration, localJvmOptions); var pluginDependencies = this.WhenAnyValue( vm => vm.ConfigurationModel.IngestNode, vm => vm.NoticeModel.AlreadyInstalled, vm => vm.LocationsModel.InstallDir, vm => vm.LocationsModel.ConfigDirectory ); this.PluginsModel = new PluginsModel(pluginStateProvider, pluginDependencies); var observeHost = this.WhenAnyValue(vm => vm.ConfigurationModel.NetworkHost, vm => vm.ConfigurationModel.HttpPort, (h, p) => $"http://{(string.IsNullOrWhiteSpace(h) ? "localhost" : h)}:{p}"); var observeLog = this.WhenAnyValue(vm => vm.MsiLogFileLocation); var observeElasticsearchLog = this.WhenAnyValue(vm => vm.LocationsModel.ElasticsearchLog); var isUpgrade = versionConfig.InstallationDirection == InstallationDirection.Up; this.ClosingModel = new ClosingModel(wixStateProvider.CurrentVersion, isUpgrade, observeHost, observeLog, observeElasticsearchLog, serviceStateProvider); this.AllSteps = new ReactiveList <IStep> { this.NoticeModel, this.LocationsModel, this.ServiceModel, this.ConfigurationModel, this.PluginsModel, this.ClosingModel }; this.Steps = this.AllSteps.CreateDerivedCollection(x => x, x => x.IsRelevant); this.NextButtonText = TextResources.SetupView_NextText; var canMoveForwards = this.WhenAny(vm => vm.TabSelectedIndex, vm => vm.TabSelectionMax, (i, max) => i.GetValue() < max.GetValue()); this.Next = ReactiveCommand.Create(canMoveForwards); this.Next.Subscribe(i => { this.TabSelectedIndex = Math.Min(this.Steps.Count - 1, this.TabSelectedIndex + 1); }); var canMoveBackwards = this.WhenAny(vm => vm.TabSelectedIndex, (i) => i.GetValue() > 0); this.Back = ReactiveCommand.Create(canMoveBackwards); this.Back.Subscribe(i => { this.TabSelectedIndex = Math.Max(0, this.TabSelectedIndex - 1); }); this.Help = ReactiveCommand.Create(); this.ShowLicenseBlurb = ReactiveCommand.Create(); this.ShowCurrentStepErrors = ReactiveCommand.Create(); this.RefreshCurrentStep = ReactiveCommand.Create(); this.RefreshCurrentStep.Subscribe(x => { this.Steps[this.TabSelectedIndex].Refresh(); }); this.Exit = ReactiveCommand.Create(); var observeValidationChanges = this.WhenAny( vm => vm.NoticeModel.ValidationFailures, vm => vm.LocationsModel.ValidationFailures, vm => vm.ConfigurationModel.ValidationFailures, vm => vm.PluginsModel.ValidationFailures, vm => vm.ServiceModel.ValidationFailures, vm => vm.ClosingModel.ValidationFailures, vm => vm.TabSelectedIndex, (welcome, locations, configuration, plugins, service, install, index) => { var firstInvalidScreen = this.Steps.FirstOrDefault(s => !s.IsValid) ?? this.ClosingModel; return(firstInvalidScreen); }); var canInstall = observeValidationChanges.Select(s => s.IsValid); this.Install = ReactiveCommand.CreateAsyncTask(canInstall, _ => { this.TabSelectedIndex += 1; return(this.InstallUITask()); }); this.Install.Subscribe(installationObservable => { installationObservable.Subscribe(installed => { this.ClosingModel.Installed = installed; }); }); this.WhenAny(vm => vm.TabSelectedIndex, v => v.GetValue()) .Subscribe(i => { var c = this.Steps.Count; if (i == (c - 1)) { this.NextButtonText = TextResources.SetupView_ExitText; } else if (i == (c - 2)) { this.NextButtonText = TextResources.SetupView_InstallText; } else { this.NextButtonText = TextResources.SetupView_NextText; } }); observeValidationChanges .Subscribe(selected => { var step = this.Steps[this.TabSelectedIndex]; var failures = step.ValidationFailures; this.CurrentStepValidationFailures = selected.ValidationFailures; }); this.WhenAny( vm => vm.NoticeModel.IsValid, vm => vm.LocationsModel.IsValid, vm => vm.ConfigurationModel.IsValid, vm => vm.PluginsModel.IsValid, vm => vm.ServiceModel.IsValid, vm => vm.ClosingModel.IsValid, (welcome, locations, configuration, plugins, service, install) => { var firstInvalidScreen = this.Steps.Select((s, i) => new { s, i }).FirstOrDefault(s => !s.s.IsValid); return(firstInvalidScreen?.i ?? (this.Steps.Count - 1)); }) .Subscribe(selected => { this.TabSelectionMax = selected; //if one of the steps prior to the current selection is invalid jump back if (this.TabSelectedIndex > this.TabSelectionMax) { this.TabSelectedIndex = this.TabSelectionMax; } this.CurrentStepValidationFailures = this.ActiveStep.ValidationFailures; }); this.WhenAnyValue(view => view.ValidationFailures) .Subscribe(failures => { this.PrequisiteFailures = (failures ?? Enumerable.Empty <ValidationFailure>()) .Where(v => _prerequisiteProperties.Contains(v.PropertyName)) .ToList(); }); this.Refresh(); //validate the first stab explicitly on constructing this //main viewmodel. WPF triggers a validation already this.ParsedArguments = new InstallationModelArgumentParser(this.AllSteps.Cast <IValidatableReactiveObject>().Concat(new[] { this }).ToList(), args); this.ActiveStep.Validate(); }
public ElasticsearchInstallationModel( IWixStateProvider wixStateProvider, JavaConfiguration javaConfiguration, ElasticsearchEnvironmentConfiguration elasticsearchEnvironmentConfiguration, IServiceStateProvider serviceStateProvider, IPluginStateProvider pluginStateProvider, ElasticsearchYamlConfiguration yamlConfiguration, LocalJvmOptionsConfiguration localJvmOptions, TempDirectoryConfiguration tempDirectoryConfiguration, IFileSystem fileSystem, ISession session, string[] args) : base(wixStateProvider, session, args) { this.JavaConfiguration = javaConfiguration ?? throw new ArgumentNullException(nameof(javaConfiguration)); this.ElasticsearchEnvironmentConfiguration = elasticsearchEnvironmentConfiguration; this.TempDirectoryConfiguration = tempDirectoryConfiguration; this._yamlConfiguration = yamlConfiguration; var versionConfig = new VersionConfiguration(wixStateProvider, this.Session.IsInstalled); this.SameVersionAlreadyInstalled = versionConfig.SameVersionAlreadyInstalled; this.UnInstalling = this.Session.IsUninstalling; this.InstallationInProgress = this._wixStateProvider.InstallationInProgress; this.Installing = this.Session.IsInstalling; this.Installed = this.Session.IsInstalled; this.Upgrading = this.Session.IsUpgrading; this.HigherVersionAlreadyInstalled = versionConfig.HigherVersionAlreadyInstalled; this.LocationsModel = new LocationsModel(elasticsearchEnvironmentConfiguration, yamlConfiguration, versionConfig, fileSystem); this.ServiceModel = new ServiceModel(serviceStateProvider, versionConfig); this.NoticeModel = new NoticeModel(versionConfig, serviceStateProvider, this.LocationsModel, this.ServiceModel); this.ConfigurationModel = new ConfigurationModel(yamlConfiguration, localJvmOptions); var pluginDependencies = this.WhenAnyValue( vm => vm.NoticeModel.ExistingVersionInstalled, vm => vm.LocationsModel.PreviousInstallationDirectory, vm => vm.LocationsModel.ConfigDirectory ); this.PluginsModel = new PluginsModel(pluginStateProvider, versionConfig.CurrentVersion, pluginDependencies); var upgradeFromXPackPlugin = this.WhenAnyValue(vm => vm.PluginsModel.PreviousInstallationHasXPack); var canAutomaticallySetup = this.WhenAnyValue(vm => vm.ServiceModel.StartAfterInstall, vm => vm.ServiceModel.InstallAsService) .Select(t => t.Item1 && t.Item2); this.XPackModel = new XPackModel(versionConfig, canAutomaticallySetup, upgradeFromXPackPlugin); var isUpgrade = versionConfig.InstallationDirection == InstallationDirection.Up; var observeHost = this.WhenAnyValue(vm => vm.ConfigurationModel.NetworkHost, vm => vm.ConfigurationModel.HttpPort, (h, p) => $"http://{(string.IsNullOrWhiteSpace(h) ? "localhost" : h)}:{p}"); var observeInstallationLog = this.WhenAnyValue(vm => vm.MsiLogFileLocation); var observeElasticsearchLog = this.WhenAnyValue(vm => vm.LocationsModel.ElasticsearchLog); this.ClosingModel = new ClosingModel(wixStateProvider.CurrentVersion, isUpgrade, observeHost, observeInstallationLog, observeElasticsearchLog, serviceStateProvider); this.AllSteps.AddRange(new List <IStep> { this.NoticeModel, this.LocationsModel, this.ServiceModel, this.ConfigurationModel, this.PluginsModel, this.XPackModel, this.ClosingModel }); this.AllSteps.ChangeTrackingEnabled = true; var observeValidationChanges = this.WhenAny( vm => vm.NoticeModel.ValidationFailures, vm => vm.LocationsModel.ValidationFailures, vm => vm.ConfigurationModel.ValidationFailures, vm => vm.PluginsModel.ValidationFailures, vm => vm.XPackModel.ValidationFailures, vm => vm.ServiceModel.ValidationFailures, vm => vm.ClosingModel.ValidationFailures, vm => vm.TabSelectedIndex, (welcome, locations, configuration, plugins, xpack, service, install, index) => { var firstInvalidScreen = this.Steps.FirstOrDefault(s => !s.IsValid) ?? this.ClosingModel; return(firstInvalidScreen); }); observeValidationChanges .Subscribe(firstInvalidStep => { this.TabFirstInvalidIndex = this.Steps .Select((s, i) => new { s, i = (int?)i }) .Where(t => !t.s.IsValid) .Select(t => t.i) .FirstOrDefault(); this.FirstInvalidStepValidationFailures = firstInvalidStep.ValidationFailures; }); this.WhenAny( vm => vm.NoticeModel.IsValid, vm => vm.LocationsModel.IsValid, vm => vm.ConfigurationModel.IsValid, vm => vm.PluginsModel.IsValid, vm => vm.XPackModel.IsValid, vm => vm.ServiceModel.IsValid, vm => vm.ClosingModel.IsValid, (welcome, locations, configuration, plugins, xpack, service, install) => { var firstInvalidScreen = this.Steps.Select((s, i) => new { s, i }).FirstOrDefault(s => !s.s.IsValid); return(firstInvalidScreen?.i ?? (this.Steps.Count - 1)); }) .Subscribe(selected => { this.TabSelectionMax = selected; //if one of the steps prior to the current selection is invalid jump back if (this.TabSelectedIndex > this.TabSelectionMax) { this.TabSelectedIndex = this.TabSelectionMax; } this.FirstInvalidStepValidationFailures = this.ActiveStep.ValidationFailures; }); this.Steps.Changed.Subscribe(e => { var firstInvalidScreen = this.Steps.Select((s, i) => new { s, i }).FirstOrDefault(s => !s.s.IsValid); var selectedTabIndex = firstInvalidScreen?.i ?? (this.Steps.Count - 1); this.TabSelectionMax = selectedTabIndex; //if one of the steps prior to the current selection is invalid jump back if (this.TabSelectedIndex > this.TabSelectionMax) { this.TabSelectedIndex = this.TabSelectionMax; } this.FirstInvalidStepValidationFailures = this.ActiveStep.ValidationFailures; }); this.Install = ReactiveCommand.CreateAsyncTask(observeValidationChanges.Select(s => s.IsValid), _ => { this.TabSelectedIndex += 1; return(this.InstallUITask()); }); this.Install.Subscribe(installationObservable => { installationObservable.Subscribe(installed => this.ClosingModel.Installed = installed); }); this.Refresh(); //validate the first stab explicitly on constructing this //main viewmodel. WPF triggers a validation already this.ParsedArguments = new ElasticsearchArgumentParser( this.AllSteps.Cast <IValidatableReactiveObject>().Concat(new[] { this }).ToList(), args); this.ActiveStep.Validate(); }
public ElasticsearchInstallationModel( IWixStateProvider wixStateProvider, JavaConfiguration javaConfiguration, ElasticsearchEnvironmentConfiguration elasticsearchEnvironmentConfiguration, IServiceStateProvider serviceStateProvider, IPluginStateProvider pluginStateProvider, ElasticsearchYamlConfiguration yamlConfiguration, LocalJvmOptionsConfiguration localJvmOptions, ISession session, string[] args ) : base(wixStateProvider, session, args) { this.JavaConfiguration = javaConfiguration ?? throw new ArgumentNullException(nameof(javaConfiguration)); this.ElasticsearchEnvironmentConfiguration = elasticsearchEnvironmentConfiguration; this._yamlConfiguration = yamlConfiguration; var versionConfig = new VersionConfiguration(wixStateProvider); this.SameVersionAlreadyInstalled = versionConfig.SameVersionAlreadyInstalled; this.HigherVersionAlreadyInstalled = versionConfig.HigherVersionAlreadyInstalled; this.LocationsModel = new LocationsModel(elasticsearchEnvironmentConfiguration, yamlConfiguration, versionConfig); this.NoticeModel = new NoticeModel(versionConfig, serviceStateProvider, this.LocationsModel); this.ServiceModel = new ServiceModel(serviceStateProvider, versionConfig); this.ConfigurationModel = new ConfigurationModel(yamlConfiguration, localJvmOptions); var pluginDependencies = this.WhenAnyValue( vm => vm.ConfigurationModel.IngestNode, vm => vm.NoticeModel.AlreadyInstalled, vm => vm.LocationsModel.InstallDir, vm => vm.LocationsModel.ConfigDirectory ); this.PluginsModel = new PluginsModel(pluginStateProvider, pluginDependencies); var isUpgrade = versionConfig.InstallationDirection == InstallationDirection.Up; var observeHost = this.WhenAnyValue(vm => vm.ConfigurationModel.NetworkHost, vm => vm.ConfigurationModel.HttpPort, (h, p) => $"http://{(string.IsNullOrWhiteSpace(h) ? "localhost" : h)}:{p}"); var observeInstallationLog = this.WhenAnyValue(vm => vm.MsiLogFileLocation); var observeElasticsearchLog = this.WhenAnyValue(vm => vm.LocationsModel.ElasticsearchLog); var observeInstallXPack = this.PluginsModel.AvailablePlugins.ItemChanged .Where(x => x.PropertyName == nameof(Plugin.Selected) && x.Sender.PluginType == PluginType.XPack) .Select(x => x.Sender.Selected); this.ClosingModel = new ClosingModel(wixStateProvider.CurrentVersion, isUpgrade, observeHost, observeInstallationLog, observeElasticsearchLog, observeInstallXPack, serviceStateProvider); this.AllSteps = new ReactiveList <IStep> { this.NoticeModel, this.LocationsModel, this.ServiceModel, this.ConfigurationModel, this.PluginsModel, this.ClosingModel }; this.Steps = this.AllSteps.CreateDerivedCollection(x => x, x => x.IsRelevant); var observeValidationChanges = this.WhenAny( vm => vm.NoticeModel.ValidationFailures, vm => vm.LocationsModel.ValidationFailures, vm => vm.ConfigurationModel.ValidationFailures, vm => vm.PluginsModel.ValidationFailures, vm => vm.ServiceModel.ValidationFailures, vm => vm.ClosingModel.ValidationFailures, vm => vm.TabSelectedIndex, (welcome, locations, configuration, plugins, service, install, index) => { var firstInvalidScreen = this.Steps.FirstOrDefault(s => !s.IsValid) ?? this.ClosingModel; return(firstInvalidScreen); }); observeValidationChanges .Subscribe(selected => { var step = this.Steps[this.TabSelectedIndex]; var failures = step.ValidationFailures; this.CurrentStepValidationFailures = selected.ValidationFailures; }); this.WhenAny( vm => vm.NoticeModel.IsValid, vm => vm.LocationsModel.IsValid, vm => vm.ConfigurationModel.IsValid, vm => vm.PluginsModel.IsValid, vm => vm.ServiceModel.IsValid, vm => vm.ClosingModel.IsValid, (welcome, locations, configuration, plugins, service, install) => { var firstInvalidScreen = this.Steps.Select((s, i) => new { s, i }).FirstOrDefault(s => !s.s.IsValid); return(firstInvalidScreen?.i ?? (this.Steps.Count - 1)); }) .Subscribe(selected => { this.TabSelectionMax = selected; //if one of the steps prior to the current selection is invalid jump back if (this.TabSelectedIndex > this.TabSelectionMax) { this.TabSelectedIndex = this.TabSelectionMax; } this.CurrentStepValidationFailures = this.ActiveStep.ValidationFailures; }); this.Install = ReactiveCommand.CreateAsyncTask(observeValidationChanges.Select(s => s.IsValid), _ => { this.TabSelectedIndex += 1; return(this.InstallUITask()); }); this.Install.Subscribe(installationObservable => { installationObservable.Subscribe(installed => this.ClosingModel.Installed = installed); }); this.Refresh(); //validate the first stab explicitly on constructing this //main viewmodel. WPF triggers a validation already this.ParsedArguments = new ElasticsearchArgumentParser( this.AllSteps.Cast <IValidatableReactiveObject>().Concat(new[] { this }).ToList(), args); this.ActiveStep.Validate(); }