很多童鞋看了我的博客以后也去实践MVVM,但却发现Silverlight实践中的MVVM很难实现,比纯粹的CodeBehind难度大很多。首先是原来在xaml.cs的CodeBehind部分很容易控制界面逻辑,现在这部分逻辑移到ViewModel里面去了以后,就很难调用CodeBind的部分;其次是很多View和ViewModel、或者一个ViewModel多个View,他们之间如何通信?这些问题貌似非常复杂。其实很简单,就是分割、分工、协同。搞清楚谁应该负责什么,如何抽象和隔离、然后如何用适合的技术来实现。这些问题想清楚了搞清楚了就不难了。
提出问题:MVVM的TreeView不能在ViewModel中按需展开TreeNode节点
今天要讲的就是一个很常见的问题:你的TreeView已经用MVVM模式实现了(参见这一篇:[Silverlight入门系列]Prism中TreeView真正实现MVVM模式和Expanded发生时异步动态加载子节点(WCFRiaService)),但你在ViewModel的业务逻辑加载了节点以后要自动展开第一个节点怎么办?你发现现在不能直接调用Node.Expand()方法了,因为你在ViewModel中。怎么办?本文的方法其实很简单,也是Silverlight中MVVM消除CodeBehind的万能膏药:DataTrigger + Behavior。(其它方法参见我的上一篇:[Silverlight入门系列]使用MVVM模式(9): 想在ViewModel中控制Storyboard动画?)
解决思路:Silverlight中MVVM消除CodeBehind的万能膏药:DataTrigger + Behavior
首先实现一个Behavior,这个行为就是展开TreeView的Root节点喽。然后这个Behavior什么时候执行呢?我们需要是按需执行。呼叫它的时候它才执行。那就在ViewModel里面加一个属性,这个属性实现了INotifyPropertyChanged接口,当ViewModel里面一段逻辑执行完毕需要呼叫它展开节点了,那就设置这个属性为True,它就会因为绑定自动通知界面。而Behavior和这个属性怎么联系起来?那就是通过Trigger,监控这个属性变化,当值变为需要的值就自动执行你的Behavior。思路就是这样。
实现代码
上面那个图是最后的XAML代码了。先来看看展开根节点的Behavior:
1: using System.Windows.Controls;
2: using System.Windows.Interactivity;
3: using System.ComponentModel;
4: using System;
5:
6: namespace SilverlightApplication1
7: {
8: [System.ComponentModel.Description("Expands Tree Root Node")]
9: public class TreeRootExpandBehavior : TargetedTriggerAction<LazyTreeView>, INotifyPropertyChanged
10: {
11: #region "Initialization"
12:
13: private LazyTreeView objTreeView;
14:
15: /// <summary>
16: /// Called after the action is attached to an AssociatedObject.
17: /// </summary>
18: protected override void OnAttached()
19: {
20: base.OnAttached();
21:
22: objTreeView = (LazyTreeView)(AssociatedObject);
23: }
24:
25: /// <summary>
26: /// Invokes the action.
27: /// </summary>
28: /// <param name="parameter">The parameter to the action. If the action does not require a parameter, the parameter may be set to a null reference.</param>
29: protected override void Invoke(object parameter)
30: {
31: ExpandRoot();
32: }
33:
34: #endregion
35:
36: #region "Expands the root"
37:
38: /// <summary>
39: /// Expands the root.
40: /// </summary>
41: private void ExpandRoot()
42: {
43: objTreeView.UpdateLayout();
44:
45: var vm = (MyViewModel)objTreeView.DataContext; //MyViewModel是你的TreeView的ViewModel
46: if (vm == null) return;
47:
48: var root = vm.Root;//这个是ViewModel的一个公共属性,你的可能不一样,知道原理就行了
49: if (vm.Root == null || vm.Root.Count == 0) return;
50:
51: var objTreeViewItem = (TreeViewItem)objTreeView.ItemContainerGenerator.ContainerFromItem(root[0]);
52: if (objTreeViewItem != null)
53: {
54: objTreeViewItem.IsExpanded = true;
55: objTreeView.UpdateLayout();
56: }
57: }
58:
59: #endregion
60:
61: #region "INotifyPropertyChanged Implementation"
62:
63: public event PropertyChangedEventHandler PropertyChanged;
64:
65: /// <summary>
66: /// Notifies the property changed.
67: /// </summary>
68: /// <param name="info">The info.</param>
69: private void NotifyPropertyChanged(String info)
70: {
71: if (PropertyChanged != null)
72: {
73: PropertyChanged(this, new PropertyChangedEventArgs(info));
74: }
75: }
76: #endregion
77: }
78: }
在ViewModel里面加个属性
当ViewModel里面一段逻辑执行完毕需要呼叫它展开节点了,那就设置这个属性为True,它就会因为绑定自动通知界面。而Behavior和这个属性怎么联系起来?那就是通过Trigger,监控这个属性变化,当值变为需要的值就自动执行你的Behavior。注意引用几个命名空间:
1: //引用下面命名空间:
2: System.Windows.Interactivity
3: Microsoft.Expression.Interactions
4:
5: //在Xaml中:
6: xmlns:ei="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
7: xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
本文来自Mainz的博客,原文地址:http://www.cnblogs.com/Mainz/archive/2011/09/20/2182267.html