目前为止,我们已经简单的处理了对象。然而,这并不是数据的唯一来源;XML和突然想到的相关数据库,都是流行的选择。更进一步地,由于XML

相关数据库并不能存储数据为.NET对象,某些转换可能需要支持数据绑定,正如你会想到的,需要数据源对象上的.NET属性。而且即使我们可以直接在xaml中声明对象,仍然希望有一个层间接地从其他源中拉数据,甚至于将这个工作交给一个工作线程,如果说取回是一个呆板的操作。

简而言之,为了对象的转换和加载,我们希望间接的而不是直接的声明方式。对于这个间接方式,我们必须致力于IDataSource接口的实现,其中一种就是数据对象源。

4.4.1数据对象源

一种对IDataSource接口的实现是,为所有的操作提供一个间接的层,这些操作用于生成要绑定到的对象。例如,如果我们想要在Web上加载一组Person对象,我们需增强一些代码中的逻辑,如示例4-34

示例4-34

}

在示例4-34中,RemotePeopleLoader类从People集合类中派生,在构造器中检索数据,因为对象数据源希望它创建的对象是一个集合,正如示例4-35

示例4-35

《Programming WPF》翻译 第4章 4.数据源<Window.Resources>
《Programming WPF》翻译 第4章 4.数据源  《Programming WPF》翻译 第4章 4.数据源
《Programming WPF》翻译 第4章 4.数据源  
<ObjectDataSource
《Programming WPF》翻译 第4章 4.数据源    x:Key
="Family"
《Programming WPF》翻译 第4章 4.数据源    TypeName
="PersonBinding.RemotePeopleLoader"
《Programming WPF》翻译 第4章 4.数据源    Asynchronous
="True" />
《Programming WPF》翻译 第4章 4.数据源
</Window.Resources>
《Programming WPF》翻译 第4章 4.数据源
<Grid DataContext="{StaticResource Family}">
《Programming WPF》翻译 第4章 4.数据源  《Programming WPF》翻译 第4章 4.数据源
《Programming WPF》翻译 第4章 4.数据源  
<ListBox ItemsSource="{Binding}" 《Programming WPF》翻译 第4章 4.数据源>
《Programming WPF》翻译 第4章 4.数据源
</Grid>

ObjectDataSource元素通常位于资源块中,按名称在xaml的其他位置中使用。TypeName属性引用了集合类的完整的限定名称。

WPF中的大部分具有type参数的类,如DataTemplate元素的DataType属性,在设置中带上type扩展标记,这包括类,命名空间和使用mapping语法的编译集信息。

《Programming WPF》翻译 第4章 4.数据源<!-- set up DataTemplate for Bar.Quux in assembly foo -->
《Programming WPF》翻译 第4章 4.数据源
<?Mapping
《Programming WPF》翻译 第4章 4.数据源  XmlNamespace="local"
《Programming WPF》翻译 第4章 4.数据源  ClrNamespace="Bar"  Assembly="foo" /><Window 《Programming WPF》翻译 第4章 4.数据源 xmlns="local">
《Programming WPF》翻译 第4章 4.数据源  <Window.Resources>
《Programming WPF》翻译 第4章 4.数据源    <DataTemplate
《Programming WPF》翻译 第4章 4.数据源      DataType="{x:Type local:Quux}">《Programming WPF》翻译 第4章 4.数据源</DataTemplate>
《Programming WPF》翻译 第4章 4.数据源  </Window.Resources>
《Programming WPF》翻译 第4章 4.数据源  《Programming WPF》翻译 第4章 4.数据源
《Programming WPF》翻译 第4章 4.数据源</Window>

然而,ObjectDataSource以自己的方式设置type的信息。

《Programming WPF》翻译 第4章 4.数据源<!-- set up ObjectDataSource for Bar.Quux in assembly foo -->
《Programming WPF》翻译 第4章 4.数据源
<ObjectDataSource x:Key="foo" TypeName="Bar.Quux, foo" />

愿望是美好的,现实是残酷的。在RTM版本之前,两种技术都是合理的。

伴随着对象数据源担当数据和绑定之间的中介者,我们需要更新代码,当我们遍历People集合时(现在是一个基本类RemotePeopleLoader,但是仍然是Person对象的容器),正如示例4-36所示。

