zoukankan      html  css  js  c++  java
  • [Silverlight入门系列]使用MVVM模式(7):ViewModel的INotifyPropertyChanged接口实现

    本文说说ViewModel的这个INotifyPropertyChanged接口可以用来做啥?

     

    举例1:我有个TabControl,里面放了很多View,每个由ViewModel控制,我想是想TabSelectionChanged就打开相应的ViewModel,怎么做?

    解答:用ViewModel的INotifyPropertyChanged接口实现,因为TabItem作为一个选择器就有 IsSelected属性,把这个属性绑定到ViewModel的IsSelected字段,然后这个字段改变的时候用INotifyPropertyChanged接口实现通知即可。整个流程用MVVM实现非常整洁。

    Xaml
    1 <TabControl ...>
    2 <TabControl.ItemContainerStyle>
    3 <Style TargetType="{x:Type TabItem}">
    4 <Setter Property="IsSelected"
    5 Value="{Binding Path=IsSelected,Mode=TwoWay}"/>
    6 </Style>
    7 </TabControl.ItemContainerStyle>
    8  </TabControl>
    ViewModel
    1 public class MyViewModel : INotifyPropertyChanged
    2 {
    3 private bool _isLoaded;
    4
    5 private void Load()
    6 {
    7 // code
    8   }
    9
    10 private bool _isSelected;
    11
    12 public bool IsSelected
    13 {
    14 get
    15 {
    16 return this._isSelected;
    17 }
    18 set
    19 {
    20 if (this._isSelected != value)
    21 {
    22 this._isSelected = value;
    23
    24 if (this._isSelected && !this._isLoaded)
    25 {
    26 this.Load();
    27 this._isLoaded = true;
    28 }
    29
    30 var propertyChanged = this.PropertyChanged;
    31 if (propertyChanged != null)
    32 {
    33 propertyChanged(this, new PropertyChangedEventArgs("IsSelected"));
    34 }
    35 }
    36 }
    37 }
    38
    39 public event PropertyChangedEventHandler PropertyChanged;
    40 }

     

    举例2:我有个TreeView,里面的项非常复杂,还需要惰性加载(点击了才取数据并展开),怎么用MVVM实现?彻底晕了

    解答:还是用ViewModel的INotifyPropertyChanged接口实现,要使得你的视图没有代码,就要你不再把TreeView当成一个存储数据的地方,而是看做一个展现数据的地方,那么一切都将水到渠成。这就是ViewModel这个想法的由来。而用INotifyPropertyChanged接口可以神奇的实现它们之间的解耦。

    Xaml
    1 <TreeView ItemsSource="{Binding FirstGeneration}">
    2 <TreeView.ItemContainerStyle>
    3 <!--
    4 This Style binds a TreeViewItem to a PersonViewModel.
    5 -->
    6 <Style TargetType="{x:Type TreeViewItem}">
    7 <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
    8 <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
    9 <Setter Property="FontWeight" Value="Normal" />
    10 <Style.Triggers>
    11 <Trigger Property="IsSelected" Value="True">
    12 <Setter Property="FontWeight" Value="Bold" />
    13 </Trigger>
    14 </Style.Triggers>
    15 </Style>
    16 </TreeView.ItemContainerStyle>
    17
    18 <TreeView.ItemTemplate>
    19 <HierarchicalDataTemplate ItemsSource="{Binding Children}">
    20 <TextBlock Text="{Binding Name}" />
    21 </HierarchicalDataTemplate>
    22 </TreeView.ItemTemplate>
    23  </TreeView>

    ViewModel
    1 public class PersonViewModel
    2 {
    3 public PersonViewModel(Person person)
    4 : this(person, null)
    5 {
    6 }
    7
    8 private PersonViewModel(Person person, PersonViewModel parent)
    9 {
    10 _person = person;
    11 _parent = parent;
    12
    13 _children = new ReadOnlyCollection<PersonViewModel>(
    14 (from child in _person.Children
    15 select new PersonViewModel(child, this))
    16 .ToList<PersonViewModel>());
    17 }
    18
    19 private bool _isSelected;
    20 public bool IsSelected
    21 {
    22 get { return _isSelected; }
    23 set
    24 {
    25 if (value != _isSelected)
    26 {
    27 _isSelected = value;
    28 this.OnPropertyChanged("IsSelected");
    29 }
    30 }
    31 }
    32
    33 private bool _isExpanded;
    34 public bool IsExpanded
    35 {
    36 get { return _isExpanded; }
    37 set
    38 {
    39 if (value != _isExpanded)
    40 {
    41 _isExpanded = value;
    42 this.OnPropertyChanged("IsExpanded");
    43 }
    44
    45 // Expand all the way up to the root.
    46   if (_isExpanded && _parent != null)
    47 _parent.IsExpanded = true;
    48 }
    49 }
    50 public string Name
    51 {
    52 get { return _person.Name; }
    53 }
    54 }
    55  public class Person
    56 {
    57 readonly List<Person> _children = new List<Person>();
    58 public IList<Person> Children
    59 {
    60 get { return _children; }
    61 }
    62
    63 public string Name { get; set; }
    64 }

    按需加载:

    View Code
    1 interface ITreeViewItemViewModel : INotifyPropertyChanged
    2 {
    3 ObservableCollection<TreeViewItemViewModel> Children { get; }
    4 bool HasDummyChild { get; }
    5 bool IsExpanded { get; set; }
    6 bool IsSelected { get; set; }
    7 TreeViewItemViewModel Parent { get; }
    8 }
    9  public TreeViewItemViewModel : ITreeViewItemViewModel
    10 {
    11 protected TreeViewItemViewModel(TreeViewItemViewModel parent, bool lazyLoadChildren)
    12 {
    13 _parent = parent;
    14
    15 _children = new ObservableCollection<TreeViewItemViewModel>();
    16
    17 if (lazyLoadChildren)
    18 _children.Add(DummyChild);
    19 }
    20 public bool IsExpanded
    21 {
    22 get { return _isExpanded; }
    23 set
    24 {
    25 if (value != _isExpanded)
    26 {
    27 _isExpanded = value;
    28 this.OnPropertyChanged("IsExpanded");
    29 }
    30
    31 // Expand all the way up to the root.
    32   if (_isExpanded && _parent != null)
    33 _parent.IsExpanded = true;
    34
    35 // Lazy load the child items, if necessary.
    36   if (this.HasDummyChild)
    37 {
    38 this.Children.Remove(DummyChild);
    39 this.LoadChildren();
    40 }
    41 }
    42 }
    43
    44  /// <summary>
    45  /// Returns true if this object's Children have not yet been populated.
    46  /// </summary>
    47  public bool HasDummyChild
    48 {
    49 get { return this.Children.Count == 1 && this.Children[0] == DummyChild; }
    50 }
    51
    52  /// <summary>
    53  /// Invoked when the child items need to be loaded on demand.
    54  /// Subclasses can override this to populate the Children collection.
    55  /// </summary>
    56  protected virtual void LoadChildren()
    57 {
    58 }
    59 }

    真正加载子项的工作留给子类去实现。它们重载LoadChildren方法来提供一个跟类型相关的加载子项的实现。比如下面的RegionViewModel类,它重载了该方法来加载State对象并且创建StateViewModel对象。

    View Code
    1 public class RegionViewModel : TreeViewItemViewModel
    2 {
    3 readonly Region _region;
    4
    5 public RegionViewModel(Region region)
    6 : base(null, true)
    7 {
    8 _region = region;
    9 }
    10
    11 public string RegionName
    12 {
    13 get { return _region.RegionName; }
    14 }
    15
    16 protected override void LoadChildren()
    17 {
    18 foreach (State state in Database.GetStates(_region))
    19 base.Children.Add(new StateViewModel(state, this));
    20 }
    21 }
    Xaml
    1 <TreeView ItemsSource="{Binding Regions}">
    2 <TreeView.ItemContainerStyle>
    3 <!--
    4 This Style binds a TreeViewItem to a TreeViewItemViewModel.
    5 -->
    6 <Style TargetType="{x:Type TreeViewItem}">
    7 <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
    8 <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
    9 <Setter Property="FontWeight" Value="Normal" />
    10 <Style.Triggers>
    11 <Trigger Property="IsSelected" Value="True">
    12 <Setter Property="FontWeight" Value="Bold" />
    13 </Trigger>
    14 </Style.Triggers>
    15 </Style>
    16 </TreeView.ItemContainerStyle>
    17
    18 <TreeView.Resources>
    19 <HierarchicalDataTemplate
    20 DataType="{x:Type local:RegionViewModel}"
    21 ItemsSource="{Binding Children}"
    22 >
    23 <StackPanel Orientation="Horizontal">
    24 <Image Width="16" Height="16"
    25 Margin="3,0" Source="Images"Region.png" />
    26 <TextBlock Text="{Binding RegionName}" />
    27 </StackPanel>
    28 </HierarchicalDataTemplate>
    29
    30 <HierarchicalDataTemplate
    31 DataType="{x:Type local:StateViewModel}"
    32 ItemsSource="{Binding Children}"
    33 >
    34 <StackPanel Orientation="Horizontal">
    35 <Image Width="16" Height="16"
    36 Margin="3,0" Source="Images"State.png" />
    37 <TextBlock Text="{Binding StateName}" />
    38 </StackPanel>
    39 </HierarchicalDataTemplate>
    40
    41 <DataTemplate DataType="{x:Type local:CityViewModel}">
    42 <StackPanel Orientation="Horizontal">
    43 <Image Width="16" Height="16"
    44 Margin="3,0" Source="Images"City.png" />
    45 <TextBlock Text="{Binding CityName}" />
    46 </StackPanel>
    47 </DataTemplate>
    48 </TreeView.Resources>
    49  </TreeView>

    未完待续。

    Powered By D&J (URL:http://www.cnblogs.com/Areas/)
  • 相关阅读:
    vue项目总结,所用到的技术点
    豆瓣电影个人项目总结
    博学谷项目总结,个人总结经验
    JavaScript数组的常用方法
    移动端base.css的内容,
    normalize.css可以对css初始化,不同浏览器可以统一初始样式
    jsp第十次作业
    jsp第九次作业
    jsp第八次作业
    jsp第七次作业
  • 原文地址:https://www.cnblogs.com/Areas/p/2169890.html
Copyright © 2011-2022 走看看