WPF and Silverlight 学习笔记(二十四):数据源提供器(DataProvider)

在WPF中系统提供了两个数据源提供器(DataProvider):对象数据源提供器(ObjectDataProvider)和XML数据源提供器(XmlDataProvider)。其作用类似于ASP.Net数据源(DataSource)中的对象数据源(ObjectDataSource)和Xml数据源(XmlDataSource)。其继承结构如下:

24-1

ObjectDataProvider用于处理由方法返回值所产生的数据源,其应用非常广泛,通常多层应用程序通常在界面上使用ObjectDataProvider处理由组件层所产生的数据。在本节中我们主要处理ObjectDataProvider,对于XmlDataProvider感兴趣的朋友可以参考MSDN。

一、组件端定义

例如:定义一个类库项目,在其中定义一个ProductInfo类、CategoryInfo类,用来封装Northwind数据库中的Products表及Categories表中的数据。定义NorthwindDataSet,包含Product、Category两个DataTable。定义DataControl类,处理对Northwind数据库的操作,返回相应的封装后的类型或集合作为界面显示的数据源。

1、ProductInfo类和CategoryInfo类

   1: namespace WPF_24_Library
   2: {
   3:     /// <summary>
   4:     /// 封装产品表的信息
   5:     /// </summary>
   6:     public class ProductInfo
   7:     {
   8:         public int ProductID
   9:         {
  10:             set; get;
  11:         }
  12:         public string ProductName
  13:         {
  14:             set; get;
  15:         }
  16:         public decimal UnitPrice
  17:         {
  18:             set; get;
  19:         }
  20:         public int CategoryID
  21:         {
  22:             set; get;
  23:         }
  24:     }
  25: }

 

   1: using System.Collections.Generic;
   2:  
   3: namespace WPF_24_Library
   4: {
   5:     /// <summary>
   6:     /// 封装类别表的信息
   7:     /// </summary>
   8:     public class CategoryInfo
   9:     {
  10:         public CategoryInfo()
  11:         {
  12:             Products = new List<ProductInfo>();
  13:         }
  14:  
  15:         public int CategoryID
  16:         {
  17:             set; get;
  18:         }
  19:         public string CategoryName
  20:         {
  21:             set; get;
  22:         }
  23:  
  24:         /// <summary>
  25:         /// 封装该类别的所有产品
  26:         /// </summary>
  27:         public List<ProductInfo> Products
  28:         {
  29:             private set; get;
  30:         }
  31:     }
  32: }

2、类型化DataSet

此类型化DataSet由Visual Studio IDE生成:

24-2

3、DataControl类

