1. Trang chủ
  2. » Công Nghệ Thông Tin

programming WPF phần 4 doc

88 392 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 88
Dung lượng 1,72 MB

Nội dung

238 | Chapter 7: Binding to List Data view.MoveCurrentToNext( ); if( view.IsCurrentAfterLast ) { view.MoveCurrentToLast( ); } } void addButton_Click(object sender, RoutedEventArgs e) { // Creating a new PeopleRow DataSourceProvider provider = (DataSourceProvider)this.FindResource("Family"); AdoBinding.Family.PeopleDataTable table = (AdoBinding.Family.PeopleDataTable)provider.Data; table.AddPeopleRow("Chris", 37); } void sortButton_Click(object sender, RoutedEventArgs e) { ICollectionView view = GetFamilyView( ); if( view.SortDescriptions.Count == 0 ) { view.SortDescriptions.Add( new SortDescription("Name", ListSortDirection.Ascending)); view.SortDescriptions.Add( new SortDescription("Age", ListSortDirection.Descending)); } else { view.SortDescriptions.Clear( ); } } void filterButton_Click(object sender, RoutedEventArgs e) { // Can't set the Filter property, but can set the // CustomFilter on a BindingListCollectionView BindingListCollectionView view = (BindingListCollectionView)GetFamilyView( ); if( string.IsNullOrEmpty(view.CustomFilter) ) { view.CustomFilter = "Age > 25"; } else { view.CustomFilter = null; } } void groupButton_Click(object sender, RoutedEventArgs e) { ICollectionView view = GetFamilyView( ); if( view.GroupDescriptions.Count == 0 ) { // Group by age view.GroupDescriptions.Add(new PropertyGroupDescription("Age")); } else { view.GroupDescriptions.Clear( ); } } } Example 7-38. Accessing the data held by ADO.NET (continued) Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Data Source Providers | 239 In Example 7-38, you’ll notice that manipulating and displaying a person is different because we’re dealing with a DataRowView object’s Row property to get the typed PeopleRow we want. Also, adding a new person is different because we’re dealing with a PeopleDataTable. Finally, filtering is different because the BindingListCollectionView doesn’t support the Filter property (setting it causes an exception at runtime). How- ever, we set the CustomFilter string on the BindingListCollectionView using the ADO. NET filter syntax. Everything else, though—including accessing the collection view, navigating the rows, and even sorting and grouping—is the same, as shown in Figure 7-20. So, although there was no relational data-specific data provider, none is needed—the object data provider works just fine for data binding to relational data in WPF. XML Data Source Provider In addition to object and relational data, WPF also supports binding to XML data. For instance, Example 7-39 shows some family data represented in XML. With this file available in the same folder as the executing application, we can bind to it using the XmlDataProvider, as shown in Example 7-40. Figure 7-20. ADO.NET data binding in action Example 7-39. A random family rendered in XML <Family xmlns="http://sellsbrothers.com"> <Person Name="Tom" Age="11" /> <Person Name="John" Age="12" /> <Person Name="Melissa" Age="38" /> </Family> Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 240 | Chapter 7: Binding to List Data The first thing I want to point out in Example 7-40 is the use of the XmlDataProvider with a relative URL that points to the family.xml file. The first thing you’ll probably notice, though, is the large amount of XAML to deal with namespaces. Looking back at the XML file (Example 7-39), you’ll notice that no prefix was used, only a default namespace of http://sellsbrothers.com. Using namespace prefixes in the XAML makes it possible to construct the XPath statement to find the set of Person elements in our sam- ple XML. Finally, notice the use of the XPath property in the Binding objects instead of the Path property, and the @ symbol to designate binding to an XML attribute. * Example 7-40. An XmlDataProvider in action <! Window1.xaml > <Window > <Window.Resources> <XmlDataProvider x:Key="Family" Source="family.xml" XPath="/sb:Family/sb:Person"> <XmlDataProvider.XmlNamespaceManager> <XmlNamespaceMappingCollection> <XmlNamespaceMapping Uri="http://sellsbrothers.com" Prefix="sb" /> </XmlNamespaceMappingCollection> </XmlDataProvider.XmlNamespaceManager> </XmlDataProvider> <local:AgeToForegroundConverter x:Key="ageConverter" /> </Window.Resources> <Grid DataContext="{StaticResource Family}"> <ListBox ItemsSource="{Binding}"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding XPath=@Name}" /> <TextBlock Text=" (age: " /> <TextBlock Text="{Binding XPath=@Age}" Foreground="{Binding XPath=@Age, Converter= {StaticResource ageConverter}}" /> <TextBlock Text=")" /> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid> </Window> * An explanation of the XPath syntax is beyond the scope of this book, but for a good reference, I’d start with Essential XML Quick Reference, by Aaron Skonnard and Martin Gudgin (Addison-Wesley Professional). Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Data Source Providers | 241 XML data islands If you happen to know your data at compile time, the XML data provider also sup- ports XML data islands, as shown in Example 7-41. In Example 7-41, we’ve copied the contents of family.xml under the XmlDataProvider element and wrapped it in an XData element to designate it as separate from the rest of how XAML is parsed (Appendix A is a good place to read up on that topic). We’ve also dropped the Source attribute (because the data is embedded), but left the XPath statement as it was. And as you might expect, now that we’re using XML instead of object data, some of the operations in our sample application need to be changed (see Example 7-42). Example 7-41. An XML data island in XAML <XmlDataProvider x:Key="Family" XPath="/sb:Family/sb:Person"> <XmlDataProvider.XmlNamespaceManager> <XmlNamespaceMappingCollection> <XmlNamespaceMapping Uri="http://sellsbrothers.com" Prefix="sb" /> </XmlNamespaceMappingCollection> </XmlDataProvider.XmlNamespaceManager> <x:XData> <Family xmlns="http://sellsbrothers.com"> <Person Name="Tom" Age="11" /> <Person Name="John" Age="12" /> <Person Name="Melissa" Age="38" /> </Family> </x:XData> </XmlDataProvider> Example 7-42. Managing XML bound data // Window1.xaml.cs using System.Xml; public partial class Window1 : Window { public Window1( ) { InitializeComponent( ); this.birthdayButton.Click += birthdayButton_Click; this.backButton.Click += backButton_Click; this.forwardButton.Click += forwardButton_Click; this.addButton.Click += addButton_Click; this.sortButton.Click += sortButton_Click; this.filterButton.Click += filterButton_Click; this.groupButton.Click += groupButton_Click; } Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 242 | Chapter 7: Binding to List Data ICollectionView GetFamilyView( ) { DataSourceProvider provider = (DataSourceProvider)this.FindResource("Family"); return CollectionViewSource.GetDefaultView(provider.Data); } void birthdayButton_Click(object sender, RoutedEventArgs e) { ICollectionView view = GetFamilyView( ); // Each "person" is an XmlElement and attribute // values come from a string-based indexer XmlElement person = (XmlElement)view.CurrentItem; person.SetAttribute("Age", (int.Parse(person.Attributes["Age"].Value) + 1).ToString( )); MessageBox.Show( string.Format( "Happy Birthday, {0}, age {1}!", person.Attributes["Name"].Value, person.Attributes["Age"].Value), "Birthday"); } void backButton_Click(object sender, RoutedEventArgs e) { ICollectionView view = GetFamilyView( ); view.MoveCurrentToPrevious( ); if( view.IsCurrentBeforeFirst ) { view.MoveCurrentToFirst( ); } } void forwardButton_Click(object sender, RoutedEventArgs e) { ICollectionView view = GetFamilyView( ); view.MoveCurrentToNext( ); if( view.IsCurrentAfterLast ) { view.MoveCurrentToLast( ); } } void addButton_Click(object sender, RoutedEventArgs e) { // Creating a new XmlElement XmlDataProvider provider = (XmlDataProvider)this.FindResource("Family"); XmlElement person = provider.Document.CreateElement("Person", "http://sellsbrothers.com"); person.SetAttribute("Name", "Chris"); person.SetAttribute("Age", "37"); provider.Document.ChildNodes[0].AppendChild(person); } Example 7-42. Managing XML bound data (continued) Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Data Source Providers | 243 Whereas in the ADO.NET example we used PeopleDataTable, PeopleDataRow, and DataRowView, in the XML example we use XmlDocument and XmlElement. For updating and accessing values, Example 7-42 uses the XmlElement SetAttribute method to change a value and the Attributes collection to get one. When adding a new person, we get the XmlDocument from the XmlDataProvider, ask it to create a new XmlElement, set the attributes, and add it to the child node collection of the document. When filtering, we simply cast to an XmlElement to access the attributes we need to make filter- ing decisions. Finally, when sorting or grouping, the descriptions include paths as XPath expressions (e.g., @Age). The results look just like Figure 7-20. void sortButton_Click(object sender, RoutedEventArgs e) { ICollectionView view = GetFamilyView( ); if( view.SortDescriptions.Count == 0 ) { view.SortDescriptions.Add( new SortDescription("@Name", ListSortDirection.Ascending)); view.SortDescriptions.Add( new SortDescription("@Age", ListSortDirection.Descending)); } else { view.SortDescriptions.Clear( ); } } void filterButton_Click(object sender, RoutedEventArgs e) { ICollectionView view = GetFamilyView( ); if( view.Filter == null ) { view.Filter = delegate(object item) { return int.Parse(((XmlElement)item).Attributes["Age"].Value) > 25; }; } else { view.Filter = null; } } void groupButton_Click(object sender, RoutedEventArgs e) { ICollectionView view = GetFamilyView( ); if( view.GroupDescriptions.Count == 0 ) { // Group by age view.GroupDescriptions.Add(new PropertyGroupDescription("@Age")); } else { view.GroupDescriptions.Clear( ); } } } Example 7-42. Managing XML bound data (continued) Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 244 | Chapter 7: Binding to List Data XML binding without the data source provider If you’ve already got a source of XML data that isn’t readily available for use by the XML data source provider, * you can programmatically bind to it instead, as shown in Example 7-43. * For example, if you need to retrieve XML data via an HTTP POST, you can’t use the XML data source pro- vider, as it can only use HTTP GET. Example 7-43. XML binding without the data source provider <! Window1.xaml > <Window > <Window.Resources> <! no XmlDataProvider > <local:AgeToForegroundConverter x:Key="ageConverter" /> </Window.Resources> <! DataContext set in code-behind > <Grid Name="grid"> </Grid> </Window> // Window1.xaml.cs public partial class Window1 : Window { // the family XML document XmlDocument doc; public Window1( ) { LoadFamilyXml( ); } void LoadFamilyXml( ) { // Load the XML using an XmlDocument doc = new XmlDocument( ); doc.Load("family.xml"); // Make the namespace prefix mappings available for use in binding XmlNamespaceManager manager = new XmlNamespaceManager(doc.NameTable); manager.AddNamespace("sb", "http://sellsbrothers.com"); Binding.SetXmlNamespaceManager(grid, manager); // Make the XML available for data binding. We use a binding here // because it will detect when the source document changes so it can // refresh the set of nodes returned by the XPath query Binding b = new Binding( ); b.XPath = "/sb:Family/sb:Person"; b.Source = doc; grid.SetBinding(Grid.DataContextProperty, b); } Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Master-Detail Binding | 245 In Example 7-43, we’re loading the XML manually from a file, but you can get access to the XML in whatever way is convenient, as long as you have an XmlNode or XmlNodeList to which to bind. Here we’re creating the XmlDocument as a member variable so that we can use it again to create and add a new XmlElement in the addButton_Click event handler. Notice also that we’re populating an XmlNamespaceManager and binding it to the grid so that binding knows how to trans- late XPath strings that use namespace prefixes. And finally, instead of setting the XML data directly as the grid’s DataContext, we’re actually binding it, along with the XPath to filter the set of nodes available in the XML data. The binding is there so that when the underlying XML data changes, resulting in a new set of nodes returned from the XPath expression, the grid’s data context is updated appropriately. Also, as this data context changes, the view may change, so we’re using the DataContext property of the grid to get the view in GetFamilyView each time we need it. The rest of the XML-related code in this sample does not have to change, as we’ve just done manually what the XML data source provider was doing for us (although we did leave out support for asynchronous access to the data, if it happens to be far away). Master-Detail Binding We’ve seen binding to a single object. We’ve seen binding to a single list of objects. Another very popular thing to do is to bind to more than one list, especially related lists. For example, if you’re showing your users a list of customers and then, when they select one, you’d like to show that customer’s related orders, you’ll want master-detail binding. ICollectionView GetFamilyView( ) { // The default view comes directly from the data return CollectionViewSource.GetDefaultView(grid.DataContext); } void addButton_Click(object sender, RoutedEventArgs e) { // Creating a new XmlElement XmlElement person = doc.CreateElement("Person", "http://sellsbrothers.com"); person.SetAttribute("Name", "Chris"); person.SetAttribute("Age", "37"); doc.DocumentElement.AppendChild(person); } } Example 7-43. XML binding without the data source provider (continued) Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 246 | Chapter 7: Binding to List Data Master-detail binding is a form of filtering, where the selection in the master list (e.g., customer 452) sets the filtering parameters for the associated detail data (e.g., orders for customer 452). In our discussion thus far, we don’t have customers and orders, but we do have fami- lies and people, which we could further formalize as shown in Example 7-44. In Example 7-44, we’ve got our familiar Person class with Name and Age properties, collected into a familiar People collection. Further, we have a Family class with a FamilyName property and a Members property of type People. Finally, we have a Families collection, which collects Family objects. In other words, families have members, which consist of people with names and ages. You could imagine instances of Families, Family, People, and Person that looked like Figure 7-21. Example 7-44. Master-detail data for binding public class Person { string name; public string Name { get { return name; } set { name = value; } } int age; public int Age { get { return age; } set { age = value; } } } public class People : ObservableCollection<Person> {} public class Family { string familyName; public string FamilyName { get { return familyName; } set { familyName = value; } } People members; public People Members { get { return members; } set { members = value; } } } public class Families : ObservableCollection<Family> {} Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Master-Detail Binding | 247 In Figure 7-21, the Families collection forms the master data, holding instances of the Family class, each of which holds a Members property of type People, which holds the detail Person data. You could populate instances of these data structures as shown in Example 7-45. Figure 7-21. Example master-detail data Example 7-45. Declaring example master-detail data <! Window1.xaml > <Window xmlns:local="clr-namespace:MasterDetailBinding"> <Window.Resources> <local:Families x:Key="Families"> <local:Family FamilyName="Stooge"> <local:Family.Members> <local:People> <local:Person Name="Larry" Age="21" /> <local:Person Name="Curly" Age="22" /> <local:Person Name="Moe" Age="23" /> </local:People> </local:Family.Members> </local:Family> Family .Name = "Stooges" .Members Family .Name = "Addams" .Members Families (Master) Person .Name = "Larry" .Age = 21 Person .Name = "Moe" .Age = 23 People (Details) Person .Name = "Curly" .Age = 22 Person .Name = "Gomez" .Age = 135 Person .Name = "Morticia" .Age = 121 People (Details) Person .Name = "Fester" .Age = 137 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com [...]... are things that WPF s data binding engine supports that we haven’t discussed (some of which we’ll get to in the next chapter, but some of which are beyond the scope of this book*) The thorough support for data binding at every level of WPF makes it a first-class feature in a way that data binding has never been before You’ll find that it permeates pretty much every aspect of your WPF programming, including... nicer-looking tic-tac-toe board Inline Styles A style in WPF is expressed as zero or more Setter objects inside a Style object Every element in WPF that derives from either FrameworkElement or FrameworkContentElement has a Style property, which you can set inline using standard XAML property element syntax, as shown in Example 8 -4 Example 8 -4 Setting an inline style ... Expanding our XAML a bit to include traits would look like Example 7 -49 Example 7 -49 Declaring a third level of detail 250 | Chapter 7: Binding to List Data Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Example 7 -49 Declaring a third level of detail (continued) WPF, a style is also a set of properties... binding for the next level To see this in action, let’s add one more level of detail to our data classes (see Example 7 -48 ) Master-Detail Binding | 249 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Figure 7-23 Showing master Family and detail Person data Example 7 -48 Adding a third level of detail public class Person { string name; public string Name { get { return name; } set... control Our element-typed styles return our game to looking again like Figure 8 -4 Element-typed styles are handy whenever you’d like all instances of a certain element to share a look, depending on the scope For example, we’ve scoped the button style in our sample thus far at the top-level Window (see Example 8- 14) Example 8- 14 Style scoped to the Window Binding to this data at the top level (i.e., to show the family names) could look like Example 7 -46 Example 7 -46 Binding to master Family data . the family XML document XmlDocument doc; public Window1( ) { LoadFamilyXml( ); } void LoadFamilyXml( ) { // Load the XML using an XmlDocument doc = new XmlDocument( ); doc. Load("family.xml"); . customer 45 2). In our discussion thus far, we don’t have customers and orders, but we do have fami- lies and people, which we could further formalize as shown in Example 7 -44 . In Example 7 -44 , we’ve. view.GroupDescriptions.Clear( ); } } } Example 7 -42 . Managing XML bound data (continued) Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 244 | Chapter 7: Binding to List Data XML

Ngày đăng: 13/08/2014, 08:20

TỪ KHÓA LIÊN QUAN