在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. ES6解构之复杂数据

    今天在写代码的是否,碰到如下的数据,我要取值 fvkey,fn,url. { , , "fl":Object{...}, , "ip":"106.39 ...

  2. px-rem自适应转换

    当前团队开发过程,存在2种度量单位(px.rem)各有说辞px:各个终端统一大小,简单明了,未尝不可!rem:大屏幕显示大字体,小屏幕显示小字体,渐进增强视觉感.业界各种写法都有,不逐一讨论.团队呼声 ...

  3. #多个关联的python程序在linux后台运行

    由于在shell脚本中直接使用&符号和python程序有冲突,不能正常的进入后台执行python(多番尝试python xxx.py &,后面的&总是不能正常识别.系统cent ...

  4. 第六章:Python基础の反射与常用模块解密

    本课主题 反射 Mapping 介绍和操作实战 模块介绍和操作实战 random 模块 time 和 datetime 模块 logging 模块 sys 模块 os 模块 hashlib 模块 re ...

  5. fastdfs集群

    高可用的两大目的:数据备份,数据分片 1.FastDFS安装配置 先配置一台,将其中的配置文件打包,下载,然后配置其他机器时只需要解压即可, 打包命令 然后下载,上传到其他机器相对应的/etc目录下 ...

  6. 学习总结:工程管理与makefile

    工程管理与makefile 一.为什么需要makefile和make 一个工程中的源文件可能很多,按照类型.功能.模块分别放在若干个目录中,为了有效地管理软件工程,更高效地编译整个工程,需要用到mak ...

  7. Linux架构浅谈

    以下图为基础: 最内层是硬件,最外层是用户常用的应用,比如说firefox浏览器,evolution查看邮件,一个计算流体模型等等.硬件是物质基础,而应用提供服务.但在两者之间,还要经过一番周折. 还 ...

  8. Spring Boot实战:集成Swagger2

    一.Swagger简介 上一篇文章中我们介绍了Spring Boot对Restful的支持,这篇文章我们继续讨论这个话题,不过,我们这里不再讨论Restful API如何实现,而是讨论Restful ...

  9. element-ui,router.push到其他路由,菜单栏不会高亮对应的路由

    使用饿了吗的路由,使用this.$router.push({path: ''})跳到其他的路由,菜单不会高亮. 如图所示,点击图上三个位置,需要使用this.$router.push({path: ' ...

  10. bzoj:2595: [Wc2008]游览计划

    Description Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个景点:否则表示控制该方块至少需要的志愿者数 ...