在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也完全可以复用,如果大家还有更好的方案,欢迎提出来一起讨论。源代码已在下方给出,需要的自行下载。

点击下载源代码

MVVM探索:从ViewModel关闭Window的最佳实践的更多相关文章

  1. 腾讯优测优分享 | 探索react native首屏渲染最佳实践

    腾讯优测是专业的移动云测试平台,旗下的优分享不定时提供大量移动研发及测试相关的干货~ 此文主要与以下内容相关,希望对大家有帮助. react native给了我们使用javascript开发原生app ...

  2. 探索react native首屏渲染最佳实践

    文 / 腾讯 龚麒 0.前言 react native给了我们使用javascript开发原生app的能力,在使用react native完成兴趣部落安卓端发现tab改造后,我们开始对由react n ...

  3. WPF:MVVM模式下ViewModel关闭View

    不外乎两种基本方法. 消息通知和参数传递. 一.消息通知 利用View里的IsEnable属性 原理是这样的: 1.UI中的IsEnabled绑定VM中的属性 2.UI的后台代码中,注册IsEnabl ...

  4. WPF - ViewModle中关闭Window

    在Binding close event时候,需要从ViewModel关闭Window. 一个很简洁的解决方案就是,将Window 当做CommandParameter传过去. Command=&qu ...

  5. easyUI的window包含一个iframe,在iframe中如何关闭window?

    easyUI的window包含一个iframe,在iframe中如何关闭window? parent.$('#win').window('close');

  6. 通过钩子程序跨程序关闭Window

    需求: 在实际场景中会有自身程序在调用第三方的动态库过程中,因为第三方的动态库弹框导致线程阻塞,必须手动将弹窗关闭后才能回到自身程序的主线程中. 最简单的场景就是很多自助设备,本身是没有固定操作员的, ...

  7. 【C#】关闭 Window 之后,无法设置 Visibility,也无法调用 Show、ShowDialogor 或 WindowInteropHelper.EnsureHandle

    问题: 在做WPF项目时,点击一个按钮弹出一个自定义的窗体,然后点击X关闭该窗体,然后再点击按钮想弹出该窗体时,报错:关闭 Window 之后,无法设置 Visibility,也无法调用 Show.S ...

  8. EasyUI-window包含一个iframe,在iframe中如何关闭window

    我试过类似$('#win').window('close');报$.data...options无效的错误,我已经引入了js文件,路径没问题,而且在同一个页面,不用iframe是可以关闭的 在ifra ...

  9. Window下使用Xshell连接VirtualBox中CentOS SSH最佳实践

    网上已经有非常多讲怎样连接VMware的文章.可是针对一些可能遇到的细节没有讲全. 这里会有一个非常 实际的样例,附带全部软件的链接,保证成功. 最佳实践什么的都是骗人的. 1.安装VirtualBo ...

随机推荐

  1. Qt实现QQ界面

    1.Qt实现QQ界面是通过QToolBox类来实现的,基本结构是:QToolBox里面装QGroupBox,然后QGroupBox里面装QToolButton,设置好相关属性即可 2.定义类继承QTo ...

  2. css半透明边框

    html <div class="parent"> <div class="translucent">I am Bob</div& ...

  3. Elasticsearch Head插件实践

    简介 Elasticsearch是一个基于Lucene的搜索服务器.它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口.Elasticsearch是用Java开发的,并作为Ap ...

  4. Java框架之Spring MVC(二)

    一.Spring MVC 验证 JSR 303 是ajvaEE6 中的一项子规范 ,叫 Bean Validation 用于对javaBean中的字段进行校验. 官方的参考实现是: Hibernate ...

  5. Windows AD域升级方

    前面的博客中我谈到了网络的基本概念和网络参考模型,今天我们来谈企业中常用的技术,Windows AD 域,今天我的笔记将重点讲解Windows AD 域的升级和迁移方法,通过3个小实验进行配置,真实环 ...

  6. CSS选择器:伪类(图文详解)

    本文最初发表于博客园,并在GitHub上持续更新前端的系列文章.欢迎在GitHub上关注我,一起入门和进阶前端. 以下是正文. 伪类(伪类选择器) 伪类:同一个标签,根据其不同的种状态,有不同的样式. ...

  7. 【MySQL】计算 TPS,QPS 的方式

     在做db基准测试的时候,qps,tps 是衡量数据库性能的关键指标.本文比较了网上的两种计算方式.先来了解一下相关概念. 概念介绍: QPS:Queries Per Second         查 ...

  8. 【转载】LINUX上MYSQL优化三板斧

    现在MySQL运行的大部分环境都是在Linux上的,如何在Linux操作系统上根据MySQL进行优化,我们这里给出一些通用简单的策略.这些方法都有助于改进MySQL的性能. 闲话少说,进入正题. 一. ...

  9. Linux之内存描述符mm_struct

    Linux对于内存的管理涉及到非常多的方面,这篇文章首先从对进程虚拟地址空间的管理说起.(所依据的代码是2.6.32.60) 无论是内核线程还是用户进程,对于内核来说,无非都是task_struct这 ...

  10. mysql常用的函数

    #链接字符串SELECT CONCAT('My','S','ql');#将字符串str从3位置开始,10个字符长的子串替换为字符串newstrSELECT INSERT('这是sql server数据 ...