public void TestInitialFalse() { var notifier = new BooleanNotifier(false); var recorder = new TestScheduler().CreateObserver<bool>(); notifier.Subscribe(recorder); notifier.Value.Is(false); notifier.TurnOn(); notifier.TurnOff(); notifier.Value = true; notifier.Value = true; notifier.Value = false; notifier.TurnOn(); notifier.SwitchValue(); notifier.SwitchValue(); recorder.Messages.Is( OnNext(0, true), OnNext(0, false), OnNext(0, true), OnNext(0, true), OnNext(0, false), OnNext(0, true), OnNext(0, false), OnNext(0, true)); recorder.Messages.Clear(); notifier.TurnOn(); recorder.Messages.Count.Is(0); }
public void TestInitialFalse() { var notifier = new BooleanNotifier(false); var recorder = new TestScheduler().CreateObserver <bool>(); notifier.Subscribe(recorder); notifier.Value.Is(false); notifier.TurnOn(); notifier.TurnOff(); notifier.Value = true; notifier.Value = true; notifier.Value = false; notifier.TurnOn(); notifier.SwitchValue(); notifier.SwitchValue(); recorder.Messages.Is( OnNext(0, true), OnNext(0, false), OnNext(0, true), OnNext(0, true), OnNext(0, false), OnNext(0, true), OnNext(0, false), OnNext(0, true)); recorder.Messages.Clear(); notifier.TurnOn(); recorder.Messages.Count.Is(0); }
public Operation(IWlanWorker worker) { this._worker = worker; IsLoading = new BooleanNotifier(); IsWorking = new BooleanNotifier(); ShowLoadingTime(); // For debug ShowWorkingTime(); // For debug ReloadTimer = new ReactiveTimer(TimeSpan.FromSeconds(Settings.Current.AutoReloadInterval)) .AddTo(this.Subscription); ReloadTimer .Subscribe(async _ => await LoadProfilesAsync(true)) .AddTo(this.Subscription); Settings.Current .ObserveProperty(x => x.AutoReloadInterval) .Throttle(TimeSpan.FromMilliseconds(100)) .Subscribe(x => ReloadTimer.Interval = TimeSpan.FromSeconds(x)) .AddTo(this.Subscription); this.ObserveProperty(x => x.IsAutoReloadEnabled) .Subscribe(isEnabled => { if (isEnabled) { ReloadTimer.Start(); } else { ReloadTimer.Stop(); } }) .AddTo(this.Subscription); this.ObserveProperty(x => x.IsSuspended, false) .Subscribe(async isSuspended => { if (isSuspended) { ReloadTimer.Stop(); } else { if (IsAutoReloadEnabled) { ReloadTimer.Start(); } else { await LoadProfilesAsync(false); } } }) .AddTo(this.Subscription); var initialTask = LoadProfilesAsync(false); }
public BooleanNotifierViewModel() { ToggleCommand.Subscribe(BooleanNotifier.SwitchValue).AddTo(DisposeCollection); ONCommand.Subscribe(BooleanNotifier.TurnOn).AddTo(DisposeCollection); OFFCommand.Subscribe(BooleanNotifier.TurnOff).AddTo(DisposeCollection); BooleanNotifier.Subscribe(_ => CountNotifier.Increment()).AddTo(DisposeCollection); }
public Operation(IWlanWorker worker) { this._worker = worker; IsLoading = new BooleanNotifier(); IsWorking = new BooleanNotifier(); ShowLoadingTime(); // For debug ShowWorkingTime(); // For debug ReloadTimer = new ReactiveTimer(TimeSpan.FromSeconds(Settings.Current.AutoReloadInterval)) .AddTo(this.Subscription); ReloadTimer .Subscribe(async _ => await LoadProfilesAsync(true)) .AddTo(this.Subscription); Settings.Current .ObserveProperty(x => x.AutoReloadInterval) .Throttle(TimeSpan.FromMilliseconds(100)) .Subscribe(x => ReloadTimer.Interval = TimeSpan.FromSeconds(x)) .AddTo(this.Subscription); this.ObserveProperty(x => x.IsAutoReloadEnabled) .Subscribe(isEnabled => { if (isEnabled) ReloadTimer.Start(); else ReloadTimer.Stop(); }) .AddTo(this.Subscription); this.ObserveProperty(x => x.IsSuspended, false) .Subscribe(async isSuspended => { if (isSuspended) { ReloadTimer.Stop(); } else { if (IsAutoReloadEnabled) ReloadTimer.Start(); else await LoadProfilesAsync(false); } }) .AddTo(this.Subscription); var initialTask = LoadProfilesAsync(false); }
public TypingPracticeItemContentViewModel(AppContextService appService) : base(appService) { this.practiceKeyInfoViewModelsDisposable = new CompositeDisposable().AddTo(this.Disposables); this.notMatchedKeyNotifier = new BooleanNotifier(); this.keyMissingNotifier = new BooleanNotifier(); this.OdaiText = new ReactivePropertySlim <string>().AddTo(this.Disposables); this.YomiText = new ReactivePropertySlim <string>().AddTo(this.Disposables); this.PracticeKeyInfoViewModels = new ReactiveCollection <PracticeKeyInfoViewModel>().AddTo(this.Disposables); this.CurrentExpectedKey = new ReactivePropertySlim <Key>().AddTo(this.Disposables); this.IsNotMatched = this.notMatchedKeyNotifier.ToReadOnlyReactivePropertySlim().AddTo(this.Disposables); this.IsKeyMissing = this.keyMissingNotifier.ToReadOnlyReactivePropertySlim().AddTo(this.Disposables); this.Initialize(appService); }
public TimerStartCommandViewModel() { // ctorからカウント開始 var ctorDateTime = DateTime.Now; MyTimer1 = Observable.Interval(TimeSpan.FromSeconds(1)) .Select(_ => DateTime.Now - ctorDateTime) .ToReadOnlyReactivePropertySlim() .AddTo(CompositeDisposable); #if false // 1. DataTime.Now からの差分でカウント ◆もう少しかっちょよい実装ない? var clickDateTime = DateTime.MinValue; MyTimer2 = Observable.Interval(TimeSpan.FromSeconds(1)) .Where(_ => clickDateTime != DateTime.MinValue) .Select(_ => DateTime.Now - clickDateTime) .ToReadOnlyReactivePropertySlim() .AddTo(CompositeDisposable); StartCommand = new ReactiveCommand() .WithSubscribe(() => clickDateTime = DateTime.Now, CompositeDisposable.Add); StopCommand = new ReactiveCommand() .WithSubscribe(() => clickDateTime = DateTime.MinValue, CompositeDisposable.Add); #else // 2. BooleanNotifier で管理。Command.CanExecute も取れるので良い感じ var timerRunning = new BooleanNotifier(); // BooleanNotifier は IDisposable じゃないので Good! //var timerRunning = new ReactivePropertySlim<bool>(initialValue: false).AddTo(CompositeDisposable); MyTimer2 = Observable.Interval(TimeSpan.FromSeconds(1)) .TakeWhile(_ => timerRunning.Value) .Repeat() .Select(sec => TimeSpan.FromSeconds(sec)) .ToReadOnlyReactivePropertySlim() .AddTo(CompositeDisposable); StartCommand = timerRunning.Inverse() .ToReactiveCommand(!timerRunning.Value) .WithSubscribe(() => timerRunning.TurnOn(), CompositeDisposable.Add); // ◆BooleanNotifier は (Rpと違って) Subscribe 時に LatestValue を発行しないので初期値の指定が必要する StopCommand = timerRunning .ToReactiveCommand(timerRunning.Value) .WithSubscribe(() => timerRunning.TurnOff(), CompositeDisposable.Add); #endif }
public AsyncReactiveCommand1ViewModel() { // インスタンス作ってから Subscribe するパターン ButtonClickAsyncCommand1 = new AsyncReactiveCommand() .AddTo(CompositeDisposable); ButtonClickAsyncCommand1 .Subscribe(async() => await Task.Delay(1000)) .AddTo(CompositeDisposable); // WithSubscribe() : インスタンス化 + Disposable 登録 ButtonClickAsyncCommand2 = new AsyncReactiveCommand() .WithSubscribe(async() => await Task.Delay(3000), CompositeDisposable.Add); // IObservable<bool>から作成する例 var boolean = new BooleanNotifier(); var command = boolean .ToAsyncReactiveCommand() .WithSubscribe(() => Task.CompletedTask, CompositeDisposable.Add); }
public MainWindowViewModel(AppContextService appService) : base(appService) { this.booleanNotifier = new BooleanNotifier(); this.serialDisposable = new SerialDisposable().AddTo(this.Disposables); this.IsDark = new ReactivePropertySlim <bool>().AddTo(this.Disposables); this.ToggleThemeCommand = new ReactiveCommand().AddTo(this.Disposables); this.ContentsIndex = new ReactivePropertySlim <int>().AddTo(this.Disposables); this.KeyUpCommand = new ReactiveCommand <KeyEventArgs>().AddTo(this.Disposables); this.DialogViewModel = new ReactivePropertySlim <object>().AddTo(this.Disposables); this.IsDialogOpen = new ReactivePropertySlim <bool>().AddTo(this.Disposables); this.CurrentConfirmContentViewModel = new TypingConfirmContentViewModel(appService).AddTo(this.Disposables); this.CurrentPracticeItemContentViewModel = new TypingPracticeItemContentViewModel(appService).AddTo(this.Disposables); this.CurrentResultContentViewModel = new TypingResultContentViewModel(appService).AddTo(this.Disposables); this.Initialize(appService); }
public BooleanNotifier2ViewModel() { var booleanNotifier = new BooleanNotifier(initialValue: false); var reactiveProperty = new ReactiveProperty <bool>(initialValue: false).AddTo(CompositeDisposable); BooleanNotifierButtonCommand0 = booleanNotifier.ToReactiveCommand().AddTo(CompositeDisposable); ReactivePropertyButtonCommand = reactiveProperty.ToReactiveCommand().AddTo(CompositeDisposable); // CanExecute method of ReactiveCommand returns true as default. // So, you set initialValue explicitly to `booleanNotifier.Value`. BooleanNotifierButtonCommand1 = booleanNotifier .ToReactiveCommand(initialValue: booleanNotifier.Value) .AddTo(CompositeDisposable); // Or if you would like to convert to something using Select and others // before calling ToReactiveCommand, you can use StartWith. BooleanNotifierButtonCommand2 = booleanNotifier .StartWith(booleanNotifier.Value) .Select(x => Something(x)) .ToReactiveCommand() .AddTo(CompositeDisposable);
internal async Task <bool> SetInputtedKeyAsync(Key actualKey, BooleanNotifier notMatchedKeyNotifier, BooleanNotifier keyMissingNotifier) { if (actualKey.TryToChar(out var actualChar)) { this.KeyInputtedCount.Value++; this.InputtedKey.Value = actualChar; if (actualKey == this.ExpectedKey.Value.ToKey()) { notMatchedKeyNotifier.TurnOff(); return(true); } this.KeyMistakedCount.Value++; notMatchedKeyNotifier.TurnOn(); keyMissingNotifier.TurnOn(); var task1 = AppContextService.BeepAsync(); var task2 = Task.Delay(TimeSpan.FromSeconds(0.2)); await Task.WhenAny(task1, task2); keyMissingNotifier.TurnOff(); } return(false); }
public MainController(StartupAgent agent, IWlanWorker worker) { StartupAgent = agent ?? throw new ArgumentNullException(nameof(agent)); Profiles = new ObservableCollection <ProfileItem>(); BindingOperations.EnableCollectionSynchronization(Profiles, _profilesLock); NotifyIconContainer = new NotifyIconContainer(); NotifyIconContainer.MouseLeftButtonClick += OnMainWindowShowRequested; NotifyIconContainer.MouseRightButtonClick += OnMenuWindowShowRequested; this._worker = worker; IsUpdating = new BooleanNotifier(); IsWorking = new BooleanNotifier(); ShowUpdatingTime(); // For debug ShowWorkingTime(); // For debug RushesRescan = new ReactiveProperty <bool>() .AddTo(this.Subscription); EngagesPriority = new ReactiveProperty <bool>() .AddTo(this.Subscription); EngagesPriority .Subscribe(_ => SetNotifyIconText()) .AddTo(this.Subscription); #region Update RescanTimer = new ReactiveTimer(TimeSpan.FromSeconds(Settings.Current.RescanInterval)) .AddTo(this.Subscription); RescanCommand = IsUpdating .Inverse() .ObserveOnUIDispatcher() // This is for thread access by ReactiveCommand. .ToReactiveCommand(); RescanCommand .Merge(EngagesPriority.Where(x => x).Select(x => x as object)) .Merge(RescanTimer.Select(x => x as object)) .Subscribe(async _ => await ScanNetworkAsync()) .AddTo(this.Subscription); Settings.Current .ObserveProperty(x => x.RescanInterval) .Subscribe(rescanInterval => RescanTimer.Interval = TimeSpan.FromSeconds(rescanInterval)) .AddTo(this.Subscription); RushesRescan .Subscribe(rushesRescan => { if (rushesRescan) { RescanTimer.Start(); } else { RescanTimer.Stop(); } SetNotifyIconText(); }) .AddTo(this.Subscription); var networkRefreshed = Observable.FromEventPattern( h => _worker.NetworkRefreshed += h, h => _worker.NetworkRefreshed -= h); var availabilityChanged = Observable.FromEventPattern( h => _worker.AvailabilityChanged += h, h => _worker.AvailabilityChanged -= h); var interfaceChanged = Observable.FromEventPattern( h => _worker.InterfaceChanged += h, h => _worker.InterfaceChanged -= h); var connectionChanged = Observable.FromEventPattern( h => _worker.ConnectionChanged += h, h => _worker.ConnectionChanged -= h); var profileChanged = Observable.FromEventPattern( h => _worker.ProfileChanged += h, h => _worker.ProfileChanged -= h); Observable.Merge(networkRefreshed, availabilityChanged, interfaceChanged, connectionChanged, profileChanged) .Throttle(TimeSpan.FromMilliseconds(100)) .Subscribe(async _ => { if (RushesRescan.Value) { RescanTimer.Start(TimeSpan.FromSeconds(Settings.Current.RescanInterval)); // Wait for due time. } await LoadProfilesAsync(); }) .AddTo(this.Subscription); #endregion #region Close CloseCommand = new ReactiveProperty <bool>(true) .ToReactiveCommand(); CloseCommand .Subscribe(_ => _current.Shutdown()) .AddTo(this.Subscription); #endregion }
public static void SwitchValueWith(this BooleanNotifier self, Action <bool> action) { action?.Invoke(self.Value); self.SwitchValue(); }
public DevicePageViewModel(UnconnectedImbleDevice unconnectedDevice) { this.Device = Observable.FromAsync(token => unconnectedDevice.Connect(token)) .CatchIgnore((Exception e) => { }) .ToReadOnlyReactiveProperty() .AddTo(this.disposables); this.IsConnected = this.Device.Select(device => device != null).ToReadOnlyReactiveProperty().AddTo(this.disposables); this.DeviceStatus = this.Device .Where(device => device != null) .Select(device => device.ObserveProperty(self => self.Status)) .Switch() .Do(value => Debug.WriteLine(value)) .ObserveOnUIDispatcher() .ToReadOnlyReactiveProperty() .AddTo(this.disposables); this.ReceivedMessage = this.Device .Where(device => device != null) .Select(device => Observable.FromEventPattern <DataArrivedEventArgs>(handler => device.DataArrived += handler, handler => device.DataArrived -= handler)) .Switch() .Select(args => new ReceivedMessageViewModel(args.EventArgs.Data, args.EventArgs.Timestamp)) .OnErrorRetry() .ToReadOnlyReactiveProperty() .AddTo(this.disposables); var notBusyNotifier = new BooleanNotifier(false); this.Name = new ReactiveProperty <string>("Fuga") .SetValidateNotifyError(value => value != null && Encoding.UTF8.GetByteCount(value) < 12 ? null : "Input short message") .AddTo(this.disposables); this.CanSendCommand = Observable.CombineLatest( this.DeviceStatus.Select(status => status == ImbleDeviceStatus.Running), this.Name.ObserveHasErrors, notBusyNotifier.Do(value => Debug.WriteLine(value)), (isRunning, hasErrors, notBusy) => isRunning && !hasErrors && notBusy) .ToReadOnlyReactiveProperty().AddTo(this.disposables); notBusyNotifier.TurnOn(); this.SendCommand = this.CanSendCommand.ToReactiveCommand().AddTo(this.disposables); this.SendCommand .Do(_ => notBusyNotifier.TurnOff()) .Select(_ => { var data = Encoding.UTF8.GetBytes(this.Name.Value); return(Observable.FromAsync(token => { using (var timeoutTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(5))) using (var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, timeoutTokenSource.Token)) { return this.Device.Value.SendAsync(data, 0, data.Length, linkedTokenSource.Token); } }) .Finally(() => notBusyNotifier.TurnOn())); }) .Switch() .OnErrorRetry((Exception e) => Debug.WriteLine(e)) .Subscribe() .AddTo(this.disposables); }
public DevicePageViewModel(UnconnectedImbleDevice unconnectedDevice) { this.Device = Observable.FromAsync(token => unconnectedDevice.Connect(token)) .CatchIgnore((Exception e) => { }) .ToReadOnlyReactiveProperty() .AddTo(this.disposables); this.IsConnected = this.Device.Select(device => device != null).ToReadOnlyReactiveProperty().AddTo(this.disposables); this.DeviceStatus = this.Device .Where(device => device != null) .Select(device => device.ObserveProperty(self => self.Status)) .Switch() .Do(value => Debug.WriteLine(value)) .ObserveOnUIDispatcher() .ToReadOnlyReactiveProperty() .AddTo(this.disposables); this.ReceivedMessage = this.Device .Where(device => device != null) .Select(device => Observable.FromEventPattern<DataArrivedEventArgs>(handler => device.DataArrived += handler, handler => device.DataArrived -= handler)) .Switch() .Select(args => new ReceivedMessageViewModel(args.EventArgs.Data, args.EventArgs.Timestamp)) .OnErrorRetry() .ToReadOnlyReactiveProperty() .AddTo(this.disposables); var notBusyNotifier = new BooleanNotifier(false); this.Name = new ReactiveProperty<string>("Fuga") .SetValidateNotifyError(value => value != null && Encoding.UTF8.GetByteCount(value) < 12 ? null : "Input short message") .AddTo(this.disposables); this.CanSendCommand = Observable.CombineLatest( this.DeviceStatus.Select(status => status == ImbleDeviceStatus.Running), this.Name.ObserveHasErrors, notBusyNotifier.Do(value => Debug.WriteLine(value)), (isRunning, hasErrors, notBusy) => isRunning && !hasErrors && notBusy) .ToReadOnlyReactiveProperty().AddTo(this.disposables); notBusyNotifier.TurnOn(); this.SendCommand = this.CanSendCommand.ToReactiveCommand().AddTo(this.disposables); this.SendCommand .Do(_ => notBusyNotifier.TurnOff()) .Select(_ => { var data = Encoding.UTF8.GetBytes(this.Name.Value); return Observable.FromAsync(token => { using (var timeoutTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(5))) using (var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, timeoutTokenSource.Token)) { return this.Device.Value.SendAsync(data, 0, data.Length, linkedTokenSource.Token); } }) .Finally(() => notBusyNotifier.TurnOn()); }) .Switch() .OnErrorRetry((Exception e) => Debug.WriteLine(e)) .Subscribe() .AddTo(this.disposables); }
internal AppViewModel() { CompositeDisposable itemDisposable = new CompositeDisposable(); App app = new App(); CurrentLanguageCode = app .ToReactivePropertyAsSynchronized(x => x.CurrentLanguageCode) .AddTo(disposable); CurrentLanguageCode.Subscribe(_ => app.CreateItem()).AddTo(disposable); SaveLocation = app .ObserveProperty(x => x.SaveLocation) .ToReadOnlyReactivePropertySlim() .AddTo(disposable); MessageBox = app .ObserveProperty(x => x.MessageBox) .Do(_ => MessageBox?.Value.Dispose()) .Select(x => new MessageBoxViewModel(x)) .ToReadOnlyReactivePropertySlim() .AddTo(disposable); OpenFileDialog = app .ObserveProperty(x => x.OpenFileDialog) .Do(_ => OpenFileDialog?.Value.Dispose()) .Select(x => new OpenFileDialogViewModel(x)) .ToReadOnlyReactivePropertySlim() .AddTo(disposable); SaveFileDialog = app .ObserveProperty(x => x.SaveFileDialog) .Do(_ => SaveFileDialog?.Value.Dispose()) .Select(x => new SaveFileDialogViewModel(x)) .ToReadOnlyReactivePropertySlim() .AddTo(disposable); CurrentTrainFileType = app .ToReactivePropertyAsSynchronized(x => x.CurrentTrainFileType) .AddTo(disposable); CurrentPanelFileType = app .ToReactivePropertyAsSynchronized(x => x.CurrentPanelFileType) .AddTo(disposable); CurrentSoundFileType = app .ToReactivePropertyAsSynchronized(x => x.CurrentSoundFileType) .AddTo(disposable); TrainDatImportLocation = app .ToReactivePropertyAsSynchronized( x => x.TrainDatImportLocation, ignoreValidationErrorValue: true ) .SetValidateNotifyError(x => !string.IsNullOrEmpty(x) && !File.Exists(x) ? @"指定されたファイルは存在しません。" : null) .AddTo(disposable); TrainDatExportLocation = app .ToReactivePropertyAsSynchronized( x => x.TrainDatExportLocation, ignoreValidationErrorValue: true ) .SetValidateNotifyError(x => !string.IsNullOrEmpty(x) && x.IndexOfAny(Path.GetInvalidPathChars()) >= 0 ? @"ファイル名に使用できない文字が使われています。" : null) .AddTo(disposable); ExtensionsCfgImportLocation = app .ToReactivePropertyAsSynchronized( x => x.ExtensionsCfgImportLocation, ignoreValidationErrorValue: true ) .SetValidateNotifyError(x => !string.IsNullOrEmpty(x) && !File.Exists(x) ? @"指定されたファイルは存在しません。" : null) .AddTo(disposable); ExtensionsCfgExportLocation = app .ToReactivePropertyAsSynchronized( x => x.ExtensionsCfgExportLocation, ignoreValidationErrorValue: true ) .SetValidateNotifyError(x => !string.IsNullOrEmpty(x) && x.IndexOfAny(Path.GetInvalidPathChars()) >= 0 ? @"ファイル名に使用できない文字が使われています。" : null) .AddTo(disposable); Panel2CfgImportLocation = app .ToReactivePropertyAsSynchronized( x => x.Panel2CfgImportLocation, ignoreValidationErrorValue: true ) .SetValidateNotifyError(x => !string.IsNullOrEmpty(x) && !File.Exists(x) ? @"指定されたファイルは存在しません。" : null) .AddTo(disposable); Panel2CfgExportLocation = app .ToReactivePropertyAsSynchronized( x => x.Panel2CfgExportLocation, ignoreValidationErrorValue: true ) .SetValidateNotifyError(x => !string.IsNullOrEmpty(x) && x.IndexOfAny(Path.GetInvalidPathChars()) >= 0 ? @"ファイル名に使用できない文字が使われています。" : null) .AddTo(disposable); PanelXmlImportLocation = app .ToReactivePropertyAsSynchronized( x => x.PanelXmlImportLocation, ignoreValidationErrorValue: true ) .SetValidateNotifyError(x => !string.IsNullOrEmpty(x) && !File.Exists(x) ? @"指定されたファイルは存在しません。" : null) .AddTo(disposable); PanelXmlExportLocation = app .ToReactivePropertyAsSynchronized( x => x.PanelXmlExportLocation, ignoreValidationErrorValue: true ) .SetValidateNotifyError(x => !string.IsNullOrEmpty(x) && x.IndexOfAny(Path.GetInvalidPathChars()) >= 0 ? @"ファイル名に使用できない文字が使われています。" : null) .AddTo(disposable); TrainFolderImportLocation = app .ToReactivePropertyAsSynchronized( x => x.TrainFolderImportLocation, ignoreValidationErrorValue: true ) .SetValidateNotifyError(x => !string.IsNullOrEmpty(x) && !Directory.Exists(x) ? @"指定されたフォルダは存在しません。" : null) .AddTo(disposable); SoundCfgImportLocation = app .ToReactivePropertyAsSynchronized( x => x.SoundCfgImportLocation, ignoreValidationErrorValue: true ) .SetValidateNotifyError(x => !string.IsNullOrEmpty(x) && !File.Exists(x) ? @"指定されたファイルは存在しません。" : null) .AddTo(disposable); SoundCfgExportLocation = app .ToReactivePropertyAsSynchronized( x => x.SoundCfgExportLocation, ignoreValidationErrorValue: true ) .SetValidateNotifyError(x => !string.IsNullOrEmpty(x) && x.IndexOfAny(Path.GetInvalidPathChars()) >= 0 ? @"ファイル名に使用できない文字が使われています。" : null) .AddTo(disposable); SoundXmlImportLocation = app .ToReactivePropertyAsSynchronized( x => x.SoundXmlImportLocation, ignoreValidationErrorValue: true ) .SetValidateNotifyError(x => !string.IsNullOrEmpty(x) && !File.Exists(x) ? @"指定されたファイルは存在しません。" : null) .AddTo(disposable); SoundXmlExportLocation = app .ToReactivePropertyAsSynchronized( x => x.SoundXmlExportLocation, ignoreValidationErrorValue: true ) .SetValidateNotifyError(x => !string.IsNullOrEmpty(x) && x.IndexOfAny(Path.GetInvalidPathChars()) >= 0 ? @"ファイル名に使用できない文字が使われています。" : null) .AddTo(disposable); Train = app .ObserveProperty(x => x.Train) .Do(_ => Train?.Value.Dispose()) .Select(x => new TrainViewModel(x, app)) .ToReadOnlyReactivePropertySlim() .AddTo(disposable); Panel = app .ObserveProperty(x => x.Panel) .Do(_ => Panel?.Value.Dispose()) .Select(x => new PanelViewModel(x)) .ToReadOnlyReactivePropertySlim() .AddTo(disposable); Sound = app .ObserveProperty(x => x.Sound) .Do(_ => Sound?.Value.Dispose()) .Select(x => new SoundViewModel(x)) .ToReadOnlyReactivePropertySlim() .AddTo(disposable); Item = app .ObserveProperty(x => x.Item) .Do(_ => Item?.Value.Dispose()) .Select(x => new TreeViewItemViewModel(x)) .ToReactiveProperty() .AddTo(disposable); Item.Subscribe(x => { itemDisposable.Dispose(); itemDisposable = new CompositeDisposable(); x.PropertyChangedAsObservable() .Subscribe(_ => Item.ForceNotify()) .AddTo(itemDisposable); }) .AddTo(disposable); SelectedItem = app .ToReactivePropertyAsSynchronized( x => x.SelectedItem, x => Item.Value.SearchViewModel(x), x => x?.Model ) .AddTo(disposable); IsVisibleInfo = new BooleanNotifier(); IsVisibleWarning = new BooleanNotifier(); IsVisibleError = new BooleanNotifier(); IsVisibleCritical = new BooleanNotifier(); VisibleLogMessages = app.VisibleLogMessages .ToReadOnlyReactiveCollection(x => new ListViewItemViewModel(x)) .AddTo(disposable); Interface.LogMessages .ObserveAddChanged() .Where(x => { BooleanNotifier notifier; switch (x.Type) { case MessageType.Information: notifier = IsVisibleInfo; break; case MessageType.Warning: notifier = IsVisibleWarning; break; case MessageType.Error: notifier = IsVisibleError; break; case MessageType.Critical: notifier = IsVisibleCritical; break; default: throw new ArgumentOutOfRangeException(); } return(notifier.Value); }) .Subscribe(app.AddLogMessage) .AddTo(disposable); Interface.LogMessages .ObserveRemoveChanged() .Subscribe(app.RemoveLogMessage) .AddTo(disposable); Interface.LogMessages .ObserveResetChanged() .Subscribe(_ => app.ResetLogMessages()) .AddTo(disposable); CreateNewFile = new ReactiveCommand() .WithSubscribe(app.CreateNewFile) .AddTo(disposable); OpenFile = new ReactiveCommand() .WithSubscribe(app.OpenFile) .AddTo(disposable); SaveFile = SaveLocation .Select(x => !string.IsNullOrEmpty(x)) .ToReactiveCommand() .WithSubscribe(app.SaveFile) .AddTo(disposable); SaveAsFile = new ReactiveCommand() .WithSubscribe(app.SaveAsFile) .AddTo(disposable); ImportFiles = new[] { TrainDatImportLocation.ObserveHasErrors, ExtensionsCfgImportLocation.ObserveHasErrors, Panel2CfgImportLocation.ObserveHasErrors, PanelXmlImportLocation.ObserveHasErrors, TrainFolderImportLocation.ObserveHasErrors, SoundCfgImportLocation.ObserveHasErrors, SoundXmlImportLocation.ObserveHasErrors } .CombineLatestValuesAreAllFalse() .ToReactiveCommand() .WithSubscribe(app.ImportFiles) .AddTo(disposable); ExportFiles = new[] { TrainDatExportLocation.ObserveHasErrors, ExtensionsCfgExportLocation.ObserveHasErrors, Panel2CfgExportLocation.ObserveHasErrors, PanelXmlExportLocation.ObserveHasErrors, SoundCfgExportLocation.ObserveHasErrors, SoundXmlExportLocation.ObserveHasErrors } .CombineLatestValuesAreAllFalse() .ToReactiveCommand() .WithSubscribe(app.ExportFiles) .AddTo(disposable); OutputLogs = new ReactiveCommand() .WithSubscribe(app.OutputLogs) .AddTo(disposable); UpCar = SelectedItem .Select(x => Item.Value.Children[1].Children.IndexOf(x) > 0) .ToReactiveCommand() .WithSubscribe(app.UpCar) .AddTo(disposable); DownCar = SelectedItem .Select(x => Item.Value.Children[1].Children.IndexOf(x)) .Select(x => x >= 0 && x < Item.Value.Children[1].Children.Count - 1) .ToReactiveCommand() .WithSubscribe(app.DownCar) .AddTo(disposable); AddCar = SelectedItem .Select(x => x == Item.Value.Children[1] || Item.Value.Children[1].Children.Contains(x)) .ToReactiveCommand() .WithSubscribe(app.AddCar) .AddTo(disposable); RemoveCar = SelectedItem .Select(x => Item.Value.Children[1].Children.Contains(x) && Item.Value.Children[1].Children.Where(y => y != x).Any(y => y.Tag.Value is MotorCar)) .ToReactiveCommand() .WithSubscribe(app.RemoveCar) .AddTo(disposable); CopyCar = SelectedItem .Select(x => Item.Value.Children[1].Children.Contains(x)) .ToReactiveCommand() .WithSubscribe(app.CopyCar) .AddTo(disposable); UpCoupler = SelectedItem .Select(x => Item.Value.Children[2].Children.IndexOf(x) > 0) .ToReactiveCommand() .WithSubscribe(app.UpCoupler) .AddTo(disposable); DownCoupler = SelectedItem .Select(x => Item.Value.Children[2].Children.IndexOf(x)) .Select(x => x >= 0 && x < Item.Value.Children[2].Children.Count - 1) .ToReactiveCommand() .WithSubscribe(app.DownCoupler) .AddTo(disposable); ChangeCarClass = SelectedItem .Select(x => x?.Tag.Value is TrailerCar || Item.Value.Children[1].Children.Count(y => y?.Tag.Value is MotorCar) > 1) .ToReactiveCommand() .WithSubscribe(() => app.ChangeCarClass(app.Train.Cars.IndexOf((Car)SelectedItem.Value.Tag.Value))) .AddTo(disposable); ChangeVisibleLogMessages = new ReactiveCommand <MessageType>() .WithSubscribe(x => { BooleanNotifier notifier; switch (x) { case MessageType.Information: notifier = IsVisibleInfo; break; case MessageType.Warning: notifier = IsVisibleWarning; break; case MessageType.Error: notifier = IsVisibleError; break; case MessageType.Critical: notifier = IsVisibleCritical; break; default: throw new ArgumentOutOfRangeException(nameof(x), x, null); } app.ChangeVisibleLogMessages(x, !notifier.Value); notifier.SwitchValue(); }) .AddTo(disposable); ClearLogMessages = new ReactiveCommand() .WithSubscribe(Interface.LogMessages.Clear) .AddTo(disposable); itemDisposable.AddTo(disposable); }