Пример #1
0
        public void OnPropertyChanged(Expression <Func <object> > expr)
        {
            MemberExpression me = INotifyPropertyChangedExtensions.Resolve(expr);

            if (me != null)
            {
                NotifyPropertyChanged(me.Member.Name);
            }
        }
Пример #2
0
        /// <summary>
        /// コンストラクタ
        /// </summary>
        public MainWindowViewModel()
        {
            this.Tree = this.Model.Content
                        .Tree
                        .ToReadOnlyReactiveCollection(x => new NodeViewModel(x));

            this.WindowTitle = this.Model.ObserveProperty(x => x.FilePath)
                               .Select(x => string.IsNullOrWhiteSpace(x) ? "" : System.IO.Path.GetFileName(x))
                               .Select(x => "Quartet Editor" + (string.IsNullOrWhiteSpace(x) ? "" : $" - {x}"))
                               .CombineLatest(this.Model.ObserveProperty(x => x.IsEdited), (title, flg) => title + (flg ? "(変更あり)" : ""))
                               .ToReactiveProperty()
                               .AddTo(this.Disposable);

            // エラーメッセージ表示要求の処理
            this.Model.ShowErrorMessageRequest.Subscribe(async message =>
            {
                var arg = new DialogArg
                {
                    Title   = "エラー",
                    Message = message,
                    Style   = MessageDialogStyle.Affirmative
                };

                await this._MessageDialogRequest.RaiseAsync(new Confirmation
                {
                    Content = arg
                });
                return;
            });

            #region Content

            // VM -> M 一方向バインド
            this.SelectedNode = ReactiveProperty.FromObject(
                this.Model.Content,
                x => x.SelectedNode,
                convert: x => (NodeViewModel)null, // M -> VMの変換処理
                convertBack: x => x?.Model);       // VM -> Mの変換処理
            this.SelectedNode.Subscribe(_ =>
            {
                // 選択状態が変わったときにTreeViewからフォーカスが外れるのを避ける
                this.setFocusRequest.Raise(new Confirmation {
                    Content = "_NodeView"
                });
            });

            // M -> VM 一方向バインド
            this.TextContent = this.Model.Content
                               .ObserveProperty(x => x.TextContent)
                               .ToReactiveProperty()
                               .AddTo(this.Disposable);

            this.ParentTextContent = this.Model.Content
                                     .ObserveProperty(x => x.ParentTextContent)
                                     .ToReactiveProperty()
                                     .AddTo(this.Disposable);

            this.PrevTextContent = this.Model.Content
                                   .ObserveProperty(x => x.PrevTextContent)
                                   .ToReactiveProperty()
                                   .AddTo(this.Disposable);

            this.NextTextContent = this.Model.Content
                                   .ObserveProperty(x => x.NextTextContent)
                                   .ToReactiveProperty()
                                   .AddTo(this.Disposable);

            #endregion Content

            #region Panel

            this.LeftPanelOpen = this.Config
                                 .ToReactivePropertyAsSynchronized(x => x.LeftPanelOpen)
                                 .AddTo(this.Disposable);

            this.TopPanelOpen = this.Config
                                .ToReactivePropertyAsSynchronized(x => x.TopPanelOpen)
                                .AddTo(this.Disposable);

            this.BottomPanelOpen = this.Config
                                   .ToReactivePropertyAsSynchronized(x => x.BottomPanelOpen)
                                   .AddTo(this.Disposable);

            // パネルの開閉状態が変わったときはRisePanelStateを呼び出す
            new[] { this.LeftPanelOpen, this.TopPanelOpen, this.BottomPanelOpen }
            .Select(x => INotifyPropertyChangedExtensions.PropertyChangedAsObservable(x))
            .Merge()
            .Subscribe(_ =>
            {
                this.RisePanelState();
                this.Model.Content.UpdatePanelReffer();
                // ReleaseでビルドするとなぜかReactivePropertyが反応しないので…
            })
            .AddTo(this.Disposable);

            this.PanelChangeCommand.Subscribe(kind =>
            {
                switch (kind)
                {
                case PanelKind.Left:
                    this.LeftPanelOpen.Value = !this.LeftPanelOpen.Value;
                    return;

                case PanelKind.Top:
                    this.TopPanelOpen.Value = !this.TopPanelOpen.Value;
                    return;

                case PanelKind.Bottom:
                    this.BottomPanelOpen.Value = !this.BottomPanelOpen.Value;
                    return;

                default:
                    return;
                }
            });

            #endregion Panel

            #region DragDrop

            Observable.FromEvent <Action <DragEventArgs>, DragEventArgs>(
                h => (e) => h(e),
                h => this.DragAcceptDescription.DragOverAction += h,
                h => this.DragAcceptDescription.DragOverAction -= h).Subscribe(arg =>
            {
                var fe = arg.OriginalSource as FrameworkElement;
                if (fe == null)
                {
                    return;
                }
                var target = fe.DataContext as NodeViewModel;
                var data   = arg.Data.GetData(typeof(NodeViewModel)) as NodeViewModel;

                if (data == null)
                {
                    if (!arg.Data.GetDataPresent(DataFormats.FileDrop, true))
                    {
                        return;
                    }

                    // ファイルドロップの場合
                    if (arg.AllowedEffects.HasFlag(System.Windows.DragDropEffects.Move))
                    {
                        arg.Effects = System.Windows.DragDropEffects.Move;
                    }
                }
                else
                {
                    if (data.IsNameEditMode.Value)
                    {
                        return;
                    }

                    if (arg.AllowedEffects.HasFlag(System.Windows.DragDropEffects.Move) && !KeyboardUtility.IsCtrlKeyPressed)
                    {
                        arg.Effects = System.Windows.DragDropEffects.Move;
                    }
                    else if (arg.AllowedEffects.HasFlag(System.Windows.DragDropEffects.Copy) && KeyboardUtility.IsCtrlKeyPressed)
                    {
                        arg.Effects = System.Windows.DragDropEffects.Copy;
                    }
                    else
                    {
                        return;
                    }
                }

                this.Model.Content.DragOverAction(target?.Model, data?.Model);
            }).AddTo(this.Disposable);

            Observable.FromEvent <Action <DragEventArgs>, DragEventArgs>(
                h => (e) => h(e),
                h => this.DragAcceptDescription.DragEnterAction += h,
                h => this.DragAcceptDescription.DragEnterAction -= h).Subscribe(arg =>
            {
                var fe = arg.OriginalSource as FrameworkElement;
                if (fe == null)
                {
                    return;
                }
                var target = fe.DataContext as NodeViewModel;
                var data   = arg.Data.GetData(typeof(NodeViewModel)) as NodeViewModel;

                this.Model.Content.DragEnterAction(target?.Model, data?.Model);
            }).AddTo(this.Disposable);

            Observable.FromEvent <Action <DragEventArgs>, DragEventArgs>(
                h => (e) => h(e),
                h => this.DragAcceptDescription.DragLeaveAction += h,
                h => this.DragAcceptDescription.DragLeaveAction -= h).Subscribe(arg =>
            {
                var fe = arg.OriginalSource as FrameworkElement;
                if (fe == null)
                {
                    return;
                }
                var target = fe.DataContext as NodeViewModel;
                this.Model.Content.DragLeaveAction(target?.Model);
            }).AddTo(this.Disposable);

            Observable.FromEvent <Action <DragEventArgs>, DragEventArgs>(
                h => (e) => h(e),
                h => this.DragAcceptDescription.DragDropAction += h,
                h => this.DragAcceptDescription.DragDropAction -= h).Subscribe(arg =>
            {
                var fe = arg.OriginalSource as FrameworkElement;
                if (fe == null)
                {
                    return;
                }
                var target = fe.DataContext as NodeViewModel;
                var data   = arg.Data.GetData(typeof(NodeViewModel)) as NodeViewModel;

                this.Model.Content.DragDropAction(arg, target?.Model, data?.Model, KeyboardUtility.IsCtrlKeyPressed);
            }).AddTo(this.Disposable);

            #endregion DragDrop

            #region AboutFlyout

            // AboutCommand
            this.OpenAboutCommand.Subscribe(_ => this.IsAboutOpen = true).AddTo(this.Disposable);

            #endregion AboutFlyout

            #region Focus
            // SetFocusCommand
            this.SetFocusCommand.Subscribe(param =>
            {
                // Viewにリクエストを投げる
                this.setFocusRequest.Raise(new Confirmation {
                    Content = param
                });
            }).AddTo(this.Disposable);
            #endregion Focus

            #region NodeManipulation

            this.NameEditCommand.Subscribe(_ => this.Model.Content.CallNameEditMode()).AddTo(this.Disposable);

            this.UndoCommand = new ReactiveCommand(this.Model.Content.ChangedCanUndo, false);
            this.UndoCommand.Subscribe(_ =>
            {
                this.Model.Content.Undo();
            }).AddTo(this.Disposable);

            this.RedoCommand = new ReactiveCommand(this.Model.Content.ChangedCanRedo, false);
            this.RedoCommand.Subscribe(_ => this.Model.Content.Redo()).AddTo(this.Disposable);

            this.DeleteNodeCommand.Subscribe(_ => this.Model.Content.DeleteNode()).AddTo(this.Disposable);

            this.AddNodeSameCommand.Subscribe(_ => this.Model.Content.AddNodeSame()).AddTo(this.Disposable);

            this.AddNodeLowerCommand.Subscribe(_ => this.Model.Content.AddNodeLower()).AddTo(this.Disposable);

            this.ReproduceCommand.Subscribe(_ => this.Model.Content.Reproduce()).AddTo(this.Disposable);

            this.AddNodeFromHeaderCommand.Subscribe(_ => this.Model.Content.AddNodeFromHeader()).AddTo(this.Disposable);

            this.MoveUpCommand = new ReactiveCommand(this.Model.Content.ChangedCanMoveUp, false);
            this.MoveUpCommand.Subscribe(_ => this.Model.Content.MoveUp()).AddTo(this.Disposable);

            this.MoveDownCommand = new ReactiveCommand(this.Model.Content.ChangedCanMoveDown, false);
            this.MoveDownCommand.Subscribe(_ => this.Model.Content.MoveDown()).AddTo(this.Disposable);

            this.MoveChildCommand = new ReactiveCommand(this.Model.Content.ChangedCanMoveChild, false);
            this.MoveChildCommand.Subscribe(_ => this.Model.Content.MoveChild()).AddTo(this.Disposable);

            this.MoveParentCommand = new ReactiveCommand(this.Model.Content.ChangedCanMoveParent, false);
            this.MoveParentCommand.Subscribe(_ => this.Model.Content.MoveParent()).AddTo(this.Disposable);

            #endregion NodeManipulation

            #region File

            // ファイルのドロップイベント処理
            Observable.FromEvent <Action <DragEventArgs>, DragEventArgs>(
                h => (e) => h(e),
                h => this.DraggedFileAcceptDescription.DragOverAction += h,
                h => this.DraggedFileAcceptDescription.DragOverAction -= h).Subscribe(arg =>
            {
                if (arg.Data.GetDataPresent(DataFormats.FileDrop, true))
                {
                    arg.Effects = DragDropEffects.Copy;
                    arg.Handled = true;
                }
            }).AddTo(this.Disposable);;

            Observable.FromEvent <Action <DragEventArgs>, DragEventArgs>(
                h => (e) => h(e),
                h => this.DraggedFileAcceptDescription.DragDropAction += h,
                h => this.DraggedFileAcceptDescription.DragDropAction -= h).Subscribe(arg =>
            {
                string[] files = arg.Data.GetData(DataFormats.FileDrop) as string[];

                if (files != null && files.Count() == 1)
                {
                    this.Model.Load(files[0]);
                }
            }).AddTo(this.Disposable);

            this.SaveCommand.Subscribe(_ => this.Model.SaveOverwrite()).AddTo(this.Disposable);
            this.RenameSaveCommand.Subscribe(_ => this.Model.SaveAs()).AddTo(this.Disposable);
            this.Model.SavePathRequest.Subscribe(act =>
            {
                if (this.SaveDialogViewAction == null)
                {
                    return;
                }

                var dialog          = new SaveFileDialog();
                dialog.Title        = "QEDファイルを保存";
                dialog.Filter       = "QEDファイル(*.qed)|*.qed|全てのファイル(*.*)|*.*";
                dialog.AddExtension = true;
                dialog.DefaultExt   = "qed";
                dialog.FileName     = "新規";
                string path         = this.SaveDialogViewAction(dialog);
                act(path);
            }).AddTo(this.Disposable);
            this.OpenCommand.Subscribe(_ => this.Model.OpenQED()).AddTo(this.Disposable);
            this.Model.OpenPathRequest.Subscribe(act =>
            {
                if (this.OpenDialogViewAction == null)
                {
                    return;
                }

                var dialog    = new OpenFileDialog();
                dialog.Title  = "QEDファイルを開く";
                dialog.Filter = "QEDファイル(*.qed)|*.qed|階層付きテキスト(*.txt)|*.txt|全てのファイル(*.*)|*.*";
                string path   = this.OpenDialogViewAction(dialog);
                act(path);
            }).AddTo(this.Disposable);

            #endregion File

            #region Export

            this.OpenExportDialogCommand.Subscribe(_ =>
            {
                this.ExportDialogViewAction();
            }).AddTo(this.Disposable);

            this.Model.ExportSavePathRequest.Subscribe(tuple =>
            {
                if (this.SaveDialogViewAction == null)
                {
                    return;
                }

                var dialog          = new SaveFileDialog();
                dialog.Title        = "エクスポート";
                dialog.Filter       = tuple.Item1;
                dialog.AddExtension = true;
                dialog.DefaultExt   = tuple.Item2;
                dialog.FileName     = string.IsNullOrWhiteSpace(this.Model.FilePath) ? "新規" : System.IO.Path.GetFileNameWithoutExtension(this.Model.FilePath);
                string path         = this.SaveDialogViewAction(dialog);
                tuple.Item3(path);
            }).AddTo(this.Disposable);

            #endregion Export

            #region FindAndReplace

            this.OpenFindDialogCommand.Subscribe(_ =>
            {
                this._FindReplaceDialogRequest.Raise(new Confirmation
                {
                    Content = new FindReplaceDialogRequestEntity()
                    {
                        RequestKind = FindReplaceDialogRequestEntity.DialogRequest.OpenFind
                    }
                });
            }).AddTo(this.Disposable);

            this.OpenReplaceDialogCommand.Subscribe(_ =>
            {
                this._FindReplaceDialogRequest.Raise(new Confirmation
                {
                    Content = new FindReplaceDialogRequestEntity()
                    {
                        RequestKind = FindReplaceDialogRequestEntity.DialogRequest.OpenReplace
                    }
                });
            }).AddTo(this.Disposable);

            this.FindNextCommand.Subscribe(_ =>
            {
                this._FindReplaceDialogRequest.Raise(new Confirmation
                {
                    Content = new FindReplaceDialogRequestEntity()
                    {
                        RequestKind = FindReplaceDialogRequestEntity.DialogRequest.FindNext
                    }
                });
            }).AddTo(this.Disposable);

            this.FindPrevCommand.Subscribe(_ =>
            {
                this._FindReplaceDialogRequest.Raise(new Confirmation
                {
                    Content = new FindReplaceDialogRequestEntity()
                    {
                        RequestKind = FindReplaceDialogRequestEntity.DialogRequest.FindPrev
                    }
                });
            }).AddTo(this.Disposable);

            #endregion FindAndReplace

            #region ScrolledLine

            this.CenterScrolledLine = new ReactiveProperty <int?>().AddTo(this.Disposable);
            this.CenterScrolledLine.Subscribe(t =>
            {
                if (this.Model?.Content?.SelectedNode != null && this.Config.RestoreCenterScrolledLine)
                {
                    this.Model.Content.SelectedNode.LastScrolledLine = t;
                }
            }).AddTo(this.Disposable);

            this.ParentScrolledLine = new ReactiveProperty <int?>().AddTo(this.Disposable);
            this.ParentScrolledLine.Subscribe(t =>
            {
                if (this.Model?.Content?.ParentNode != null && this.Config.RestoreLeftScrolledLine)
                {
                    this.Model.Content.ParentNode.LastScrolledLine = t;
                }
            }).AddTo(this.Disposable);

            this.PrevScrolledLine = new ReactiveProperty <int?>().AddTo(this.Disposable);
            this.PrevScrolledLine.Subscribe(t =>
            {
                if (this.Model?.Content?.PrevNode != null && this.Config.RestoreTopBottomScrolledLine)
                {
                    this.Model.Content.PrevNode.LastScrolledLine = t;
                }
            }).AddTo(this.Disposable);

            this.NextScrolledLine = new ReactiveProperty <int?>().AddTo(this.Disposable);
            this.NextScrolledLine.Subscribe(t =>
            {
                if (this.Model?.Content?.NextNode != null && this.Config.RestoreTopBottomScrolledLine)
                {
                    this.Model.Content.NextNode.LastScrolledLine = t;
                }
            }).AddTo(this.Disposable);

            this.SelectedNode.Subscribe(_ =>
            {
                // { [編集パネル], [左パネル], [上パネル], [下パネル] }
                var val = new int?[4];
                if (this.Model?.Content?.SelectedNode?.LastScrolledLine.HasValue == true &&
                    this.Config.RestoreCenterScrolledLine)
                {
                    val[0] = this.Model.Content.SelectedNode.LastScrolledLine.Value;
                }

                if (this.Model?.Content?.ParentNode?.LastScrolledLine.HasValue == true &&
                    this.Config.RestoreLeftScrolledLine)
                {
                    val[1] = this.Model.Content.ParentNode.LastScrolledLine.Value;
                }

                if (this.Model?.Content?.PrevNode?.LastScrolledLine.HasValue == true &&
                    this.Config.RestoreTopBottomScrolledLine)
                {
                    val[2] = this.Model.Content.PrevNode.LastScrolledLine.Value;
                }

                if (this.Model?.Content?.NextNode?.LastScrolledLine.HasValue == true &&
                    this.Config.RestoreTopBottomScrolledLine)
                {
                    val[3] = this.Model.Content.NextNode.LastScrolledLine.Value;
                }

                this.setScrollRequest.Raise(new Confirmation
                {
                    Content = val
                });
            })
            .AddTo(this.Disposable);

            #endregion ScrolledLine
        }