示例4-36

}

由于Family资源现在是一个ObjectDataSource,本身就是IDataSource接口的实现,在示例4-26中,当我们需要People集合的时候,我们将Family中的资源转换为IdataSource,并从Data属性中拉出这个集合。

即使数据源对象通过Data属性暴露他的数据,这并不意味着你必须绑定它。如果你注意到示例4-35,我们仍然像从前一样绑定了列表框。

《Programming WPF》翻译 第4章 4.数据源<!--do not bind to Path=Data -->
《Programming WPF》翻译 第4章 4.数据源
<ListBox ItemSource=”{Binding}” >

这样做的原因是WPFIDataSource提供内建的支持,因此没有必要间接地这样做。

4.4.1.1异步数据遍历

在示例4-35中,我们应用了Asynchronous属性,这是最有趣的一块功能:数据源对象提供给我们所欠缺的——当我们直接在xaml中声明对象图的时候。

Asynchronous属性设置为true时(默认为false),通过TypeName创建详细对象的任务就交给工作线程处理,当遍历过数据,仅仅在UI线程表现绑定。这与绑定到数据并不一样——数据是在网络流中遍历到的,但是这总比当一个长时间的遍历发生时阻塞了UI线程要好。

4.4.1.2传递参数

Asynchronous属性外,数据源对象还提供了Parameters属性,这是一个逗号分隔的字符串列表,作为一个字符串传递到由数据源对象创建的类型中。例如,如果我们要传递一组URL参数,用来尝试遍历其中的数据,我们可以使用Parameters参数如示例4-37

示例4-37

《Programming WPF》翻译 第4章 4.数据源<ObjectDataSource
《Programming WPF》翻译 第4章 4.数据源  
x:Key="Family"
《Programming WPF》翻译 第4章 4.数据源  TypeName
="PersonBinding.RemotePeopleLoader"
《Programming WPF》翻译 第4章 4.数据源  Asynchronous
="True"
《Programming WPF》翻译 第4章 4.数据源Parameters
="http://sellsbrothers.com/sons.dat, http://sellssisters.com/daughters.dat" />

在示例4-37中,我们已经添加了一个包含两个URL的列表,这将被转换为调用RemotePeopleLoader有两个参数的构造函数,如示例4-38

示例4-38

}

不幸的是,如果我们把其他的数据类型放入由数据源对象的Property属性支持的参数列表,如整型,这将不会被转换,即使构造函数拥有适当的类型是有效的;数据源对象只支持创建带有无参或有参构造函数的对象。如果每一个数据都必须转换,你就不得不这么做了。

4.4.2 XMLDataSource

正如我提及的,对象是仅由数据绑定支持的,但数据究竟不仅仅存为对象。实际上,大部分数据并不存储为对象。一种日益流行的方法是把数据存储到XML。例如,示例4-39显示了我们的家庭数据,表示以XML的形式。

示例4-39

《Programming WPF》翻译 第4章 4.数据源<!-- family.xml -->
《Programming WPF》翻译 第4章 4.数据源
<Family xmlns="">
《Programming WPF》翻译 第4章 4.数据源  
<Person Name="Tom" Age="9" />
《Programming WPF》翻译 第4章 4.数据源  
<Person Name="John" Age="11" />
《Programming WPF》翻译 第4章 4.数据源  
<Person Name="Melissa" Age="36" />
《Programming WPF》翻译 第4章 4.数据源
</Family>

这个文件作为可执行应用程序,在同样的文件夹中是有效的,我们能够使用XmlDataSource绑定到它,正如示例4-40所示。

示例4-40

