Exemplo n.º 1
0
 public OperationViewModel(OperationConfig oc)
 {
     _operationConfig               = oc;
     UpdateIntervalOfThumbnails     = ReactiveProperty.FromObject(oc, w => w.UpdateIntervalOfThumbnails);
     ReceptionIntervalOfComments    = ReactiveProperty.FromObject(oc, w => w.ReceptionIntervalOfComments);
     SamplingIntervalOfProgramState = ReactiveProperty.FromObject(oc, w => w.SamplingIntervalOfProgramState);
     NumberOfHoldingComments        = ReactiveProperty.FromObject(oc, w => w.NumberOfHoldingComments);
     PostKey = ReactiveProperty.FromObject(oc, w => w.PostKeyType, x => new EnumWrap <PostKey>(x),
                                           w => w.EnumValue);
     ToastNotificationBeforeMinutes = ReactiveProperty.FromObject(oc, w => w.ToastNotificationBeforeMinutes);
     Branch             = ReactiveProperty.FromObject(oc, w => w.Branch, x => new EnumWrap <Branch>(x), w => w.EnumValue);
     IsAbsoluteTime     = ReactiveProperty.FromObject(oc, w => w.IsAbsoluteTime);
     IsShowFavoriteOnly = ReactiveProperty.FromObject(oc, w => w.IsShowFavoriteOnly);
     Delay        = ReactiveProperty.FromObject(oc, w => w.Delay);
     MuteKeywords = oc.MuteKeywords;
     IsRegex      = new ReactiveProperty <bool>(false);
     Keyword      = new ReactiveProperty <string>("")
                    .SetValidateNotifyError(x => IsRegex.Value ? _rgxValidator.Validate(Keyword.Value) : null);
     Keyword.Subscribe(w => AddMuteKeywordCommand.RaiseCanExecuteChanged()).AddTo(this);
     IsRegex.Subscribe(w =>
     {
         Keyword.ForceValidate();
         AddMuteKeywordCommand.RaiseCanExecuteChanged();
     }).AddTo(this);
     SelectedKeyword = new ReactiveProperty <MuteKeyword>();
     SelectedKeyword.Subscribe(w =>
     {
         EditMuteKeywordCommand.RaiseCanExecuteChanged();
         DeleteMuteKeywordCommand.RaiseCanExecuteChanged();
     }).AddTo(this);
     SelectedIndex = new ReactiveProperty <int>();
 }
Exemplo n.º 2
0
        public void IgnoreInitErrorAndForceValidate()
        {
            var rp = new ReactiveProperty <string>(mode: ReactivePropertyMode.Default | ReactivePropertyMode.IgnoreInitialValidationError)
                     .SetValidateNotifyError(x => string.IsNullOrEmpty(x) ? "error" : null);

            rp.HasErrors.IsFalse();
            rp.ForceValidate();
            rp.HasErrors.IsTrue();
        }
        public void ForceValidate()
        {
            var minValue = 0;
            var rp       = new ReactiveProperty <int>(0)
                           .SetValidateNotifyError(x => x < minValue ? "Error" : null);

            rp.GetErrors("Value").IsNull();

            minValue = 1;
            rp.GetErrors("Value").IsNull();

            rp.ForceValidate();
            rp.GetErrors("Value").OfType <string>().Is("Error");
        }
Exemplo n.º 4
0
        public MainWindowViewModel()
        {
            Input = new ReactiveProperty <string>()
                    .SetValidateAttribute(() => Input)
                    .SetValidateNotifyError(_ => _model.Input.GetErrors(nameof(ReactiveProperty <string> .Value)));
            _model.Input.ObserveErrorChanged.Subscribe(_ => Input.ForceValidate());

            ErrorMessage = Input.ObserveErrorChanged
                           .Select(x => x?.OfType <string>().FirstOrDefault())
                           .ToReadOnlyReactivePropertySlim();

            SubmitCommand = new ReactiveCommand()
                            .WithSubscribe(() =>
            {
                _model.Input.Value = Input.Value;
            });
        }
