MVVM回顾###

经过上一篇文章的介绍,相信你对MVVM的设计思想有所了解。MVVM的核心思想就是解耦,View与ViewModel应该感受不到彼此的存在。

View只关心怎样渲染,而ViewModel只关心怎么处理逻辑,整个架构由数据进行驱动。不仅View与ViewModel彼此解耦,ViewModel与ViewModel之间也是解耦的。

通过消息订阅-发布机制,解决了ViewModel之间的强依赖关系。

先回顾一下我们已完成的功能,Framework中最核心就是BindableProperty 类,ViewModel 中所有需要被绑定到UI 控件的属性必须是一个BindableProperty 对象。它是一个职责非常单一的类,监听Value的数值是否发生变化,当变化时,触发OnValueChanged 事件,通知View 做出相应的更新。

public class BindableProperty<T>
{
public delegate void ValueChangedHandler(T oldValue, T newValue); public ValueChangedHandler OnValueChanged; private T _value;
public T Value
{
get
{
return _value;
}
set
{
if (!object.Equals(_value, value))
{
T old = _value;
_value = value;
ValueChanged(old, _value);
}
}
} private void ValueChanged(T oldValue, T newValue)
{
if (OnValueChanged != null)
{
OnValueChanged(oldValue, newValue);
}
}
}

那问题来了,View在何时并以怎样的方式去监听这些属性的变化呢?

BindableProperty是一个很好的设计,它不仅可以用在ViewModel中,还可以用在View中,用它来修饰 ViewModel,当ViewModel 改变时,比如初始化时,或者从一个ViewModel变化到另一个ViewModel对象时,在触发的OnBindingContextChanged 事件中实现对ViewModel中的属性监听。如下定义的抽象父类:UnityGuiView

public readonly BindableProperty<ViewModel> ViewModelProperty = new BindableProperty<ViewModel>();
public ViewModel BindingContext
{
get { return ViewModelProperty.Value; }
set { ViewModelProperty.Value = value; }
} protected virtual void OnBindingContextChanged(ViewModel oldViewModel, ViewModel newViewModel)
{
} public UnityGuiView()
{
this.ViewModelProperty.OnValueChanged += OnBindingContextChanged;
}

子类SetupView继承自UnityGuiView,并且Override OnBindingContextChanged,并实现对ViewModel中的属性监听。

protected override void OnBindingContextChanged(ViewModel oldViewModel, ViewModel newViewModel)
{ base.OnBindingContextChanged(oldViewModel, newViewModel); SetupViewModel oldVm = oldViewModel as SetupViewModel;
if (oldVm != null)
{
oldVm.Name.OnValueChanged -= NameValueChanged;
...
}
if (ViewModel!=null)
{
ViewModel.Name.OnValueChanged += NameValueChanged;
...
}
}

进一步抽象###

实际上对于ViewModel而言会有非常多的BindableProperty需要被绑定到UI控件中,从代码的可读性而言,如下代码是非常沉长和啰嗦的:

if (oldVm != null)
{
oldVm.Name.OnValueChanged -= NameValueChanged;
...
}
if (ViewModel!=null)
{
ViewModel.Name.OnValueChanged += NameValueChanged;
...
}

因为+=和-=是成对出现的,所以只要是看到 OnValueChanged,这部份代码的长度几乎都是*2。

仔细观察一下,每个View都会出现 具体的 ViewModel.属性.OnValueChanged事件+=或者-=具体的处理函数 这样的固定模板。

那么是否可以将这部分代码抽象到一个公共类中呢,并且暴露出一个简单的方法提供给View来初始化这些OnValueChanged事件,比如:

PropertyBindingUtils.Init<string>("Color",OnColorPropertyValueChanged);

然后在Init方法中+=或者-=具体的处理函数。

当然是可以得,定义一个PropertyBinder属性绑定器,通过反射技术,动态为属性+=或者-= OnValueChanged 事件,脑海里的 Raw 代码如下

