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);
    }
Example #3
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);
        }
Example #4
0
        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);
        }
Example #5
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 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);
        }
Example #10
0
        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);
        }
Example #12
0
        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
        }
Example #13
0
 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);
        }
Example #16
0
        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);
        }