《Programming WPF》翻译 第4章 4.数据源<!-- Window1.xaml -->
《Programming WPF》翻译 第4章 4.数据源
<Window 《Programming WPF》翻译 第4章 4.数据源>
《Programming WPF》翻译 第4章 4.数据源  
<Window.Resources>
《Programming WPF》翻译 第4章 4.数据源    《Programming WPF》翻译 第4章 4.数据源
《Programming WPF》翻译 第4章 4.数据源    
<XmlDataSource
《Programming WPF》翻译 第4章 4.数据源      
x:Key="Family"
《Programming WPF》翻译 第4章 4.数据源      Source
="family.xml"
《Programming WPF》翻译 第4章 4.数据源      XPath
="/Family/Person" />
《Programming WPF》翻译 第4章 4.数据源  
</Window.Resources>
《Programming WPF》翻译 第4章 4.数据源  
<Grid DataContext="{StaticResource Family}">
《Programming WPF》翻译 第4章 4.数据源    《Programming WPF》翻译 第4章 4.数据源
《Programming WPF》翻译 第4章 4.数据源    
<ListBox 《Programming WPF》翻译 第4章 4.数据源 ItemsSource="{Binding}">
《Programming WPF》翻译 第4章 4.数据源      
<ListBox.ItemTemplate>
《Programming WPF》翻译 第4章 4.数据源        
<DataTemplate>
《Programming WPF》翻译 第4章 4.数据源          
<StackPanel Orientation="Horizontal">
《Programming WPF》翻译 第4章 4.数据源            
<TextBlock TextContent="{Binding XPath=@Name}" />
《Programming WPF》翻译 第4章 4.数据源            
<TextBlock TextContent=" (age: " />
《Programming WPF》翻译 第4章 4.数据源            
<TextBlock TextContent="{Binding XPath=@Age}" 《Programming WPF》翻译 第4章 4.数据源 />
《Programming WPF》翻译 第4章 4.数据源            
<TextBlock TextContent=")" />
《Programming WPF》翻译 第4章 4.数据源          
</StackPanel>
《Programming WPF》翻译 第4章 4.数据源        
</DataTemplate>
《Programming WPF》翻译 第4章 4.数据源      
</ListBox.ItemTemplate>
《Programming WPF》翻译 第4章 4.数据源    
</ListBox>
《Programming WPF》翻译 第4章 4.数据源
《Programming WPF》翻译 第4章 4.数据源    
<TextBlock 《Programming WPF》翻译 第4章 4.数据源>Name:</TextBlock>
《Programming WPF》翻译 第4章 4.数据源    
<TextBox Text="{Binding XPath=@Name}" 《Programming WPF》翻译 第4章 4.数据源/>
《Programming WPF》翻译 第4章 4.数据源    
<TextBlock 《Programming WPF》翻译 第4章 4.数据源>Age:</TextBlock>
《Programming WPF》翻译 第4章 4.数据源    
<TextBox Text="{Binding XPath=@Age}" 《Programming WPF》翻译 第4章 4.数据源 />
《Programming WPF》翻译 第4章 4.数据源    《Programming WPF》翻译 第4章 4.数据源
《Programming WPF》翻译 第4章 4.数据源  
</Grid>
《Programming WPF》翻译 第4章 4.数据源
</Window>

注意到,XmlDataSource的使用,带着一个相对的URL指向family.xml文件,这个Xpath表达式在Family根元素下推出Person元素。在XAML文件中唯一改变的是,使用ObjectDataSource绑定NameAgeTextBox控件,而我们使用Xpath表达式代替了Path表达式

    *Xpath语法的说明草果了本书的范围,一个好的参考书目是,Essential XML Quick Reference by Aaron Skonnard and Martin Gudgin (Addison Wesley)

4.4.2.1 XML数据岛

如果你恰好在编译期知道你的数据,XML数据源也以同样的方式支持“数据岛”:由XAML直接创建对象,如示例4-41所示。

示例4-41

《Programming WPF》翻译 第4章 4.数据源<XmlDataSource x:Key="Family" XPath="/Family/Person">
《Programming WPF》翻译 第4章 4.数据源  
<Family xmlns="">
《Programming WPF》翻译 第4章 4.数据源    
<Person Name="Tom" Age="9" />
《Programming WPF》翻译 第4章 4.数据源    
<Person Name="John" Age="11" />
《Programming WPF》翻译 第4章 4.数据源    
<Person Name="Melissa" Age="36" />
《Programming WPF》翻译 第4章 4.数据源  
</Family>
《Programming WPF》翻译 第4章 4.数据源
</XmlDataSource>

在示例4-41中,我们将XmlDataSource元素下的内容复制到了family.xml中,去掉Source属性而保留了Xpath表达式。