DataControl类用来处理所有的数据库的操作,其功能分为以下几个部分:

  • 基于返回集合的方法GetAllProductInfo
  • 基于返回集合并带参数的方法GetProductInfoByCategoryID
  • 基于返回带主从关联数据的集合的方法GetAllCategoriesWithProducts
  • 基于返回带主从关联数据的类型化DataSet的方法GetNorthwindDataSet
   1: using System.Collections.Generic;
   2: using System.Data;
   3: using System.Data.SqlClient;
   4:  
   5: namespace WPF_24_Library
   6: {
   7:     public static class DataControl
   8:     {
   9:         // 连接字符串
  10:         private const string CONNECTION_STRING =
  11:             @"Server=.;Integrated Security=SSPI;Database=Northwind";
  12:  
  13:         // 所使用的各存储过程的名称
  14:         private const string SQL_GETALLPRODUCTINFO = "sp_GetAllProductInfo";
  15:         private const string SQL_GETPRODUCTINFOBYCATEGORYID = "sp_GetProductInfoByCategoryID";
  16:         private const string SQL_GETALLCATEGORIES = "sp_GetAllCategories";
  17:  
  18:         /// <summary>
  19:         /// 获取所有的产品
  20:         /// </summary>
  21:         /// <returns></returns>
  22:         public static List<ProductInfo> GetAllProductInfo()
  23:         {
  24:             SqlCommand command = new SqlCommand();
  25:             command.CommandType = CommandType.StoredProcedure;
  26:             command.CommandText = SQL_GETALLPRODUCTINFO;
  27:  
  28:             List<ProductInfo> result = GetProducts(command);
  29:  
  30:             return result;
  31:         }
  32:  
  33:         /// <summary>
  34:         /// 根据产品类别获取此类别的产品
  35:         /// </summary>
  36:         /// <param name="categoryID"></param>
  37:         /// <returns></returns>
  38:         public static List<ProductInfo> GetProductInfoByCategoryID(int categoryID)
  39:         {
  40:             SqlCommand command = new SqlCommand();
  41:             command.CommandType = CommandType.StoredProcedure;
  42:             command.CommandText = SQL_GETPRODUCTINFOBYCATEGORYID;
  43:  
  44:             command.Parameters.Add("@categoryID", SqlDbType.Int).Value = categoryID;
  45:  
  46:             List<ProductInfo> result = GetProducts(command);
  47:  
  48:             return result;
  49:         }
  50:  
  51:         /// <summary>
  52:         /// 封装产品数据
  53:         /// </summary>
  54:         /// <param name="command"></param>
  55:         /// <returns></returns>
  56:         private static List<ProductInfo> GetProducts(SqlCommand command)
  57:         {
  58:             SqlConnection connection = new SqlConnection();
  59:             connection.ConnectionString = CONNECTION_STRING;
  60:             connection.Open();
  61:  
  62:             command.Connection = connection;
  63:  
  64:             SqlDataReader dataReader = command.ExecuteReader();
  65:             List<ProductInfo> result = new List<ProductInfo>();
  66:             while (dataReader.Read())
  67:             {
  68:                 int id = dataReader.GetInt32(0);
  69:                 string name = dataReader.GetString(1);
  70:                 int categoryID = dataReader.GetInt32(2);
  71:                 decimal unitprice = dataReader.GetDecimal(3);
  72:  
  73:                 ProductInfo info = new ProductInfo()
  74:                 {
  75:                     ProductID = id,
  76:                     ProductName = name,
  77:                     CategoryID = categoryID,
  78:                     UnitPrice = unitprice
  79:                 };
  80:                 result.Add(info);
  81:             }
  82:             dataReader.Close();
  83:             connection.Close();
  84:  
  85:             return result;
  86:         }
  87:  
  88:         /// <summary>
  89:         /// 获取所有的类别及该类别的产品
  90:         /// </summary>
  91:         /// <returns></returns>
  92:         public static List<CategoryInfo> GetAllCategoriesWithProducts()
  93:         {
  94:             SqlConnection connection = new SqlConnection();
  95:             connection.ConnectionString = CONNECTION_STRING;
  96:             connection.Open();
  97:  
  98:             SqlCommand command = new SqlCommand();
  99:             command.CommandType = CommandType.StoredProcedure;
 100:             command.CommandText = SQL_GETALLCATEGORIES;
 101:  
 102:             SqlDataReader dataReader = command.ExecuteReader();
 103:             List<CategoryInfo> result = new List<CategoryInfo>();
 104:             while (dataReader.Read())
 105:             {
 106:                 int id = dataReader.GetInt32(0);
 107:                 string name = dataReader.GetString(1);
 108:  
 109:                 CategoryInfo info = new CategoryInfo()
 110:                 {
 111:                     CategoryID = id,
 112:                     CategoryName = name,
 113:                 };
 114:                 result.Add(info);
 115:             }
 116:             dataReader.Close();
 117:             connection.Close();
 118:  
 119:             foreach (CategoryInfo info in result)
 120:             {
 121:                 info.Products.AddRange(
 122:                     GetProductInfoByCategoryID(info.CategoryID));
 123:             }
 124:  
 125:             return result;
 126:         }
 127:  
 128:         /// <summary>
 129:         /// 获取封装所有类别及产品的DataSet
 130:         /// </summary>
 131:         /// <returns></returns>
 132:         public static NorthwindDataSet GetNorthwindDataSet()
 133:         {
 134:             SqlDataAdapter categoryAdapter = new SqlDataAdapter(
 135:                 SQL_GETALLCATEGORIES, CONNECTION_STRING);
 136:             categoryAdapter.SelectCommand.CommandType = CommandType.StoredProcedure;
 137:  
 138:             SqlDataAdapter productAdapter = new SqlDataAdapter(
 139:                 SQL_GETALLPRODUCTINFO, CONNECTION_STRING);
 140:             productAdapter.SelectCommand.CommandType = CommandType.StoredProcedure;
 141:  
 142:             NorthwindDataSet result = new NorthwindDataSet();
 143:  
 144:             categoryAdapter.Fill(result.Categories);
 145:             productAdapter.Fill(result.Products);
 146:  
 147:             return result;
 148:         }
 149:     }
 150: }

