zoukankan      html  css  js  c++  java
  • MVVM探索:从ViewModel关闭Window的最佳实践

    在WPF里使用MVVM开发的时候,似乎总是不可避免的会遇到这样一个问题:ViewModel在处理完业务之后需要关闭这个Window,这时候要怎么处理?

    网上有很多解决方案:有的在ViewModel抛出一个事件,在View端使用(XXXViewModel)this.DataContext的方式去响应事件;有的通过Trigger、Behavior、Action之类的方式曲线救国;还有的使用了其他的第三方框架。

    这些操作从某个层面上来说确实能实现这个功能,但是有的操作起来过于麻烦,有的实现功能了但是大大的违反了MVVM的原则,有的则有很多局限性(比如只能针对关闭了Window之后什么都不做,或者必须要求Window有无参的构造函数)。直到我发现了还可以有这样一种操作之后,我觉得这应该处理这个问题的最佳实践了:优雅,简洁,符合MVVM的思想还没有局限性。

    在MVVM里,View和ViewModel之间通过绑定完成了大部分的操作,这也是MVVM最为推荐的做法。那么,为什么View的关闭这个事情不能通过绑定来实现呢?是因为Window没有控制关闭这个操作的属性么?不,在没有使用MVVM,直接在后台写代码创建了一个Window的时候,我们只需要将这个Window的DialogResult属性赋值(不管是true还是false)就可以将这个窗口关闭。那么我们为什么不直接将Window的DialogResult属性在ViewModel绑定呢?

    秉着这样的思想我去做了这个实验,编译通过,运行的时候得到了这样的异常提示:

    “不能在“ChildWindow”类型的“DialogResult”属性上设置“Binding”。只能在 DependencyObject 的 DependencyProperty 上设置“Binding”。

    这个提示已经很明显了:为什么不能直接对Window的DialogResult做绑定,因为DialogResult这个属性不是依赖属性,WPF里面所有的绑定都必须只能绑定依赖属性,而WPF里绝大部分的属性都是依赖属性,但是DialogResult恰恰不是依赖属性,所以不能绑定。

    此路不通之后就有了上面的各种解决方法,但是为什么不这样想:DialogResult不是依赖属性,那我注册一个依赖属性不就完了么?WPF又不是不让注册。

    注册依赖属性代码如下:

    public static class DialogCloser
        {
            public static readonly DependencyProperty DialogResultProperty =
                DependencyProperty.RegisterAttached(
                    "DialogResult",
                    typeof(bool?),
                    typeof(DialogCloser),
                    new PropertyMetadata(DialogResultChanged));
    
            private static void DialogResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                var window = d as Window;
                if (window != null)
                {
                    window.DialogResult = e.NewValue as bool?;
                }
            }
    
            public static void SetDialogResult(Window target, bool? value)
            {
                target.SetValue(DialogResultProperty, value);
            }
        }

    然后在View端绑定这个依赖属性DialogResult:

    <Window x:Class="mvvm_demo_close_window.ChildWindow"
            ...
            xmlns:xc="clr-namespace:mvvm_demo_close_window"
            xc:DialogCloser.DialogResult="{Binding DialogResult}">

    然后在ViewModel端将这个当做正常的依赖属性去操作就行了,当this.DialogResult=true的时候就自动在ViewModel关闭了子窗口:

    public class ChildWindowViewModel : ViewModelBase
        {
            private bool? dialogResult;
            public bool? DialogResult
            {
                get { return this.dialogResult; }
                set
                {
                    this.dialogResult = value;
                    RaisePropertyChanged("DialogResult");
                }
            }
    
            //用来接收关闭按钮的Command
            public ICommand CloseCmd
            {
                get
                {
                    return new DelegateCommand((obj) =>
                    {
                        this.DialogResult = true;
                    });
                }
            }
        }

    这是我目前发现的最优雅的解决方案,DialogCloser也完全可以复用,如果大家还有更好的方案,欢迎提出来一起讨论。源代码已在下方给出,需要的自行下载。

    点击下载源代码

  • 相关阅读:
    sass中使用穿透属性(deep)修改第三方组件样似
    Codeforces Round #647 (Div. 2) D. Johnny and Contribution(BFS)
    Codeforces Round #647 (Div. 2) C. Johnny and Another Rating Drop(数学)
    Codeforces Round #647 (Div. 2) B. Johnny and His Hobbies(枚举)
    Codeforces Round #647 (Div. 2) A. Johnny and Ancient Computer
    AtCoder Beginner Contest 169
    Codeforces Round #646 (Div. 2) E. Tree Shuffling(树上dp)
    Codeforces Round #646 (Div. 2) C. Game On Leaves(树上博弈)
    Codeforces Round #646 (Div. 2) B. Subsequence Hate(前缀和)
    Codeforces Round #646 (Div. 2) A. Odd Selection(数学)
  • 原文地址:https://www.cnblogs.com/SilveryBullet/p/8384919.html
Copyright © 2011-2022 走看看