Exemplo n.º 5
0
        public MainViewModel(INavigationService navigationService)
        {
            _navigationService = navigationService;
            ReferenceNumber    = new ReactiveProperty <string>(mode: ReactivePropertyMode.Default | ReactivePropertyMode.IgnoreInitialValidationError)
                                 .SetValidateAttribute(() => ReferenceNumber);

            ReferenceNumberErrorMessage = ReferenceNumber.ObserveErrorChanged
                                          .Select(x => x?.OfType <string>().FirstOrDefault())
                                          .ToReactiveProperty();

            ReferenceNumberFormHasErrors = new[]
            {
                ReferenceNumber.ObserveHasErrors,
            }
            .CombineLatest(x => x.Any(y => y))
            .ToReactiveProperty();

            IsBusy   = new ReactiveProperty <bool>(false);
            BusyText = new ReactiveProperty <string>("loading..");


            // You can combine some ObserveHasErrors values.
            FormHasErrors = new[]
            {
                ReferenceNumber.ObserveHasErrors,
            }
            .CombineLatest(x => !x.Any(y => y))
            .ToReactiveProperty();

            SearchCommand = new[]
            {
                ReferenceNumber.ObserveHasErrors,
            }
            .CombineLatestValuesAreAllFalse()
            .ToReactiveCommand()
            .WithSubscribe(async() =>
            {
                await _navigationService.NavigateTo <BarcodeDetailsViewModel>(typeof(BarcodeDetailsViewModel), ReferenceNumber.Value);
            });

            ReferenceNumberUnfocusedCommand = new ReactiveCommand();
            ReferenceNumberUnfocusedCommand.Subscribe(_ => ReferenceNumber.ForceValidate());
        }
Exemplo n.º 6
0
        internal HandleViewModel(Handle handle, Train train)
        {
            HandleType = handle
                         .ToReactivePropertyAsSynchronized(x => x.HandleType)
                         .AddTo(disposable);

            PowerNotches = handle
                           .ToReactivePropertyAsSynchronized(x => x.PowerNotches, ignoreValidationErrorValue: true)
                           .AddTo(disposable);

            PowerNotches.Subscribe(_ => train.ApplyPowerNotchesToCar()).AddTo(disposable);

            BrakeNotches = handle
                           .ToReactivePropertyAsSynchronized(x => x.BrakeNotches, ignoreValidationErrorValue: true)
                           .SetValidateNotifyError(x =>
            {
                if (x == 0 && train.Device.HoldBrake)
                {
                    return("BrakeNotches must be at least 1 if HoldBrake is set.");
                }

                return(null);
            })
                           .AddTo(disposable);

            BrakeNotches.Subscribe(_ => train.ApplyBrakeNotchesToCar()).AddTo(disposable);

            PowerNotchReduceSteps = handle
                                    .ToReactivePropertyAsSynchronized(x => x.PowerNotchReduceSteps)
                                    .AddTo(disposable);

            HandleBehaviour = handle
                              .ToReactivePropertyAsSynchronized(x => x.HandleBehaviour)
                              .AddTo(disposable);

            LocoBrake = handle
                        .ToReactivePropertyAsSynchronized(x => x.LocoBrake)
                        .AddTo(disposable);

            LocoBrakeNotches = handle
                               .ToReactivePropertyAsSynchronized(x => x.LocoBrakeNotches)
                               .AddTo(disposable);

            LocoBrakeNotches.Subscribe(_ => train.ApplyLocoBrakeNotchesToCar()).AddTo(disposable);

            DriverPowerNotches = handle
                                 .ToReactivePropertyAsSynchronized(x => x.DriverPowerNotches, ignoreValidationErrorValue: true)
                                 .AddTo(disposable);

            DriverBrakeNotches = handle
                                 .ToReactivePropertyAsSynchronized(x => x.DriverBrakeNotches, ignoreValidationErrorValue: true)
                                 .AddTo(disposable);

            PowerNotches
            .SetValidateNotifyError(x =>
            {
                if (x < DriverPowerNotches.Value)
                {
                    return("DriverPowerNotches must be less than or equal to PowerNotches.");
                }

                return(null);
            })
            .Subscribe(_ => DriverPowerNotches.ForceValidate())
            .AddTo(disposable);

            PowerNotches
            .ObserveHasErrors
            .ToReadOnlyReactivePropertySlim(mode: ReactivePropertyMode.DistinctUntilChanged)
            .Where(x => !x)
            .Subscribe(_ => PowerNotches.ForceNotify())
            .AddTo(disposable);

            BrakeNotches
            .SetValidateNotifyError(x =>
            {
                if (x < DriverBrakeNotches.Value)
                {
                    return("DriverBrakeNotches must be less than or equal to BrakeNotches.");
                }

                return(null);
            })
            .Subscribe(_ => DriverBrakeNotches.ForceValidate())
            .AddTo(disposable);

            BrakeNotches
            .ObserveHasErrors
            .ToReadOnlyReactivePropertySlim(mode: ReactivePropertyMode.DistinctUntilChanged)
            .Where(x => !x)
            .Subscribe(_ => BrakeNotches.ForceNotify())
            .AddTo(disposable);

            DriverPowerNotches
            .SetValidateNotifyError(x =>
            {
                if (x > PowerNotches.Value)
                {
                    return("DriverPowerNotches must be less than or equal to PowerNotches.");
                }

                return(null);
            })
            .Subscribe(_ => PowerNotches.ForceValidate())
            .AddTo(disposable);

            DriverPowerNotches
            .ObserveHasErrors
            .ToReadOnlyReactivePropertySlim(mode: ReactivePropertyMode.DistinctUntilChanged)
            .Where(x => !x)
            .Subscribe(_ => DriverPowerNotches.ForceNotify())
            .AddTo(disposable);

            DriverBrakeNotches
            .SetValidateNotifyError(x =>
            {
                if (x > BrakeNotches.Value)
                {
                    return("DriverBrakeNotches must be less than or equal to BrakeNotches.");
                }

                return(null);
            })
            .Subscribe(_ => BrakeNotches.ForceValidate())
            .AddTo(disposable);

            DriverBrakeNotches
            .ObserveHasErrors
            .ToReadOnlyReactivePropertySlim(mode: ReactivePropertyMode.DistinctUntilChanged)
            .Where(x => !x)
            .Subscribe(_ => DriverBrakeNotches.ForceNotify())
            .AddTo(disposable);
        }
        public void ForceValidate()
        {
            var minValue = 0;
            var rp = new ReactiveProperty<int>(0)
                .SetValidateNotifyError(x => x < minValue ? "Error" : null);
            rp.GetErrors("Value").IsNull();

            minValue = 1;
            rp.GetErrors("Value").IsNull();

            rp.ForceValidate();
            rp.GetErrors("Value").OfType<string>().Is("Error");
        }