二、基本ObjectDataProvider操作

(将上部分类库项目引入WPF应用程序项目中)

使用ObjectDataProvider时需提供以下几个部分:

  • x:Key:此Provider对应的资源的名称,将在界面某组件的DataContext引用,以作为数据源
  • ObjectType:包含返回数据源的方法的类,其语法上是这样的:ObjectType=”{x:Type 类名}”,此外还需要引用相应的命名空间
  • MethodName:返回数据源的方法的方法名

例如:将GetAllProductInfo方法返回的List<ProductInfo>泛型集合做为数据源,绑定到ListBox上:

   1: <Window x:Class="WPF_24.WinObjectDataProviderDemo1"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:lib="clr-namespace:WPF_24_Library;assembly=WPF_24_Library"
   5:     Title="ObjectDataProvider Demo 1" Height="300" Width="300">
   6:     <Window.Resources>
   7:         <ObjectDataProvider x:Key="productInfoSource"
   8:                             ObjectType="{x:Type lib:DataControl}"
   9:                             MethodName="GetAllProductInfo" />
  10:     </Window.Resources>
  11:     <Grid>
  12:         <ListBox Margin="5"
  13:                  DataContext="{StaticResource productInfoSource}"
  14:                  ItemsSource="{Binding}"
  15:                  DisplayMemberPath="ProductName"
  16:                  SelectedValuePath="ProductID" />
  17:     </Grid>
  18: </Window>

此处的DataContext可以写在ListBox中,好可以写在Grid或Window的DataContext中。在ListBox中ItemsSource的值为默认的Binding,未定义Path,即直接将方法返回的List集合的结果显示在ListBox中。执行的结果如下:

24-3

二、为ObjectDataProvider设置参数

在ObjectDataProvider的MethodParameters中可以添加ObjectDataProvider的参数,例如使用GetProductInfoByCategoryID,根据类别查询产品:

   1: <Window x:Class="WPF_24.WinObjectDataProviderDemo2"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:lib="clr-namespace:WPF_24_Library;assembly=WPF_24_Library"
   5:     xmlns:System="clr-namespace:System;assembly=mscorlib"
   6:     Title="WinObjectDataProviderDemo2" Height="300" Width="300">
   7:     <Window.Resources>
   8:         <ObjectDataProvider x:Key="productInfoByCategoryIDSource"
   9:                             ObjectType="{x:Type lib:DataControl}"
  10:                             MethodName="GetProductInfoByCategoryID">
  11:             <ObjectDataProvider.MethodParameters>
  12:                 <System:Int32>0</System:Int32>
  13:             </ObjectDataProvider.MethodParameters>
  14:         </ObjectDataProvider>
  15:     </Window.Resources>
  16:     <Grid>
  17:         <Grid.RowDefinitions>
  18:             <RowDefinition Height="23" />
  19:             <RowDefinition />
  20:         </Grid.RowDefinitions>
  21:         <StackPanel Grid.Row="0" Orientation="Horizontal" 
  22:                     HorizontalAlignment="Center" VerticalAlignment="Center" >
  23:             <TextBlock Margin="5,0" Text="CategoryID:" VerticalAlignment="Center" />
  24:             <TextBox Margin="5,0" Width="80" Text="0" x:Name="txtCategoryID" />
  25:             <Button Margin="5,0" Width="50" Content="OK" Click="Button_Click" />
  26:         </StackPanel>
  27:         <ListBox Grid.Row="1"
  28:                  DataContext="{StaticResource productInfoByCategoryIDSource}"
  29:                  ItemsSource="{Binding}"
  30:                  DisplayMemberPath="ProductName"
  31:                  SelectedValuePath="ProductID" />
  32:     </Grid>
  33: </Window>

此外,可以通过代码更改参数的值:

   1: private void Button_Click(object sender, RoutedEventArgs e)
   2: {
   3:     int categoryID = int.Parse(txtCategoryID.Text);
   4:  
   5:     ObjectDataProvider provider =
   6:         (ObjectDataProvider)(this.FindResource("productInfoByCategoryIDSource"));
   7:     provider.MethodParameters[0] = categoryID;
   8: }

24-4

三、带主从关联数据的数据显示

24-5

