数据绑定是一个很强大且优雅的技能,之前用过好多次,但有些地方总不是特别清晰,常常需要重新翻阅资料来回顾,于是这次用了几天时间好好梳理一下,记录一下。
首先数据绑定对数据对象的要求:需要是公有属性(不支持字段和私有属性)。
如果需要在数据内容发生变化时自动更新到控件,则需要实现INotifyPropertyChanged接口,其包含 PropertyChanged事件,在属性变化时引发PropertyChanged事件。
如果绑定对象为集合时,需要在集合内容发生变化时自动更新到控件,则需要INotifyCollectionChanged接口,其包含CollectionChanged事件,目前内置的泛型集合 ObservableCollection 实现了该接口。
通常在设置绑定时,我们需要指定数据上下文,DataContext,如果控件没有设置DataContext,则会去找父容器的DataContext,依层级依次查找。也有些控件直接在代码中指定ItemsSource的。如果要绑定的数据依赖于另外一个控件的值,可以在DataContext中,绑定到另外一个控件的选中项,然后设置该控件的数据源或者绑定值。
MainWindow.xaml
cs
<Window x:Class="TestBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestBinding"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel Name="parentContainer">
<StackPanel Orientation="Horizontal">
<TextBlock>BindData:</TextBlock>
<TextBox Text="{Binding Path=BindData}"></TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock>BindDatas:</TextBlock>
<ListBox Name="exchangeListBox" ItemsSource="{Binding Path=Exchanges}" DisplayMemberPath="ExchangeID"></ListBox>
</StackPanel>
<StackPanel>
<ComboBox Name="instrumentCombbox" DataContext="{Binding ElementName=exchangeListBox, Path=SelectedItem}" ItemsSource="{Binding Path=Instruments}" DisplayMemberPath="InstrumentID"/>
</StackPanel>
<StackPanel Orientation="Horizontal" DataContext="{Binding ElementName=instrumentCombbox, Path=SelectedItem}">
<TextBlock>ExchangeID:</TextBlock>
<TextBox Text="{Binding Path=ExchangeID}"></TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal" DataContext="{Binding ElementName=instrumentCombbox, Path=SelectedItem}">
<TextBlock>InstrumentID:</TextBlock>
<TextBox Text="{Binding Path=InstrumentID}"></TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal" DataContext="{Binding ElementName=instrumentCombbox, Path=SelectedItem}">
<TextBlock>InstrumentName:</TextBlock>
<TextBox Text="{Binding Path=InstrumentName}"></TextBox>
</StackPanel>
</StackPanel>
</Window>
MainWindow.xaml.cs
cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
namespace TestBinding;
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual bool SetProperty<T>(ref T member, T value, [CallerMemberName] string? propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(member, value))
{
return false;
}
member = value;
OnPropertyChanged(propertyName);
return true;
}
}
public class ExchangeViewModel : ViewModelBase
{
private string exchangeID = string.Empty;
public string ExchangeID { get => exchangeID; set => SetProperty(ref exchangeID, value); }
public ObservableCollection<InstrumentViewModel> Instruments { get; set; } = new ObservableCollection<InstrumentViewModel>();
}
public class InstrumentViewModel : ViewModelBase
{
public InstrumentViewModel(string exchangeID, string instrumentID, string instrumentName)
{
ExchangeID = exchangeID;
InstrumentID = instrumentID;
InstrumentName = instrumentName;
}
private string _exchangeID = string.Empty;
private string _instrumentID = string.Empty;
private string _instrumentName = string.Empty;
public string ExchangeID { get => _exchangeID; set => SetProperty(ref _exchangeID, value); }
public string InstrumentID { get => _instrumentID; set => SetProperty(ref _instrumentID, value); }
public string InstrumentName { get => _instrumentName; set => SetProperty(ref _instrumentName, value); }
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
parentContainer.DataContext = this;
Exchanges = new();
ExchangeViewModel exchange1 = new ExchangeViewModel();
exchange1.ExchangeID = "SHSE";
exchange1.Instruments.Add(new InstrumentViewModel("SHSE", "601155", "新城控股"));
exchange1.Instruments.Add(new InstrumentViewModel("SHSE", "600036", "招商银行"));
exchange1.Instruments.Add(new InstrumentViewModel("SHSE", "600266", "城建发展"));
exchange1.Instruments.Add(new InstrumentViewModel("SHSE", "600837", "海通证券"));
exchange1.Instruments.Add(new InstrumentViewModel("SHSE", "601668", "中国建筑"));
ExchangeViewModel exchange2 = new ExchangeViewModel();
exchange2.ExchangeID = "SZSE";
exchange2.Instruments.Add(new InstrumentViewModel("SZSE", "000002", "万科A"));
exchange2.Instruments.Add(new InstrumentViewModel("SZSE", "000001", "平安银行"));
exchange2.Instruments.Add(new InstrumentViewModel("SZSE", "000623", "吉林敖东"));
exchange2.Instruments.Add(new InstrumentViewModel("SZSE", "002739", "万达电影"));
exchange2.Instruments.Add(new InstrumentViewModel("SZSE", "300642", "透景生命"));
Exchanges.Add(exchange1);
Exchanges.Add(exchange2);
}
public string BindData { get; set; } = "BindData";
public ObservableCollection<ExchangeViewModel> Exchanges { get; set; }
}