尽管如此,既然我们使用XML替代了对象数据,我们示例中的操作需要改动,如访问和改变当前项(正如我们对Birthday按钮的实现),添加新项,排序和过滤。简而言之,任何我们使用Person对象集合的地方,都需要改动。另一方面,在数据项之间移动的一系列方法ICollectionView.MovingCurrentToXxx()继续工作的很好,我们的AgeToForegroundValueConverter也是这样。

IValueConverter.Convert的实现可以继续工作,因为我们对对象的字符串值进行语法解析,而不是直接将其转换为Int32。在Person对象的情形中使用转换是首选的,因为AgeInt32类型的,对其进行语法分析是不必要的。尽管如此,在XML以及我们的应用程序缺少XSD的情形,Age是一个String类型,因此解析它就是必要的了。

4.4.2.2 XML数据源和访问数据项

为了访问和操作XML数据源,取代你的自定义类型实例,你可以使用位于System.Xml命名空间的XMLElement实例,正如示例4-42所示。

示例4-42

《Programming WPF》翻译 第4章 4.数据源// Window1.xaml.cs
《Programming WPF》翻译 第4章 4.数据源
《Programming WPF》翻译 第4章 4.数据源
}

在示例4-42中,首先要注意的是GetFamilyView的实现,我们不再直接寻找People集合,而是实现由Xml数据源提供的IEnumerable接口。IEnumerable.NET中你能拥有的最简单接口,仍然有一个集合——是GetdefaultView方法所需要的。

还要注意示例4-42中集合视图的CurrentItem属性,是一个XmlElement实例。为了增加age,我们访问元素的Age属性,取出它的值,将其解析为一个整型,增加它的值,再将这个整型转换为String类型,设置为当前元素的新的Age属性值。显示每一个属性不过是对成对属性的访问。

4.4.2.3XML数据源以及添加数据项

当添加(或移除)一个数据项时,最好访问XmlDataSource自身,从而可以访问Document属性来创建和添加新元素,正如示例4-43

示例4-43

}

这里,我们使用了XmlDataSource来获取XmlDocument,以及使用XmlDodument来创建一个叫做Person的新元素(使之符合其余Person元素),设置NameAge属性,以及在Family根元素下添加这个元素(在顶级Document对象上ChildNodes[0]是有效的)。

4.4.2.4 XML数据源以及排序

Xml数据源的条目进行排序,大概会想起我们要使用XmlElements进行处理,正如示例4-44

示例4-44

}

在示例4-44中,我们进行了排序,正如先前一样,但是我们从NameAge属性中拉出数据并适当的进行转换。

4.4.2.5 XML数据源以及过滤

XML的过滤机制非常像对象的过滤,只是我们使用XmlElements进行处理,正如示例4-45

示例4-45

}

这里我们的过滤器使用了匿名委托,将每一个数据项转换为一个XmlElement元素来进行过滤。

4.4.3相关数据源

目前的版本,WPF没有直接支持绑定到相关的数据库,而且间接的支持范围并不是很广。作为WPF一个关于当前状态的绑定到相关数据的示例,我建议WinFX SDK示例提名为“Binding with Data in an ADO DataSet Sample

4.4.4自定义数据源

如果你愿意利用为遍历对象提供的间接数据源,但是没有一个内嵌数据源会使你满意,一个自定义的IDataSource实现应该会获得成功。例如,代替创建RemotePersonLoader集合来加载或移除家庭数据(在集合的构造函数中添加集合项,无论如何都有点做作),我们将要创建一个自定义的IDataSource实现,来达到这一点,如示例4-46

示例4-46

}

在示例4-46中,通过创建一个People集合的实例,我们已经实现了IDataSource接口,而且,在构造函数中,在一个神秘的数据遍历过程之后,我们激发了一个事件,让数据绑定知道我们已经得到了数据,还有再次检查Data属性。这个协议特别有用——一旦你进行异步的数据遍历(像对象数据源那样)。

如果你的数据源通过自定义属性,像Asynchronous,一个或更多属性可以在运行期被改变。如果你已经得到了多个影响数据遍历的属性,你可能不想开始搜索新数据直到Refresh方法被调用,你可能开始于一个属性的改变,但是在客户端有机会改变其他的属性之前。

相关文章: