/// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="settings"></param>
        /// <param name="logger"></param>
        public RecipeDetailViewModel(ISettings settings, ILogger logger)
        {
            // Property

            // 作成したレシピ
            this.DecidedRecipe =
                this.RecipeViewModel
                .CombineLatest(
                    // ダウンロード完了後に値をセットする
                    this.IsDownaloding,
                    (recipe, isDownloading) => isDownloading ? null : recipe)
                .ToReactiveProperty()
                .AddTo(this.CompositeDisposable);

            this.TargetUrl =
                new ReactiveProperty <string>()
                .SetValidateNotifyError(x => x != null ? null : "必須")
                .SetValidateNotifyError(x => Creator.CanCreateFromUrl(x) ? null : "対応サイトのURLではありません。")
                .AddTo(this.CompositeDisposable);

            this.Sites.Value = Creator.RecipeSitePlugins.Select(x => new Site {
                Logo             = x.LogoUrl,
                TargetUrlPattern = x.TargetUrlPattern,
                IsValid          = this.TargetUrl.Select(url => url != null && (x.TargetUrlPattern?.IsMatch(url) ?? false)).ToReactiveProperty()
            }).ToArray();


            this.RecipeView =
                this.RecipeViewModel
                .CombineLatest(this.IsEditing, (vm, isEditing) => {
                if (isEditing)
                {
                    return(Creator.CreateRecipeEditorViewInstance(settings, logger, vm));
                }
                else
                {
                    return(Creator.CreateRecipeViewerViewInstance(settings, logger, vm));
                }
            })
                .ToReactiveProperty()
                .AddTo(this.CompositeDisposable);
            // Command

            // レシピダウンロードコマンド
            this.DownloadCommand =
                new[] {
                this.TargetUrl.ObserveHasErrors.Select(x => !x),
                this.IsDownaloding.Select(x => !x),
                this.RecipeViewModel.Select(x => x == null)
            }.CombineLatestValuesAreAllTrue()
            .ToReactiveCommand()
            .AddTo(this.CompositeDisposable);
            this.DownloadCommand.Subscribe(url => {
                this.IsDownaloding.Value = true;
                var vm = Creator.CreateRecipeViewModelInstanceFromUrl(settings, logger, this.TargetUrl.Value);
                this.RecipeViewModel.Value = vm;
                vm.Url.Value = new Uri(this.TargetUrl.Value);

                vm.Recipe.FailedNotification.Where(x => x.Behavior == Composition.Behavior.Download).Subscribe(x => {
                    this.RecipeViewModel.Value = null;
                    this.IsDownaloding.Value   = false;
                });
                vm.Recipe.CompletedNotification.Where(x => x.Behavior == Composition.Behavior.Download).Subscribe(x => {
                    this.IsDownaloding.Value = false;
                });
                vm.DownloadCommand.Execute(this.Messenger);
            }).AddTo(this.CompositeDisposable);

            // レシピ登録コマンド
            this.RegisterRecipeCommand =
                new[] {
                this.IsDownaloding.Select(x => !x),
                this.RecipeViewModel.Select(x => x != null),
                this.IsEditing.Select(x => !x)
            }.CombineLatestValuesAreAllTrue()
            .ToReactiveCommand()
            .AddTo(this.CompositeDisposable);
            this.RegisterRecipeCommand.ObserveOnDispatcher(DispatcherPriority.Background).Subscribe(_ => {
                this.IsBusy.Value = true;
                this.RecipeViewModel.Value.RegisterRecipeCommand.Execute(this.Messenger);
                this._registerCompleted.Next();
                this.Messenger.Raise(new WindowActionMessage(WindowAction.Close, "Close"));
            }).AddTo(this.CompositeDisposable);

            // レシピ破棄コマンド
            this.RevertRecipeCommand.ObserveOnDispatcher(DispatcherPriority.Background).Subscribe(_ => {
                this.IsBusy.Value = true;
                // 変更前の状態を再読込
                this.RecipeViewModel.Value.LoadRecipeCommand.Execute(this.Messenger);
                this.Messenger.Raise(new WindowActionMessage(WindowAction.Close, "Close"));
            }).AddTo(this.CompositeDisposable);

            // レシピ編集モード切り替えコマンド
            this.ChangeModeToEditorCommand =
                new[] {
                this.IsDownaloding.Select(x => !x),
                this.RecipeViewModel.Select(x => x != null),
                this.IsEditing.Select(x => !x)
            }.CombineLatestValuesAreAllTrue()
            .ToReactiveCommand()
            .AddTo(this.CompositeDisposable);
            this.ChangeModeToEditorCommand.Subscribe(() => {
                this.IsEditing.Value = true;
            }).AddTo(this.CompositeDisposable);

            // 編集終了コマンド
            this.CompleteEditCommand =
                new[] {
                this.IsDownaloding.Select(x => !x),
                this.RecipeViewModel.Select(x => x != null),
                this.IsEditing
            }.CombineLatestValuesAreAllTrue()
            .ToReactiveCommand()
            .AddTo(this.CompositeDisposable);
            this.CompleteEditCommand.Subscribe(() => {
                this.IsEditing.Value = false;
            }).AddTo(this.CompositeDisposable);
        }