[Silverlight入门系列]使用MVVM模式(9): 想在ViewModel中控制TreeView节点展开?

很多童鞋看了我的博客以后也去实践MVVM,但却发现Silverlight实践中的MVVM很难实现,比纯粹的CodeBehind难度大很多。首先是原来在xaml.cs的CodeBehind部分很容易控制界面逻辑,现在这部分逻辑移到ViewModel里面去了以后,就很难调用CodeBind的部分;其次是很多View和ViewModel、或者一个ViewModel多个View,他们之间如何通

  

  很多童鞋看了我的博客以后也去实践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

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。