Exemplo n.º 8
0
        internal MotorViewModel(Motor motor)
        {
            CultureInfo culture = CultureInfo.InvariantCulture;

            MessageBox = motor
                         .ObserveProperty(x => x.MessageBox)
                         .Do(_ => MessageBox?.Value.Dispose())
                         .Select(x => new MessageBoxViewModel(x))
                         .ToReadOnlyReactivePropertySlim()
                         .AddTo(disposable);

            ToolTipVertexPitch = motor
                                 .ObserveProperty(x => x.ToolTipVertexPitch)
                                 .Do(_ => ToolTipVertexPitch?.Value.Dispose())
                                 .Select(x => new ToolTipViewModel(x))
                                 .ToReadOnlyReactivePropertySlim()
                                 .AddTo(disposable);

            ToolTipVertexVolume = motor
                                  .ObserveProperty(x => x.ToolTipVertexVolume)
                                  .Do(_ => ToolTipVertexVolume?.Value.Dispose())
                                  .Select(x => new ToolTipViewModel(x))
                                  .ToReadOnlyReactivePropertySlim()
                                  .AddTo(disposable);

            CurrentModifierKeys = motor
                                  .ToReactivePropertyAsSynchronized(x => x.CurrentModifierKeys)
                                  .AddTo(disposable);

            CurrentCursorType = motor
                                .ObserveProperty(x => x.CurrentCursorType)
                                .ToReadOnlyReactivePropertySlim()
                                .AddTo(disposable);

            MinVelocity = motor
                          .ToReactivePropertyAsSynchronized(
                x => x.MinVelocity,
                x => x.ToString(culture),
                x => double.Parse(x, NumberStyles.Float, culture),
                ignoreValidationErrorValue: true
                )
                          .AddTo(disposable);

            MinVelocity.Subscribe(_ => motor.IsRefreshGlControl = true).AddTo(disposable);

            MaxVelocity = motor
                          .ToReactivePropertyAsSynchronized(
                x => x.MaxVelocity,
                x => x.ToString(culture),
                x => double.Parse(x, NumberStyles.Float, culture),
                ignoreValidationErrorValue: true
                )
                          .AddTo(disposable);

            MaxVelocity.Subscribe(_ => motor.IsRefreshGlControl = true).AddTo(disposable);

            MinPitch = motor
                       .ToReactivePropertyAsSynchronized(
                x => x.MinPitch,
                x => x.ToString(culture),
                x => double.Parse(x, NumberStyles.Float, culture),
                ignoreValidationErrorValue: true
                )
                       .AddTo(disposable);

            MinPitch.Subscribe(_ => motor.IsRefreshGlControl = true).AddTo(disposable);

            MaxPitch = motor
                       .ToReactivePropertyAsSynchronized(
                x => x.MaxPitch,
                x => x.ToString(culture),
                x => double.Parse(x, NumberStyles.Float, culture),
                ignoreValidationErrorValue: true
                )
                       .AddTo(disposable);

            MaxPitch.Subscribe(_ => motor.IsRefreshGlControl = true).AddTo(disposable);

            MinVolume = motor
                        .ToReactivePropertyAsSynchronized(
                x => x.MinVolume,
                x => x.ToString(culture),
                x => double.Parse(x, NumberStyles.Float, culture),
                ignoreValidationErrorValue: true
                )
                        .AddTo(disposable);

            MinVolume.Subscribe(_ => motor.IsRefreshGlControl = true).AddTo(disposable);

            MaxVolume = motor
                        .ToReactivePropertyAsSynchronized(
                x => x.MaxVolume,
                x => x.ToString(culture),
                x => double.Parse(x, NumberStyles.Float, culture),
                ignoreValidationErrorValue: true
                )
                        .AddTo(disposable);

            MaxVolume.Subscribe(_ => motor.IsRefreshGlControl = true).AddTo(disposable);

            NowVelocity = motor
                          .ObserveProperty(x => x.NowVelocity)
                          .ToReadOnlyReactivePropertySlim()
                          .AddTo(disposable);

            NowPitch = motor
                       .ObserveProperty(x => x.NowPitch)
                       .ToReadOnlyReactivePropertySlim()
                       .AddTo(disposable);

            NowVolume = motor
                        .ObserveProperty(x => x.NowVolume)
                        .ToReadOnlyReactivePropertySlim()
                        .AddTo(disposable);

            CurrentSelectedTrack = motor
                                   .ObserveProperty(x => x.SelectedTrackInfo)
                                   .ToReadOnlyReactivePropertySlim()
                                   .AddTo(disposable);

            CurrentSelectedTrack
            .Subscribe(_ =>
            {
                motor.ResetSelect();
                motor.IsRefreshGlControl = true;
            })
            .AddTo(disposable);

            CurrentInputMode = motor
                               .ObserveProperty(x => x.CurrentInputMode)
                               .ToReadOnlyReactivePropertySlim()
                               .AddTo(disposable);

            CurrentInputMode
            .Subscribe(_ =>
            {
                motor.ResetSelect();
                motor.IsRefreshGlControl = true;
            })
            .AddTo(disposable);

            SelectedSoundIndex = motor
                                 .ToReactivePropertyAsSynchronized(x => x.SelectedSoundIndex)
                                 .AddTo(disposable);

            CurrentToolMode = motor
                              .ObserveProperty(x => x.CurrentToolMode)
                              .ToReadOnlyReactivePropertySlim()
                              .AddTo(disposable);

            CurrentToolMode
            .Subscribe(_ =>
            {
                motor.ResetSelect();
                motor.IsRefreshGlControl = true;
            })
            .AddTo(disposable);

            StoppedSim = motor
                         .ObserveProperty(x => x.CurrentSimState)
                         .Select(x => x == Motor.SimulationState.Disable || x == Motor.SimulationState.Stopped)
                         .ToReadOnlyReactivePropertySlim()
                         .AddTo(disposable);

            RunIndex = motor
                       .ToReactivePropertyAsSynchronized(x => x.RunIndex)
                       .AddTo(disposable);

            IsPlayTrack1 = motor
                           .ToReactivePropertyAsSynchronized(x => x.IsPlayTrack1)
                           .AddTo(disposable);

            IsPlayTrack2 = motor
                           .ToReactivePropertyAsSynchronized(x => x.IsPlayTrack2)
                           .AddTo(disposable);

            IsLoop = motor
                     .ToReactivePropertyAsSynchronized(x => x.IsLoop)
                     .AddTo(disposable);

            IsConstant = motor
                         .ToReactivePropertyAsSynchronized(x => x.IsConstant)
                         .AddTo(disposable);

            Acceleration = motor
                           .ToReactivePropertyAsSynchronized(
                x => x.Acceleration,
                x => x.ToString(culture),
                x => double.Parse(x, NumberStyles.Float, culture),
                ignoreValidationErrorValue: true
                )
                           .SetValidateNotifyError(x =>
            {
                double result;
                string message;

                Utilities.TryParse(x, NumberRange.NonNegative, out result, out message);

                return(message);
            })
                           .AddTo(disposable);

            StartSpeed = motor
                         .ToReactivePropertyAsSynchronized(
                x => x.StartSpeed,
                x => x.ToString(culture),
                x => double.Parse(x, NumberStyles.Float, culture),
                ignoreValidationErrorValue: true
                )
                         .SetValidateNotifyError(x =>
            {
                double result;
                string message;

                Utilities.TryParse(x, NumberRange.NonNegative, out result, out message);

                return(message);
            })
                         .AddTo(disposable);

            EndSpeed = motor
                       .ToReactivePropertyAsSynchronized(
                x => x.EndSpeed,
                x => x.ToString(culture),
                x => double.Parse(x, NumberStyles.Float, culture),
                ignoreValidationErrorValue: true
                )
                       .SetValidateNotifyError(x =>
            {
                double result;
                string message;

                Utilities.TryParse(x, NumberRange.NonNegative, out result, out message);

                return(message);
            })
                       .AddTo(disposable);

            EnabledDirect = new[]
            {
                CurrentInputMode.Select(x => x != Motor.InputMode.SoundIndex),
                StoppedSim
            }
            .CombineLatestValuesAreAllTrue()
            .ToReadOnlyReactivePropertySlim()
            .AddTo(disposable);

            DirectX = new ReactiveProperty <string>(0.0.ToString(culture))
                      .SetValidateNotifyError(x =>
            {
                double result;
                string message;

                switch (CurrentToolMode.Value)
                {
                case Motor.ToolMode.Move:
                    Utilities.TryParse(x, NumberRange.Any, out result, out message);
                    break;

                case Motor.ToolMode.Dot:
                    Utilities.TryParse(x, NumberRange.NonNegative, out result, out message);
                    break;

                default:
                    message = null;
                    break;
                }

                return(message);
            })
                      .AddTo(disposable);

            DirectY = new ReactiveProperty <string>(0.0.ToString(culture))
                      .SetValidateNotifyError(x =>
            {
                double result;
                string message;

                switch (CurrentToolMode.Value)
                {
                case Motor.ToolMode.Move:
                    Utilities.TryParse(x, NumberRange.Any, out result, out message);
                    break;

                case Motor.ToolMode.Dot:
                    Utilities.TryParse(x, NumberRange.NonNegative, out result, out message);
                    break;

                default:
                    message = null;
                    break;
                }

                return(message);
            })
                      .AddTo(disposable);

            CurrentToolMode
            .Subscribe(_ =>
            {
                DirectX.ForceValidate();
                DirectY.ForceValidate();
            })
            .AddTo(disposable);

            GlControlWidth = motor
                             .ToReactivePropertyAsSynchronized(
                x => x.GlControlWidth,
                ignoreValidationErrorValue: true
                )
                             .SetValidateNotifyError(x => x <= 0 ? string.Empty : null)
                             .AddTo(disposable);

            GlControlWidth.Subscribe(_ => motor.IsRefreshGlControl = true).AddTo(disposable);

            GlControlHeight = motor
                              .ToReactivePropertyAsSynchronized(
                x => x.GlControlHeight,
                ignoreValidationErrorValue: true
                )
                              .SetValidateNotifyError(x => x <= 0 ? string.Empty : null)
                              .AddTo(disposable);

            GlControlHeight.Subscribe(_ => motor.IsRefreshGlControl = true).AddTo(disposable);

            IsRefreshGlControl = motor
                                 .ToReactivePropertyAsSynchronized(x => x.IsRefreshGlControl)
                                 .AddTo(disposable);

            ChangeSelectedTrack = new ReactiveCommand <Motor.TrackInfo>().WithSubscribe(x => motor.SelectedTrackInfo = x).AddTo(disposable);

            ChangeInputMode = new ReactiveCommand <Motor.InputMode>().WithSubscribe(x => motor.CurrentInputMode = x).AddTo(disposable);

            ChangeToolMode = new[]
            {
                CurrentInputMode.Select(x => x != Motor.InputMode.SoundIndex),
                StoppedSim
            }
            .CombineLatestValuesAreAllTrue()
            .ToReactiveCommand <Motor.ToolMode>()
            .WithSubscribe(x => motor.CurrentToolMode = x)
            .AddTo(disposable);

            ZoomIn = new ReactiveCommand().WithSubscribe(motor.ZoomIn).AddTo(disposable);

            ZoomOut = new ReactiveCommand().WithSubscribe(motor.ZoomOut).AddTo(disposable);

            Reset = new ReactiveCommand().WithSubscribe(motor.Reset).AddTo(disposable);

            MoveLeft = new ReactiveCommand().WithSubscribe(motor.MoveLeft).AddTo(disposable);

            MoveRight = new ReactiveCommand().WithSubscribe(motor.MoveRight).AddTo(disposable);

            MoveBottom = new ReactiveCommand().WithSubscribe(motor.MoveBottom).AddTo(disposable);

            MoveTop = new ReactiveCommand().WithSubscribe(motor.MoveTop).AddTo(disposable);

            Undo = new[]
            {
                new[]
                {
                    motor.PropertyChangedAsObservable().Where(x => x.PropertyName == nameof(motor.SelectedTrackInfo)).OfType <object>(),
                    motor.PrevTrackStates.CollectionChangedAsObservable().OfType <object>()
                }
                .Merge()
                .Select(_ => motor.PrevTrackStates.Any(x => x.Info == motor.SelectedTrackInfo)),
                StoppedSim
            }
            .CombineLatestValuesAreAllTrue()
            .ToReactiveCommand(false)
            .WithSubscribe(motor.Undo)
            .AddTo(disposable);

            Redo = new[]
            {
                new[]
                {
                    motor.PropertyChangedAsObservable().Where(x => x.PropertyName == nameof(motor.SelectedTrackInfo)).OfType <object>(),
                    motor.NextTrackStates.CollectionChangedAsObservable().OfType <object>()
                }
                .Merge()
                .Select(_ => motor.NextTrackStates.Any(x => x.Info == motor.SelectedTrackInfo)),
                StoppedSim
            }
            .CombineLatestValuesAreAllTrue()
            .ToReactiveCommand(false)
            .WithSubscribe(motor.Redo)
            .AddTo(disposable);

            TearingOff = StoppedSim
                         .ToReactiveCommand()
                         .WithSubscribe(motor.TearingOff)
                         .AddTo(disposable);

            Copy = StoppedSim
                   .ToReactiveCommand()
                   .WithSubscribe(motor.Copy)
                   .AddTo(disposable);

            Paste = new[]
            {
                motor.ObserveProperty(x => x.CopyTrack).Select(x => x != null),
                StoppedSim
            }
            .CombineLatestValuesAreAllTrue()
            .ToReactiveCommand()
            .WithSubscribe(motor.Paste)
            .AddTo(disposable);

            Cleanup = StoppedSim
                      .ToReactiveCommand()
                      .WithSubscribe(motor.Cleanup)
                      .AddTo(disposable);

            Delete = StoppedSim
                     .ToReactiveCommand()
                     .WithSubscribe(motor.Delete)
                     .AddTo(disposable);

            MouseDown = new ReactiveCommand <InputEventModel.EventArgs>().WithSubscribe(motor.MouseDown).AddTo(disposable);

            MouseMove = new ReactiveCommand <InputEventModel.EventArgs>().WithSubscribe(motor.MouseMove).AddTo(disposable);

            MouseUp = new ReactiveCommand().WithSubscribe(motor.MouseUp).AddTo(disposable);

            DirectDot = new[]
            {
                StoppedSim,
                CurrentToolMode.Select(x => x == Motor.ToolMode.Dot),
                DirectX.ObserveHasErrors.Select(x => !x),
                DirectY.ObserveHasErrors.Select(x => !x)
            }
            .CombineLatestValuesAreAllTrue()
            .ToReactiveCommand()
            .WithSubscribe(() => motor.DirectDot(double.Parse(DirectX.Value), double.Parse(DirectY.Value)))
            .AddTo(disposable);

            DirectMove = new[]
            {
                StoppedSim,
                CurrentToolMode.Select(x => x == Motor.ToolMode.Move),
                DirectX.ObserveHasErrors.Select(x => !x),
                DirectY.ObserveHasErrors.Select(x => !x)
            }
            .CombineLatestValuesAreAllTrue()
            .ToReactiveCommand()
            .WithSubscribe(() => motor.DirectMove(double.Parse(DirectX.Value), double.Parse(DirectY.Value)))
            .AddTo(disposable);

            SwapSpeed = StoppedSim
                        .ToReactiveCommand()
                        .WithSubscribe(() =>
            {
                string tmp       = StartSpeed.Value;
                StartSpeed.Value = EndSpeed.Value;
                EndSpeed.Value   = tmp;
            })
                        .AddTo(disposable);

            SimulationTimer = new ReactiveTimer(TimeSpan.FromMilliseconds(1000.0 / 30.0));

            SimulationTimer
            .Subscribe(_ =>
            {
                SimulationTimer.Stop();
                Observable.Start(motor.RunSimulation, UIDispatcherScheduler.Default).Wait();
                SimulationTimer.Start(TimeSpan.FromMilliseconds(1000.0 / 30.0));
            })
            .AddTo(disposable);

            motor.ObserveProperty(x => x.CurrentSimState)
            .ToReadOnlyReactivePropertySlim()
            .Subscribe(x =>
            {
                switch (x)
                {
                case Motor.SimulationState.Started:
                    SimulationTimer.Start();
                    break;

                default:
                    SimulationTimer.Stop();
                    break;
                }
            })
            .AddTo(disposable);

            StartSimulation = motor
                              .ObserveProperty(x => x.CurrentSimState)
                              .Select(x => x == Motor.SimulationState.Paused || x == Motor.SimulationState.Stopped)
                              .ToReactiveCommand()
                              .WithSubscribe(motor.StartSimulation)
                              .AddTo(disposable);

            PauseSimulation = motor
                              .ObserveProperty(x => x.CurrentSimState)
                              .Select(x => x == Motor.SimulationState.Started)
                              .ToReactiveCommand()
                              .WithSubscribe(motor.PauseSimulation)
                              .AddTo(disposable);

            StopSimulation = motor
                             .ObserveProperty(x => x.CurrentSimState)
                             .Select(x => x == Motor.SimulationState.Paused || x == Motor.SimulationState.Started)
                             .ToReactiveCommand()
                             .WithSubscribe(motor.StopSimulation)
                             .AddTo(disposable);

            DrawGlControl = new ReactiveCommand()
                            .WithSubscribe(motor.DrawGlControl)
                            .AddTo(disposable);

            MinVelocity
            .SetValidateNotifyError(x =>
            {
                double min;
                string message;

                if (Utilities.TryParse(x, NumberRange.NonNegative, out min, out message))
                {
                    double max;

                    if (Utilities.TryParse(MaxVelocity.Value, NumberRange.NonNegative, out max) && min >= max)
                    {
                        message = "MinはMax未満でなければなりません。";
                    }
                }

                return(message);
            })
            .Subscribe(_ => MaxVelocity.ForceValidate())
            .AddTo(disposable);

            MinVelocity.ObserveHasErrors
            .ToReadOnlyReactivePropertySlim(mode: ReactivePropertyMode.DistinctUntilChanged)
            .Where(x => !x)
            .Subscribe(_ => MinVelocity.ForceNotify())
            .AddTo(disposable);

            MaxVelocity
            .SetValidateNotifyError(x =>
            {
                double max;
                string message;

                if (Utilities.TryParse(x, NumberRange.NonNegative, out max, out message))
                {
                    double min;

                    if (Utilities.TryParse(MinVelocity.Value, NumberRange.NonNegative, out min) && max <= min)
                    {
                        message = "MinはMax未満でなければなりません。";
                    }
                }

                return(message);
            })
            .Subscribe(_ => MinVelocity.ForceValidate())
            .AddTo(disposable);

            MaxVelocity.ObserveHasErrors
            .ToReadOnlyReactivePropertySlim(mode: ReactivePropertyMode.DistinctUntilChanged)
            .Where(x => !x)
            .Subscribe(_ => MaxVelocity.ForceNotify())
            .AddTo(disposable);

            MinPitch
            .SetValidateNotifyError(x =>
            {
                double min;
                string message;

                if (Utilities.TryParse(x, NumberRange.NonNegative, out min, out message))
                {
                    double max;

                    if (Utilities.TryParse(MaxPitch.Value, NumberRange.NonNegative, out max) && min >= max)
                    {
                        message = "MinはMax未満でなければなりません。";
                    }
                }

                return(message);
            })
            .Subscribe(_ => MaxPitch.ForceValidate())
            .AddTo(disposable);

            MinPitch.ObserveHasErrors
            .ToReadOnlyReactivePropertySlim(mode: ReactivePropertyMode.DistinctUntilChanged)
            .Where(x => !x)
            .Subscribe(_ => MinPitch.ForceNotify())
            .AddTo(disposable);

            MaxPitch
            .SetValidateNotifyError(x =>
            {
                double max;
                string message;

                if (Utilities.TryParse(x, NumberRange.NonNegative, out max, out message))
                {
                    double min;

                    if (Utilities.TryParse(MinPitch.Value, NumberRange.NonNegative, out min) && max <= min)
                    {
                        message = "MinはMax未満でなければなりません。";
                    }
                }

                return(message);
            })
            .Subscribe(_ => MinPitch.ForceValidate())
            .AddTo(disposable);

            MaxPitch.ObserveHasErrors
            .ToReadOnlyReactivePropertySlim(mode: ReactivePropertyMode.DistinctUntilChanged)
            .Where(x => !x)
            .Subscribe(_ => MaxPitch.ForceNotify())
            .AddTo(disposable);

            MinVolume
            .SetValidateNotifyError(x =>
            {
                double min;
                string message;

                if (Utilities.TryParse(x, NumberRange.NonNegative, out min, out message))
                {
                    double max;

                    if (Utilities.TryParse(MaxVolume.Value, NumberRange.NonNegative, out max) && min >= max)
                    {
                        message = "MinはMax未満でなければなりません。";
                    }
                }

                return(message);
            })
            .Subscribe(_ => MaxVolume.ForceValidate())
            .AddTo(disposable);

            MinVolume.ObserveHasErrors
            .ToReadOnlyReactivePropertySlim(mode: ReactivePropertyMode.DistinctUntilChanged)
            .Where(x => !x)
            .Subscribe(_ => MinVolume.ForceNotify())
            .AddTo(disposable);

            MaxVolume
            .SetValidateNotifyError(x =>
            {
                double max;
                string message;

                if (Utilities.TryParse(x, NumberRange.NonNegative, out max, out message))
                {
                    double min;

                    if (Utilities.TryParse(MinVolume.Value, NumberRange.NonNegative, out min) && max <= min)
                    {
                        message = "MinはMax未満でなければなりません。";
                    }
                }

                return(message);
            })
            .Subscribe(_ => MinVolume.ForceValidate())
            .AddTo(disposable);

            MaxVolume.ObserveHasErrors
            .ToReadOnlyReactivePropertySlim(mode: ReactivePropertyMode.DistinctUntilChanged)
            .Where(x => !x)
            .Subscribe(_ => MaxVolume.ForceNotify())
            .AddTo(disposable);
        }