所谓的“主从数据”所指的例如上图中,左侧为所有的类别,右侧为当前选中类别的所有产品,其实现的XAML如下:

   1: <Window x:Class="WPF_24.WinObjectDataProviderDemo4"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:lib="clr-namespace:WPF_24_Library;assembly=WPF_24_Library"
   5:     Title="WinObjectDataProviderDemo4" Height="300" Width="500">
   6:     <Window.Resources>
   7:         <ObjectDataProvider x:Key="myCategories"
   8:                             ObjectType="{x:Type lib:DataControl}"
   9:                             MethodName="GetAllCategoriesWithProducts" />
  10:     </Window.Resources>
  11:     <Grid DataContext="{StaticResource myCategories}">
  12:         <Grid.ColumnDefinitions>
  13:             <ColumnDefinition Width="2*" />
  14:             <ColumnDefinition Width="3*" />
  15:         </Grid.ColumnDefinitions>
  16:         <ListBox Grid.Column="0"
  17:                  ItemsSource="{Binding}"
  18:                  DisplayMemberPath="CategoryName"
  19:                  SelectedValuePath="CategoryID"
  20:                  SelectedIndex="0"
  21:                  IsSynchronizedWithCurrentItem="True" />
  22:         <ListBox Grid.Column="1"
  23:                  ItemsSource="{Binding Path=Products}"
  24:                  DisplayMemberPath="ProductName"
  25:                  SelectedValuePath="ProductID" />
  26:     </Grid>
  27: </Window>

需要注意的是,默认情况下,ListBox不会同步数据源当前项的变更改,需要使用第21行的代码,以实现点击左侧的ListBox的某一项,同时更改右侧该类别的产品。

四、使用DataSet做为数据源

将DataSet做为数据源时大体与集合相同,但要注意,DataContext对应的数据源类型是一个DataSet时,Binding时需使用Path指定控件对应绑定到的DataTable。

   1: <Window x:Class="WPF_24.WinObjectDataProviderDemo3"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:lib="clr-namespace:WPF_24_Library;assembly=WPF_24_Library"
   5:     Title="WinObjectDataProviderDemo3" Height="300" Width="500">
   6:     <Window.Resources>
   7:         <ObjectDataProvider x:Key="myDataSetSource"
   8:                             ObjectType="{x:Type lib:DataControl}"
   9:                             MethodName="GetNorthwindDataSet" />
  10:     </Window.Resources>
  11:     <Grid DataContext="{StaticResource myDataSetSource}" x:Name="grid">
  12:         <Grid.ColumnDefinitions>
  13:             <ColumnDefinition Width="2*" />
  14:             <ColumnDefinition Width="3*" />
  15:         </Grid.ColumnDefinitions>
  16:         <ListBox Grid.Column="0" SelectedIndex="0"
  17:                  ItemsSource="{Binding Path=Categories}"
  18:                  DisplayMemberPath="CategoryName"
  19:                  SelectedValuePath="CategoryID"
  20:                  SelectionChanged="ListBox_SelectionChanged"
  21:                  IsSynchronizedWithCurrentItem="True"
  22:                  x:Name="lst"/>
  23:         <ListBox Grid.Column="1"
  24:                  ItemsSource="{Binding Path=Categories/FK_Products_Categories}"
  25:                  DisplayMemberPath="ProductName"
  26:                  SelectedValuePath="ProductID" />
  27:     </Grid>
  28: </Window>

另外还需要注意的是第24行,绑定主从数据时,子数据绑定的Path为“主表名/关系名”。

应用程序执行的结果如下:

24-6

 

附:代码所使用的存储过程:

   1: use Northwind
   2: GO
   3:  
   4: Create Proc sp_GetAllProductInfo
   5: As
   6:     Select ProductID,ProductName,CategoryID,UnitPrice
   7:     From Products
   8:     
   9: GO
  10:  
  11: Create Proc sp_GetProductInfoByCategoryID
  12:     @categoryID int
  13: As
  14:     if @categoryID is null or @categoryID=0
  15:         Select ProductID,ProductName,CategoryID,UnitPrice
  16:         From Products
  17:     else
  18:         Select ProductID,ProductName,CategoryID,UnitPrice
  19:         From Products
  20:         Where CategoryID=@categoryID
  21:  
  22: GO
  23:  
  24: Create Proc sp_GetAllCategories
  25: As
  26:     Select CategoryID,CategoryName
  27:     From Categories

 

posted @ 2009-05-27 14:29  龙腾于海  阅读(5256)  评论(4编辑  收藏  举报