因此,为了我们的应用程序的功能性如图4-9所示,数据同步的代码减少到,为每一个显示数据的xaml属性设置一个绑定对象,以及使用数据上下文为Binding搜索数据。没有必要初始化UI代码或者事件句柄,来复制和转换数据(注意示例4-11中椭圆的不足)
清楚起见,实现INotifyPropertyChanged的用途绝非偶然。这是WPF数据绑定引擎保持UI同步于对象属性改变的接口。没有这个接口,UI的改变仍然可以传达到对象,但是绑定引擎没有办法知道什么时候改变UI。
一个没有实现INotifyPropertyChanged接口的对象发生改变,绑定引擎没有办法知道什么时候改变UI——这种说法不是完全正确。一种可以知道的方法是,如果一个对象实现了PropertyNameChanged事件——正如.NET 1.x中规定的数据绑定(如SizeChanged,TextChanged等等)——而.NET是向后兼容的。另一种方法是,手动调用BindingExpression对象的UpdateTarget方法,联合赈灾讨论的绑定机制。
然而,可靠的说,实现INotifyPropertyChanged是一种可取的方式,来支持属性改变通知,在WPF数据绑定中。
4.2.3可声明的数据
当我们的程序还在尝试去模仿更复杂的应用程序时,可能是从持久化形式加载“私人数据”,以及将其保存在应用程序的Session中,不难想象这样的场景,在编译期就知道某些数据的位置。可能是示例数据(如前面示例中的Tom);或者是众所周知的数据,而且在Session中不会改变,如
应用程序默认设置或错误信息。很多应用程序有独立于UI工作的字符串资源,但是仍然包装在这个应用程序中——这样做使之易于维护和本地化,将其脱离与UI逻辑本身,降低了数据与UI的耦合。到目前为止,在我们的示例中,我们已经将这些众所周知的数据保存在代码中,但是xaml是一个更好的选择,不仅因为易于在xaml中维护数据,还有xaml对本地化的支持(在第6章介绍)。
正如在第1章讨论到的,xaml是一种描述对象图表的语言,因此实际上任何带有默认构造函数的类型都可以在xaml中初始化,回忆示例4-2,Person类有一个默认的构造函数,因此我们可以创建一个Person实例在我们的xaml应用程序中,如示例4-12。
示例4-12
<?Mapping XmlNamespace="local" ClrNamespace="PersonBinding" ?>
<Window xmlns:local="local">
<Window.Resources>
<local:Person x:Key="Tom" Name="Tom" Age="9" />
</Window.Resources>
<Grid></Grid>
</Window>
我们在window标签中的资源元素中创建了一些“数据岛”,使用xaml的映射语法(在第一章介绍),引进了Person类型。
通过在xaml中使用指定的Person标签,我们能够声明性的设置grid的DataContext属性,而不是在后台代码文件中以编程方式设置,如示例4-13。
示例4-13
<!-- Window1.xaml -->
<?Mapping XmlNamespace="local" ClrNamespace="PersonBinding" ?>
<Window xmlns:local="local">
<Window.Resources>
<local:Person x:Key="Tom" Name="Tom" Age="9" />
</Window.Resources>
<Grid DataContext="{StaticResource Tom}">
<TextBlock >Name:</TextBlock>
<TextBox Text="{Binding Path=Name}" />
<TextBlock >Age:</TextBlock>
<TextBox Text="{Binding Path=Age}" />
<Button x:Name="birthdayButton">Birthday</Button>
</Grid>
</Window>
现在,我们将Person对象的创建转移到了xaml中,我们已经更新了Birthday按钮的Click事件句柄,从使用一个成员变量到使用定义在资源中的数据,正如示例4-14所示。
示例4-14
public partial class Window1 : Window {
void birthdayButton_Click(object sender, RoutedEventArgs e) {
Person person = (Person)this.FindResource("Tom"));
++person.Age;
MessageBox.Show();
}
}
在示例4-14中,我们使用了FindResource方法(在第一章介绍过,会在第6章详细介绍),用来从主窗体的资源中拖出Person对象。通过最小的改动,结果
是再次呈现图4-6所示的两个窗体。唯一的不同是不必接触后台代码文件就可以在编译期维护或本地化已知数据。(第6章讨论了本地化xaml资源)
4.2.4显示的数据源
一旦你已经得到一个命名的资源,你可以在xaml中显示的绑定对象源,而不是依赖于隐式的绑定到控件树上某处的一个非空的DataContext属性。显示的数据源方式是有用的,如果你有多个数据源,例如,两个Person对象。设置显示的数据源给绑定中的Source属性,如示例4-15所示。
示例4-15
<!-- Window1.xaml -->
<Window >
<Window.Resources>
<local:Person x:Key="Tom" />
<local:Person x:Key="John" />
</Window.Resources>
<Grid>
<TextBox x:Name="tomTextBox"
Text="
{Binding
Path=Name,
Source={StaticResource Tom}}" />
<TextBox x:Name="johnTextBox"
Text="
{Binding
Path=Name,
Source={StaticResource John}}" />
</Grid>
</Window>