[原创译文][MVVM专题]__Advanced MVVM 第三章
Advanced MVVM [中英文全集]:
第四章:ViewModel架构简介
第五章:Animated Transitions
第六章:Animated Transitions 下的无限制撤销实现
第七章:Game-Over对话框
第八章:回顾
作者: Kingmoon
转载请注明: http://www.cnblogs.com/kingmoon
第三章__View架构简介
接下来两章是对BubbleBurst的View和ViewModel的布局预览.我们将会探索这个应用程序的高级别结构.随后的章节将会更深层次地探讨我们这个程序是如何解决这些共同存在于MVVM应用程序中的问题的.让我来检查一下这么多的Views是如何一起协同工作并创造这个BubbleBurst用户界面的.
BubbleBurst Viusal Studio 解决方案包含了3个工程.那个包含了可执行程序的工程叫BubbleBurst.它仅仅包含了用来配置应用程序对象和一个窗口的XAML 再浏览一下那些被BubbleBurst引用的工程.
下面你看到的这张截图是从Mole visualize截出来的. 这个游戏的visual Tree并不复杂.
上面高亮的节点代表着view和用户面板将会在下面的章节中出现.
BubbleBrustView
BubbleBurstView是最高级别的View. 他包括了应用程序的主窗口. 它包括了3个东西: Bubble的矩阵, 游戏结束的窗口,和一个关联菜单.它仅仅只是一个其他界面的容器. 它并没有太多的责任.就是除了UI加载完成后启动一个新游戏,处理键盘按键等. 这个应用程序中的最高级别的ViewModel对象将在这个View中创建.就像我们在下一个章节看到的一样.
接下来的这段xmal展现了BubbleBurstView控件.为了更清楚一点删除了一些无关的细节.
BubbleMatrixView
BubbleMatrixView是一个用来在固定大小的Gird中安排显示Bubbles的控件.它是一个ItemControl的子类,并且充分利用了ItemTemplate和ItemPanle属性来创建一个bubbles可以滑动和爆炸数据绑定矩阵.是不是感觉WPF很神奇呢?
下面是配置BubbleMatrixView的xaml代码.
这个BubbleMatrixView 代码隐藏部分里面包含了一个ItemsPanle 的Loaded事件处理方法. 我们将会在下一节中再次说明它. 现在我们来看看这个控件在ItemsPanle加载后做了什么.
还有一些更有趣的东西在BubbleMatrixView 的代码隐藏文件里面. 我们将会在第五章来说明他.现在让我们来看下ItemPanle是如何工作的.
BubbleCanvas
BubbleMatrixView里面使用的ItemPanle是一个被我叫做BubbleCanvas的自定义Panle. 它是从WPF的CanvasPanel中派生过来的并且引进了一些功能强大的函数来管理Bubble的位置.一个关键的特性就是初始化每个泡泡的位置.为了计算每个元素合适的位置,这个Panle用的一些关于泡泡逻辑位置的信息是从子元素的的ViewModel中得来的. 代码如下:
从前面那副应用程序虚拟树的截图中很容易看得出, 每个泡泡实例都是ContentPresenter的子元素. 而这些ContentPresenter是BubbleCanvas的子元素.这就是为什么用这个BubbleCanvas来调整位置和大小而不用BubbleView来调整的原因. 一旦这些安置好这些初始化元素的位置后.BubbleCanvas是不需要再调整他们了.所有这些元素的后继移动之类的实在别的地方体现的.这点在第五章会讲到.
BubbleView
每一个在泡泡矩阵中显示出来泡泡都是一个泡泡view的实例.这个控件是button的子类并且拥有3个元素.第一,它使用了Grid面板去去捕捉鼠标左键事件. 第二, 它使用了一个椭圆元素去渲染一个圆形的Bubble. 第三,用一个Border元素提供了光滑的阴影当一个Bubble在Bubble Group中被高亮时. 完整的xaml代码如下:
<Button
x:Class="BubbleBurst.View.BubbleView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Command="{Binding Path=BurstBubbleGroupCommand, Mode=OneTime}"
RenderTransformOrigin="0.5,0.5"
Template="{DynamicResource BubbleTemplate}">
<Button.Triggers>
<!-- This causes the bubble to animate into view. -->
<EventTrigger RoutedEvent="Button.Loaded">
<BeginStoryboard
Storyboard="{DynamicResource BubbleLoadedStoryboard}" />
</EventTrigger>
</Button.Triggers>
<!-- These transforms are used during animations. -->
<Button.RenderTransform>
<TransformGroup>
<ScaleTransform />
<TranslateTransform />
</TransformGroup>
</Button.RenderTransform>
<!-- The Grid captures mouse clicks. -->
<Grid
x:Name="HitTestArea"
x:FieldModifier="private"
Background="Transparent"
IsHitTestVisible="True"
Style="{StaticResource BubbleGridStyle}"
>
<!-- The Border provides a dark rim when in a bubble group. -->
<Border
x:Name="BubbleBackground"
x:FieldModifier="private"
IsHitTestVisible="False"
Style="{StaticResource BubbleBackgroundBorderStyle}"/>
<!-- The Ellipse draws a bubble. -->
<Ellipse
x:Name="Bubble"
x:FieldModifier="private"
IsHitTestVisible="False"
Style="{StaticResource BubbleEllipseStyle}" />
</Grid>
</Button>
请注意,这里的Button的命令属性是绑定到ViewModel下的BurstBubbleGroupCommand属性.当我们看到BubbleViewModel的时候我们会来了解他的.
这里有个分割出来的文件叫: BubbleViewResource.xaml 包含了一个资源字典.该字典里面的资源是用来渲染BubbleView和定义BubbleView的动画.
GameOver View
一旦游戏结束.这个GameOver对话框就会出现.它的行为像一个模式对话框, 一直强制等待用户作出选它才会消失.在这个View上面,会出现一些有关于游戏的成果信息,并且允许用户选择再来一次或者退出游戏.当游戏结束这个对话框就会弹出来,当用户选择了再来一次就会飞出去.
我们将会在第七章来了解他是如何工作的.
一个View1究竟应该做些什么?
在第二章,我曾经谈过一个现在还在激烈争论的一个话题—什么东西应该放进View的code-behind(注:就是每个.xaml下面的.xaml.cs.也称隐藏代码文件)中.一些人认为写多少都不是问题,他们声称这会使得开发更简单.因此,会提高你的工作效率. 我只想说: 除非你是总是做一个非常非常简单的用户界面, 所以他们被误导了.
其他人坚持我们不应该把任何一行代码放进code-behind中.他们是基于一种代码不属于code-behind这样的概念.呵呵,这是多么奇怪的行为. 正经地来说, 这些热衷于不允许在code-behind中写代码的观点通常是从代码可测性上来说.单元测试和整合测试通常提倡将更多的代码从一个view中体现出来. 因为UI对象写代码测试这种做法已经是很臭名昭著了而且非常难.然而这是这个观点的优点.但是从长远来看这个也是它的缺点,除非你愿意维护这些没有必要的抽象布局和仅仅只是为了测试每一行代码.当然他们也被误导了.(注: 这一点翻译的不好,因为我自己也看不太懂.可能大概意思是说: ui控件是一种很难做单元测试的东西,所以我们代码尽量不要放在code-behind中.放在vm中.那样更方便单元测试.)
有实践的开发者会从中选取一条折衷的道路并且能够判断哪些代码属于code-behind,而不是一味地去坚持那样的教条.贯穿整个BubbleBurst源代码,你可以发现有几个code-behind文件包含了一些特殊的逻辑去处理view的功能.在一些微不足道的用户界面上,是需要一些各种各样的代码才能是的view更好地工作.如果这些代码依赖与这些元素,资源,或者其他属于用户界面领域,我想没有理由不把它们放在view的code-behind中.这条规则的一个例外是在某些类型的问题上,可重用功能使用封装行为更有效.