WPF的MVVM的分析理解(⼀)
简介
简单的三层架构⽰例和 GLUE(胶⽔)代码问题
第⼀步:最简单的 MVVM ⽰例 - 把后台代码移到类中
第⼆步:添加绑定 - 消灭后台代码
第三步:添加执⾏动作和“INotifyPropertyChanged”接⼝
第四步:在 ViewModel 中解耦执⾏动作
第五步:利⽤ PRISM
WPF MVVM 的视频演⽰
简介
从我们还是⼉童到学习成长为成年⼈,⽣命⼀直都在演变。 对于软件架构, 同样适⽤这个道理, 从⼀个基础的架构开始, 随着每个需求和情境在不断演化。
如果你问任何⼀个 .NET 开发者, 什么是最⼩的基础架构, ⾸先浮现的就是"三层架构"。 在这个框架中, 我们把项⽬分为三个逻辑层次: UI 层, 业务逻辑层和数据访问层, 每⼀层都负责各⾃对应的功能。
UI 负责显⽰功能, 业务逻辑层负责校验, 数据访问层负责 SQL 语句。3层架构有如下的好处:
包容变化: 每⼀层的变化不会重复跨越到其它层次。
重⽤性: 增强可重⽤性, 因为每⼀层都是分离, ⾃包容的独⽴实体
MVVM 是三层架构的⼀个演化。我知道我的经历不够证明这点, 但是我个⼈对 MVVM 进⾏了演化和观察。 那我们先从三层基础架构开始, 去理解三层架构存在的问题, 看 MVVM 架构是如何解决这些问题, 然后升级到去创建⼀个⾃定义的 MVVM 框架代码。 下⾯是本⽂接下来的路线图。
简单的三层架构⽰例和 GLUE(胶⽔) 代码问题
⾸先, 让我们来理解三层架构以及它存在的问题, 然后看 MVVM 如何解决这个问题。
直觉和现实是两种不同的事物。 当你看到三层架构的图, 你⾸先的直觉是每个功能可能都分布在各⾃层次。 但是当你实际编写代码时, 有些层次被强迫去做⼀些它们不应该做的额外的⼯作
额外的⼯作(破坏了SOLID 原则)。 如果你对 SOLID 原则还不熟悉可以参考这个视频: (译者注:  指 Single responsibility, Open-closed, Liskov substitution, Inter face segregation and Dependency inversion, 即单⼀功能、开闭原则、⾥⽒替换、接⼝隔离以及依赖反转)。
这部分额外⼯作就在 UI 与Model之间, 以及 Model 与 Data access 之间。 我们把这类代码称为"GLUE"(胶⽔, 译者注:由于作者全⽤⼤写字母表⽰, 因此后续延⽤ GLUE)代码。"GLUE"代码主要有两种逻辑类型。
转换逻辑:
转换逻辑:每个层次使⽤的数据格式都是不同的。⽐如⼀个 Model 类"Person"有⼀个性别属性,可取值分别为 "F"(Female) 和 "M"(Male) 分别代表⼥性和男性。但是在 UI 层中,希望将这个值可视化为⼀个复选框控件,勾选则代表男性,不勾选则代表⼥性。下⾯是⼀个转换代码⽰例。
if (obj.Gender == “M”) // 转换代码 {chkMale.IsChecked = true;}
else
{chkMale.IsChecked = false;}
⼤多数开发者最终会将"GLUE"代码写到UI层中。通常可以在后台代码中定位到这类代码,例如 .cs ⽂件。如果UI 是 XAML,则对应的 XAML.cs 包含 GLUE代码;如果 UI 是 ASPX,则对应的 ASPX.cs 包含 GLUE 代码,以此类推。
那么问题来了:是UI负责这类GLUE代码吗?让我们看下WPF应⽤中的⼀个简单的三层结构例⼦,以及更详细的GLUE 代码细节。
下⾯是⼀个简单的模型类"Customer",它有三个属性“CustomerName”,“Amount” 和“Married”。
但是,当这个模型显⽰到 UI 上时它⼜表现如下。所以,你可以看出来它包含了该模型的所有属性,以及⼀些额外的元素:颜⾊标签和 Married 复选框控件。
下⾯有⼀张简单的表,左边是 Model,右边是 UI,中间是谈过的映射和转换逻辑。
你可以看到前两⾏没有转换逻辑,只有映射逻辑,另外两⾏则同时包含转换逻辑和映射逻辑。
Model GLUE CODE UI
Customer Name                No conversion needed only Mapping                Customer Name
Amount                No conversion needed only Mapping                Amount
Amount                Mapping + Conversion logic.                > 1500 = BLUE < 1500 = RED
Married                Mapping + Conversion logic.                True – Married False - UnMarried
这些转换和映射逻辑代码通常会在“xaml.cs”⽂件中。下⾯是上图对应的后台代码,你可以看到映射代码和颜⾊判定、性别格式转换代码。我在代码中⽤注释标注出来,这样你可以看到哪些是映射代码,哪些是转换代码。
lblName.Content = o.CustomerName; // mapping code
lblAmount.Content = o.Amount; // mapping code
if (o.Amount > 2000) // transformation code
{
lblBuyingHabits.Background = new SolidColorBrush(Colors.Blue);
}
else if (o.Amount > 1500) // transformation code
{
lblBuyingHabits.Background = new SolidColorBrush(Colors.Red);
}
if (obj.Married == "Married") // transformation code
{
chkMarried.IsChecked = true;
}
else
{
chkMarried.IsChecked = false;
}
现在这些 GLUE 代码存在的问题:
单⼀责任原则被破坏(SRPViolation): 是 UI 负责这些 GLUE 代码吗?这种情况下改变了 Amount 单⼀责任原则被破坏(SRPViolation)
数量,同时也需要修改 UI 代码。现在,数据的改变为什么会让我去修改 UI 的代码?这⾥可以闻到坏代码的味道。UI 应该只在我修改样式,颜⾊和布局的时候才改变。
重⽤性: 如果我想把同样的颜⾊逻辑和性别格式转换⽤到下⾯的编辑界⾯,我该怎么做?拷贝粘帖重复的
重⽤性
代码?
如果我想⾛得更远⼀点,把这个 GLUE 代码⽤在不同的 UI 技术体系上,⽐如 MVC、Windows Form 或
者 Mobile 应⽤上。
但是这⾥跨 UI 技术平台的重⽤实际上是不可能的,因为每个平台 UI 背后都和各⾃的 UI 技术体系耦合得很紧密。
⽐如,下⾯的后台代码是继承⾃“Windows”类,⽽“Windows”类是集成在 WPF UI 体系中。如果我们想在 Web 应⽤或者 MVC 中应⽤这些逻辑,却⼜⽆法去创建⼀个这样的类对象来使⽤。
public partial class MainWindow : Window
{
// Behind code is here
}
那么我们要怎么重⽤后台代码?怎么遵循 SRP 原则?
第⼀步:最简单的 MVVM ⽰例 - 把后台代码移到类中
我想⼤部分开发者已经知道怎么解决这个问题。毫⽆疑问地把后台代码(GLUE 代码)移到⼀个类库中。这个类库代表了描述了 UI 的属性和⾏为。任何移⼊到这个类库的代码都可以编译成 DLL,然后被所有 .NET 项⽬(Windows,Web 等等)所引⽤。因此,在这⼀节我们将创建⼀个最简单的 MVVM ⽰例,然后在后续的章节中我们将基于这个⽰例创建更⾼级的 MVVM ⽰例。
我们创建⼀个“CustomerViewModel”类来包含 GLUE 代码。“CustomerViewModel”类代表了你的 UI,所以我们想保持它的属性和UI命名约定⼀致。你可以从下图看出来“CustomerViewModel”类的属性是如何从之前的 C ustomerModel 类中映射过来: “TxtCustomerName”对应“CustomerName”,“TxtAmount”对应“Amou nt”等等。
下⾯是实际代码:
public class CustomerViewModelmvc的三层架构
{
private Customer obj = new Customer();
public string TxtCustomerName
{
get { return obj.CustomerName; }
set { obj.CustomerName = value; }
}
public string TxtAmount
{
get { return Convert.ToString(obj.Amount) ; }            set { obj.Amount = Convert.ToDouble(value); }        }
public string LblAmountColor
{
get
{
if (obj.Amount > 2000)
{
return"Blue";
}
else if (obj.Amount > 1500)
{
return"Red";
}
return"Yellow";
}
}
public bool IsMarried
{
get
{
if (obj.Married == "Married")
{
return true;
}
else
{
return false;
}
}
}}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。