Init<TProperty>(string propertyName ,OnValueChanged valueChangedHandler)
{
var fieldInfo = typeof(TViewModel).GetField(propertyName, BindingFlags.Instance | BindingFlags.Public);
var value = fieldInfo.GetValue(viewModel);
BindableProperty<TProperty> bindableProperty = value as BindableProperty<TProperty>;
bindableProperty.OnValueChanged += valueChangedHandler;
bindableProperty.OnValueChanged -= valueChangedHandler;
}

最核心的代码就那么几步,详细代码可以查看源代码PropertyBinder的实现。

重构视图基类:UnityGuiView###

想象一下PropertyBinder应该放在哪儿。

它是用来监听ViewModel中的属性值变化的,用来替换沉长的 oldVm.Property.OnValueChanged +=和-= NameValueChanged,理所应当应该放在View中,因为每个View都需要,故将它定义在UnityGuiView 中。

又因为PropertyBinder需要知道为哪个ViewModel进行服务(因为需要反射),故通过泛型来约束 UnityGuiView< T >:IView where T:ViewModelBase 。

再对BindingContext稍作改变,当它被赋值时,只初始化一次对OnValueChanged事件的监听(原先是放在构造函数里)。

public readonly BindableProperty<ViewModelBase> ViewModelProperty = new BindableProperty<ViewModelBase>();
public ViewModelBase BindingContext
{
get { return ViewModelProperty.Value; }
set
{
if (!_isBindingContextInitialized)
{
OnInitialize();
_isBindingContextInitialized = true;
}
//触发OnValueChanged事件
ViewModelProperty.Value = value;
}
}
/// <summary>
/// 初始化View,当BindingContext改变时执行
/// </summary>
protected virtual void OnInitialize()
{
//无所ViewModel的Value怎样变化,只对OnValueChanged事件监听(绑定)一次
ViewModelProperty.OnValueChanged += OnBindingContextChanged;
}

值得注意的事,我定义了一个virtual的OnInitialize,这样子类可以override它从而实现一些初始化方法,比如:

protected override void OnInitialize()
{
base.OnInitialize();
Binder.Add<string>("Color",OnColorPropertyValueChanged);
} private void OnColorPropertyValueChanged(string oldValue, string newValue)
{
switch (newValue)
{
case "Red":
buttonImage.color = Color.red;
break;
case "Yellow":
buttonImage.color = Color.yellow;
break;
default:
break;
}
}

小节###

这篇博客基本上是回顾了MVVM模式在Unity 3D上的实践,结合自己的开发经验,通过反射的技术可以有效减少沉长的代码。

源代码托管在Github上,点击此了解

