绑定(Binding)是WPF提供的一个非常方便的特性,它可以方便的实现一个WPF的MVVM结构。
既可以实现数据驱动UI变化,也可以做到End-user在UI上的修改实现的反映到数据上。
但是有一点必须注意的是:WPF的数据基本上都是使用的引用类型,引用类型的特性就是在传递过程中,数据本体是没有变化的。
可能你对数据进行了几次不经意的传递后,对变量做的修改竟然体现在了UI的变化。这时,Debug又变成了一件很痛苦的事情(尤其是当项目比较庞大的时候)。
首先来看一个比较常见的例子。
我有一个如上图的UI,左边一个ListView,右边有一个TextBox用于编辑,代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 using System.Collections.Generic; 2 using System.Collections.ObjectModel; 3 using System.ComponentModel; 4 using System.Runtime.CompilerServices; 5 using System.Windows; 6 7 namespace WpfApplication2 8 { 9 /// <summary> 10 /// Interaction logic for MainWindow.xaml 11 /// </summary> 12 public partial class MainWindow : Window 13 { 14 private MainViewModel FViewModel = null; 15 public MainViewModel ViewModel 16 { 17 get { return FViewModel; } 18 set 19 { 20 if (value != FViewModel) 21 FViewModel = value; 22 } 23 } 24 public MainWindow() 25 { 26 InitializeComponent(); 27 28 FViewModel = new MainViewModel(); 29 30 this.DataContext = ViewModel; 31 } 32 } 33 34 public class MainViewModel : INotifyPropertyChanged 35 { 36 public MainViewModel() 37 { 38 DataModels = new ObservableCollection<MainDataModel>(BuildHardCodeList()); 39 } 40 41 public event PropertyChangedEventHandler PropertyChanged; 42 private void RaisePropertyChanged([CallerMemberName] string aMembName = "") 43 { 44 if (PropertyChanged != null) 45 { 46 PropertyChanged(this, new PropertyChangedEventArgs(aMembName)); 47 } 48 } 49 50 private static IEnumerable<MainDataModel> BuildHardCodeList() 51 { 52 yield return new MainDataModel() { Name = "DataModel - 1" }; 53 yield return new MainDataModel() { Name = "DataModel - 2" }; 54 yield return new MainDataModel() { Name = "DataModel - 3" }; 55 yield return new MainDataModel() { Name = "DataModel - 4" }; 56 yield return new MainDataModel() { Name = "DataModel - 5" }; 57 } 58 59 public ObservableCollection<MainDataModel> DataModels { get; set; } 60 61 private MainDataModel FCurrentDataModel; 62 public MainDataModel CurrentDataModel 63 { 64 get { return FCurrentDataModel; } 65 set 66 { 67 if (value != FCurrentDataModel) 68 { 69 FCurrentDataModel = value; 70 RaisePropertyChanged(); 71 } 72 } 73 } 74 } 75 public class MainDataModel 76 { 77 public string Name { get; set; } 78 79 public override string ToString() 80 { 81 return Name; 82 } 83 } 84 }
实际运行起来后,我选中了一行以后,Name-TextBox用选中的item的Name进行了填充。
我修改了Name的内容,鼠标移到下一个TextBox。
这时发现ListView的相应记录也修改了。这就是引用类型产生的作用。
其时在大部分情况下,我们是不希望ListView的内容进行改变,直到我点击Save button。
要想实现这个需求就必须把CurrentDataModel与DataModels的连接打断掉。
现在DataModel - 1的数据存储方式如上图,堆栈中只有一份DataModel - 1, ListView与TextBox全都引用这一份数据,当TextBox进行修改时,这唯一的一份数据进行了修改,当然就会更新ListView。
要打断这种引用关系,就需要对DataModel - 1进行一次深拷贝(注意:一定是深拷贝,否则又只是一次简单的引用关系),数据的连接关系都通过深拷贝来实现,它们的关系如下图:
具体实现晚些时候有时间再放上... ...