Unity应用架构设计(1)—— MVVM 模式的设计和实施(Part 2)的更多相关文章

  1. Unity应用架构设计(1)—— MVVM 模式的设计和实施(Part 1)

    初识 MVVM 谈起 MVVM 设计模式,可能第一映像你会想到 WPF/Sliverlight,他们提供了的数据绑定(Data Binding),命令(Command)等功能,这让 MVVM 模式得到 ...

  2. Unity 3D Framework Designing(1)—— MVVM 模式的设计和实施(Part 2)

    MVVM回顾 经过上一篇文章的介绍,相信你对 MVVM的设计思想有所了解.MVVM的核心思想就是解耦,View与ViewModel应该感受不到彼此的存在.View只关心怎样渲染,而ViewModel只 ...

  3. Unity 3D Framework Designing(1)—— MVVM 模式的设计和实施(Part 1)

    初识 MVVM 谈起 MVVM 设计模式,可能第一映像你会想到 WPF/Sliverlight,他们提供了的数据绑定(Data Binding),命令(Command)等功能,这让 MVVM 模式得到 ...

  4. WPF之MVVM模式(1)

    MVVM模式 一.MVVM模式概述 MVVM Pattern : Model\View\ViewModel View:视图.UI界面 ViewModel:ViewModel是对Model的封装,通过一 ...

  5. WPF MVVM架构 EF、WCF、IOC 设计示例经典

    概要 该演示项目利用WPF应用程序构建的MVVM架构示例, 运用了Unity容器接口注入, MVVM的经典设计, 后台利用的EF+WCF. 后台实现: 从数据库生成的emdx 结合上下文进行数据交互, ...

  6. .NET应用架构设计—表模块模式与事务脚本模式的代码编写

    阅读目录: 1.背景介绍 2.简单介绍表模块模式.事务脚本模式 3.正确的编写表模块模式.事务脚本模式的代码 4.总结 1.背景介绍 要想正确的设计系统架构就必须能正确的搞懂每个架构模式的用意,而不是 ...

  7. 企业级架构 MVVM 模式指南 (WPF 和 Silverlight 实现) 译(1)

    前言对于WPF和Silverlight来讲,MVVM是微软设计师和业内专家高度推荐的非常棒的一种设计模式.本书会探讨MVVM设计模式的一些自身缺陷以及为什么MVVM还不能成为行业内的标准设计模式.这会 ...

  8. 再谈MV*(MVVM MVP MVC)模式的设计原理—封装与解耦

    精炼并增补于:界面之下:还原真实的MV*模式 图形界面的应用程序提供给用户可视化的操作界面,这个界面提供给数据和信息.用户输入行为(键盘,鼠标等)会执行一些应用逻辑,应用逻辑(application ...

  9. .NET 云原生架构师训练营(设计原则与模式)--学习笔记

    在复杂系统的架构设计中引入设计原则与模式,能够极大降低复杂系统开发.和维护的成本 目录 几个问题 为什么要学习设计模式 优良架构设计的具体指标 理解复杂系统 面向对象思想(指导复杂系统的分析.设计.实 ...

随机推荐

  1. Codeforces 992C Nastya and a Wardrobe (思维)

    <题目链接> 题目大意: 你开始有X个裙子 你有K+1次增长机会 前K次会100%的增长一倍 但是增长后有50%的机会会减少一个 给你X,K(0<=X,K<=1e18), 问你 ...

  2. 浅谈solr

    Solr是一个独立的企业级搜索应用服务器,它对外提供类似于Web-service的API接口.用户可以通过http请求,向搜索引擎服务器提交一定格式的XML文件,生成索引:也可以通过Http Get操 ...

  3. 分布式存储MooseFS

    MooseFS 简介 http://moosefs.org/ MFS特性 安装和配置简单方便   可靠性高(数据的多个拷贝被存储在多个不同的服务器上)   通过添加新的服务器或硬盘就可以实现容量的动态 ...

  4. nodejs EventEmitter 发送消息

    var util = require('util'); var evem = require('events').EventEmitter; function myem(){ evem.call(th ...

  5. Python3从零开始爬取今日头条的新闻【三、滚动到底自动加载】

    Python3从零开始爬取今日头条的新闻[一.开发环境搭建] Python3从零开始爬取今日头条的新闻[二.首页热点新闻抓取] Python3从零开始爬取今日头条的新闻[三.滚动到底自动加载] Pyt ...

  6. UOJ#132&bzoj4200[Noi2015]小园丁与老司机

    看,这是一个传送门 Part A 把坐标离散化,按照纵坐标为第一关键字,横坐标为第二关键字排序 以$f_i$记录来到$i$这个点最多经过点数,那么答案显而易见就是$f_i$加上该层点数 转移的话就是分 ...

  7. echarts相关的可视化数据

    echarts使用步骤: 1)设置一个容器,该容器用来放图形,一定要给容器设置高度: 2)初始化echarts实例,语法:var  aa =  echarts.init(DOM); 例如: echar ...

  8. json串转化成xml文件、xml文件转换成json串

    1.json串转化成xml文件 p=[{"name":"tom","age":30,"sex":"男" ...

  9. css 文本超出n行就隐藏并且显示省略号

    首先,要知道css的三条属性. overflow:hidden; //超出的文本隐藏 text-overflow:ellipsis; //溢出用省略号显示 white-space:nowrap; // ...

  10. GMA Round 1 双曲线与面积

    传送门 双曲线与面积 P是双曲线$\frac{x^2}{1471^2}-\frac{y^2}{1372^2}=1$上的一个动点,现在过P作一条直线与该双曲线的两条渐近线相交于A.B两点,且|AP|=| ...