基于数据的自定义格式设置 (C#)
可以通过多种方式根据绑定到 GridView、DetailsView 或 FormView 的数据调整其格式。 在本教程中,我们将了解如何通过使用 DataBound 和 RowDataBound 事件处理程序来完成数据绑定格式设置。
简介
GridView、DetailsView 和 FormView 控件的外观可以通过大量与样式相关的属性进行自定义。 、CssClass
Font
、、BorderWidth
BorderStyle
、BorderColor
、Width
、 和 Height
等属性决定了呈现控件的一般外观。 包括 HeaderStyle
、RowStyle
AlternatingRowStyle
、 等的属性允许将这些相同的样式设置应用于特定节。 同样,这些样式设置可以在字段级别应用。
但是,在许多情况下,格式设置要求取决于所显示数据的值。 例如,为了引起人们对缺货产品的注意,列出产品信息的报表可能会将和 UnitsOnOrder
字段都等于 0 的产品UnitsInStock
的背景色设置为黄色。 为了突出显示更昂贵的产品,我们可能需要用粗体显示那些价格超过 75.00 美元的产品的价格。
可以通过多种方式根据绑定到 GridView、DetailsView 或 FormView 的数据调整其格式。 在本教程中,我们将了解如何通过使用 DataBound
和 RowDataBound
事件处理程序完成数据绑定格式设置。 在下一教程中,我们将探讨一种替代方法。
使用 DetailsView 控件的DataBound
事件处理程序
从数据源控件或通过以编程方式将数据分配给控件的 DataSource
属性并调用其 DataBind()
方法将数据绑定到 DetailsView 时,将发生以下一系列步骤:
- 触发数据 Web 控件的事件
DataBinding
。 - 数据绑定到数据 Web 控件。
- 触发数据 Web 控件的事件
DataBound
。
在步骤 1 和 3 之后,可以通过事件处理程序立即注入自定义逻辑。 通过为 事件创建事件处理程序, DataBound
我们可以以编程方式确定已绑定到数据 Web 控件的数据,并根据需要调整格式。 为了说明这一点,让我们创建一个 DetailsView,它将列出有关产品的一般信息,但如果该值超过 75.00 美元,则会以粗体斜体字体显示UnitPrice
该值。
步骤 1:在 DetailsView 中显示产品信息
CustomColors.aspx
打开 文件夹中的页面CustomFormatting
,将“工具箱”中的 DetailsView 控件拖到Designer上,将其ID
属性值设置为 ExpensiveProductsPriceInBoldItalic
,并将其绑定到调用ProductsBLL
类的 GetProducts()
方法的新 ObjectDataSource 控件。 为简洁起见,此处省略了完成此操作的详细步骤,因为我们在前面的教程中详细检查了这些步骤。
将 ObjectDataSource 绑定到 DetailsView 后,请花点时间修改字段列表。 我已选择删除 ProductID
、、SupplierID
、CategoryID
、ReorderLevel
UnitsInStock
UnitsOnOrder
、 和 Discontinued
BoundField,并重命名并重新格式化了剩余的 BoundField。 我还清除了 Width
和 Height
设置。 由于 DetailsView 仅显示一条记录,因此我们需要启用分页,以便最终用户能够查看所有产品。 为此,请选中 DetailsView 的智能标记中的“启用分页”复选框。
图 1:选中“详细信息”视图的“智能标记”中的“启用分页”复选框 (单击以查看全尺寸图像)
完成这些更改后,DetailsView 标记将为:
<asp:DetailsView ID="DetailsView1" runat="server" AllowPaging="True"
AutoGenerateRows="False" DataKeyNames="ProductID"
DataSourceID="ObjectDataSource1" EnableViewState="False">
<Fields>
<asp:BoundField DataField="ProductName" HeaderText="Product"
SortExpression="ProductName" />
<asp:BoundField DataField="CategoryName" HeaderText="Category"
ReadOnly="True" SortExpression="CategoryName" />
<asp:BoundField DataField="SupplierName" HeaderText="Supplier"
ReadOnly="True" SortExpression="SupplierName" />
<asp:BoundField DataField="QuantityPerUnit"
HeaderText="Qty/Unit" SortExpression="QuantityPerUnit" />
<asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}"
HeaderText="Price"
HtmlEncode="False" SortExpression="UnitPrice" />
</Fields>
</asp:DetailsView>
花点时间在浏览器中测试此页面。
图 2:DetailsView 控件一次显示一个产品 (单击以查看全尺寸图像)
步骤 2:以编程方式确定 DataBound 事件处理程序中的数据值
为了以粗体斜体字体显示价值超过 75.00 美元的产品的 UnitPrice
价格,我们需要首先能够以编程方式确定 UnitPrice
该值。 对于 DetailsView,可以在事件处理程序中 DataBound
完成此操作。 若要创建事件处理程序,请单击Designer中的 DetailsView,然后导航到属性窗口。 如果它不可见,请按 F4 将其打开,或者转到“视图”菜单并选择“属性窗口”菜单选项。 在属性窗口中,单击闪电图标以列出 DetailsView 的事件。 接下来,双击 DataBound
事件或键入要创建的事件处理程序的名称。
图 3:为 DataBound
事件创建事件处理程序
这样做将自动创建事件处理程序,并将你带到已添加它的代码部分。 此时,你将看到:
protected void ExpensiveProductsPriceInBoldItalic_DataBound(object sender, EventArgs e)
{
}
可以通过 属性访问 DataItem
绑定到 DetailsView 的数据。 回想一下,我们正在将控件绑定到强类型 DataTable,该数据表由强类型 DataRow 实例集合组成。 当 DataTable 绑定到 DetailsView 时,会将 DataTable 中的第一个 DataRow 分配给 DetailsView 的 DataItem
属性。 具体而言,为 DataItem
属性分配对象 DataRowView
。 可以使用 DataRowView
的 Row
属性来访问基础 DataRow 对象,该对象实际上是一个 ProductsRow
实例。 有了此 ProductsRow
实例后,只需检查对象的属性值即可做出决策。
以下代码演示如何确定绑定到 DetailsView 控件的值是否 UnitPrice
大于 $75.00:
protected void ExpensiveProductsPriceInBoldItalic_DataBound(object sender, EventArgs e)
{
// Get the ProductsRow object from the DataItem property...
Northwind.ProductsRow product = (Northwind.ProductsRow)
((DataRowView)ExpensiveProductsPriceInBoldItalic.DataItem).Row;
if (!product.IsUnitPriceNull() && product.UnitPrice > 75m)
{
// TODO: Make the UnitPrice text bold and italic
}
}
注意
由于UnitPrice
数据库中可以有值NULL
,因此我们首先检查,以确保在访问 ProductsRow
的 UnitPrice
属性之前不处理NULL
值。 此检查很重要,因为如果我们尝试在属性具有NULL
值时访问UnitPrice
该属性,对象ProductsRow
将引发 StrongTypingException 异常。
步骤 3:设置 DetailsView 中的 UnitPrice 值的格式
此时,我们可以确定绑定到 DetailsView 的值是否 UnitPrice
超过 $75.00,但我们尚未了解如何以编程方式相应地调整 DetailsView 的格式。 若要修改 DetailsView 中整行的格式,请使用 DetailsViewID.Rows[index]
以编程方式访问该行;若要修改特定单元格,访问使用 DetailsViewID.Rows[index].Cells[index]
。 对行或单元格的引用后,可以通过设置其样式相关属性来调整其外观。
以编程方式访问行需要知道行的索引,该索引从 0 开始。 该 UnitPrice
行是 DetailsView 中的第五行,为它提供索引 4,并使它可通过编程方式使用 ExpensiveProductsPriceInBoldItalic.Rows[4]
进行访问。 此时,可以使用以下代码以粗体斜体字体显示整行的内容:
ExpensiveProductsPriceInBoldItalic.Rows[4].Font.Bold = true;
ExpensiveProductsPriceInBoldItalic.Rows[4].Font.Italic = true;
但是, 这会使标签 (Price) 以及值加粗和斜体。 如果只想将值设置为粗体和斜体,则需要将此格式应用于行中的第二个单元格,这可以使用以下命令完成:
ExpensiveProductsPriceInBoldItalic.Rows[4].Cells[1].Font.Bold = true;
ExpensiveProductsPriceInBoldItalic.Rows[4].Cells[1].Font.Italic = true;
由于到目前为止,我们的教程已使用样式表来保持呈现的标记与样式相关信息之间的干净分离,而不是设置上面所示的特定样式属性,让我们改用 CSS 类。 Styles.css
打开样式表,添加具有以下定义的名为 ExpensivePriceEmphasis
的新 CSS 类:
.ExpensivePriceEmphasis
{
font-weight: bold;
font-style: italic;
}
然后,在 DataBound
事件处理程序中,将单元格的 CssClass
属性设置为 ExpensivePriceEmphasis
。 以下代码显示 DataBound
整个事件处理程序:
protected void ExpensiveProductsPriceInBoldItalic_DataBound(object sender, EventArgs e)
{
// Get the ProductsRow object from the DataItem property...
Northwind.ProductsRow product = (Northwind.ProductsRow)
((DataRowView)ExpensiveProductsPriceInBoldItalic.DataItem).Row;
if (!product.IsUnitPriceNull() && product.UnitPrice > 75m)
{
ExpensiveProductsPriceInBoldItalic.Rows[4].Cells[1].CssClass =
"ExpensivePriceEmphasis";
}
}
查看成本低于 75.00 美元的 Chai 时,价格以普通字体显示 (见图 4) 。 但是,当查看价格为 97.00 美元的 Mishi Kobe Niku 时,价格以粗体斜体字体显示 (见图 5) 。
图 4:价格低于 $75.00 以普通字体显示 (单击以查看全尺寸图像)
图 5:昂贵产品的价格以粗体斜体字体显示 (单击以查看全尺寸图像)
使用 FormView 控件的DataBound
事件处理程序
确定绑定到 FormView 的基础数据的步骤与 DetailsView 创建 DataBound
事件处理程序的步骤相同,将 DataItem
属性强制转换为绑定到控件的相应对象类型,并确定如何继续。 但是,FormView 和 DetailsView 在用户界面外观的更新方式上有所不同。
FormView 不包含任何 BoundFields,因此缺少 Rows
集合。 相反,FormView 由模板组成,模板可以包含静态 HTML、Web 控件和数据绑定语法的组合。 调整 FormView 的样式通常涉及调整 FormView 模板中一个或多个 Web 控件的样式。
为了说明这一点,我们使用 FormView 列出产品,如上一示例所示,但这次我们仅显示产品名称和库存单位,如果小于或等于 10,则以红色字体显示库存单位。
步骤 4:在 FormView 中显示产品信息
将 FormView 添加到 CustomColors.aspx
DetailsView 下的页面,并将其 ID
属性设置为 LowStockedProductsInRed
。 将 FormView 绑定到从上一步创建的 ObjectDataSource 控件。 这将为 FormView 创建 ItemTemplate
、 EditItemTemplate
和 InsertItemTemplate
。 EditItemTemplate
删除 和 InsertItemTemplate
并简化 ItemTemplate
以仅ProductName
包含 和 UnitsInStock
值,每个值都在其自己的适当命名的 Label 控件中。 与前面示例中的 DetailsView 一样,还检查 FormView 的智能标记中的“启用分页”复选框。
完成这些编辑后,FormView 的标记应如下所示:
<asp:FormView ID="LowStockedProductsInRed" DataKeyNames="ProductID"
DataSourceID="ObjectDataSource1" AllowPaging="True"
EnableViewState="False" runat="server">
<ItemTemplate>
<b>Product:</b>
<asp:Label ID="ProductNameLabel" runat="server"
Text='<%# Bind("ProductName") %>'>
</asp:Label><br />
<b>Units In Stock:</b>
<asp:Label ID="UnitsInStockLabel" runat="server"
Text='<%# Bind("UnitsInStock") %>'>
</asp:Label>
</ItemTemplate>
</asp:FormView>
请注意, ItemTemplate
包含:
- 静态 HTML 文本“Product:”和“单位库存:”以及
<br />
和<b>
元素。 - Web 控制 两个 Label 控件,
ProductNameLabel
和UnitsInStockLabel
。 - 数据绑定语法 和
<%# Bind("ProductName") %>
<%# Bind("UnitsInStock") %>
语法,用于将这些字段中的值分配给 Label 控件的属性Text
。
步骤 5:以编程方式确定 DataBound 事件处理程序中的数据值
完成 FormView 标记后,下一步是以编程方式确定该值是 UnitsInStock
小于还是等于 10。 这与使用 DetailsView 的方式完全相同。 首先,为 FormView 的事件 DataBound
创建事件处理程序。
图 6:创建 DataBound
事件处理程序
在 事件处理程序中,将 FormView 的 DataItem
属性强制转换为 ProductsRow
实例, UnitsInPrice
并确定该值是否需要以红色字体显示。
protected void LowStockedProductsInRed_DataBound(object sender, EventArgs e)
{
// Get the ProductsRow object from the DataItem property...
Northwind.ProductsRow product = (Northwind.ProductsRow)
((DataRowView)LowStockedProductsInRed.DataItem).Row;
if (!product.IsUnitsInStockNull() && product.UnitsInStock <= 10)
{
// TODO: Make the UnitsInStockLabel text red
}
}
步骤 6:在 FormView 的 ItemTemplate 中设置 UnitsInStockLabel 标签控件的格式
最后一步是,如果值为 10 或更少,则以红色字体设置显示 UnitsInStock
值的格式。 为此,我们需要以编程方式访问 UnitsInStockLabel
中的 ItemTemplate
控件,并设置其样式属性,使其文本以红色显示。 若要访问模板中的 Web 控件,请使用 FindControl("controlID")
如下所示的方法:
WebControlType someName = (WebControlType)FormViewID.FindControl("controlID");
在我们的示例中,我们希望访问值为 UnitsInStockLabel
的 ID
Label 控件,因此我们将使用:
Label unitsInStock =
(Label)LowStockedProductsInRed.FindControl("UnitsInStockLabel");
获得对 Web 控件的编程引用后,可以根据需要修改其样式相关的属性。 与前面的示例一样,我在 中创建了一个名为 LowUnitsInStockEmphasis
的 Styles.css
CSS 类。 若要将此样式应用于 Label Web 控件,请相应地设置其 CssClass
属性。
protected void LowStockedProductsInRed_DataBound(object sender, EventArgs e)
{
// Get the ProductsRow object from the DataItem property...
Northwind.ProductsRow product = (Northwind.ProductsRow)
((DataRowView)LowStockedProductsInRed.DataItem).Row;
if (!product.IsUnitsInStockNull() && product.UnitsInStock <= 10)
{
Label unitsInStock =
(Label)LowStockedProductsInRed.FindControl("UnitsInStockLabel");
if (unitsInStock != null)
{
unitsInStock.CssClass = "LowUnitsInStockEmphasis";
}
}
}
注意
在 DetailsView 或 GridView 控件 FindControl("controlID")
中使用 TemplateFields 时,也可以使用 以编程方式设置模板格式设置其样式相关属性的语法。 我们将在下一教程中检查 TemplateFields。
图 7 显示了查看值大于 10 的产品 UnitsInStock
时的 FormView,而图 8 中的产品的值小于 10。
图 7:对于库存数量足够大的产品,不应用自定义格式 (单击以查看全尺寸图像)
图 8:对于值为 10 或以下的产品,库存编号中的单位显示为红色 (单击以查看全尺寸图像)
使用 GridView 的事件RowDataBound
设置格式
之前,我们检查了在数据绑定期间 DetailsView 和 FormView 控件进度的步骤序列。 让我们在复习时再次查看这些步骤。
- 触发数据 Web 控件
DataBinding
的事件。 - 数据绑定到数据 Web 控件。
- 触发数据 Web 控件
DataBound
的事件。
对于 DetailsView 和 FormView 来说,这三个简单的步骤就足够了,因为它们只显示一条记录。 对于 GridView,它显示绑定到它 的所有 记录 (而不仅仅是第一个) ,步骤 2 涉及更多。
在步骤 2 中,GridView 枚举数据源,并为每个记录创建一个 GridViewRow
实例,并将当前记录绑定到该实例。 GridViewRow
对于添加到 GridView 的每个事件,将引发两个事件:
RowCreated
创建 后GridViewRow
触发RowDataBound
在当前记录绑定到GridViewRow
后触发。
对于 GridView,可通过以下步骤序列更准确地描述数据绑定:
GridView 的事件
DataBinding
将触发。数据绑定到 GridView。
对于数据源中的每个记录
- 创建
GridViewRow
对象 - 触发
RowCreated
事件 - 将记录绑定到
GridViewRow
- 触发
RowDataBound
事件 GridViewRow
将 添加到Rows
集合
- 创建
GridView 的事件
DataBound
将触发。
若要自定义 GridView 的各个记录的格式,需要为 RowDataBound
事件创建事件处理程序。 为了说明这一点,让我们向 CustomColors.aspx
页面添加一个 GridView,其中列出了每个产品的名称、类别和价格,突出显示那些价格低于 $10.00 且背景色为黄色的产品。
步骤 7:在 GridView 中显示产品信息
在上一示例中的 FormView 下方添加 GridView,并将其 ID
属性设置为 HighlightCheapProducts
。 由于我们已经有了一个可返回页面上所有产品的 ObjectDataSource,因此请将 GridView 绑定到该对象。 最后,编辑 GridView 的 BoundFields,以仅包含产品名称、类别和价格。 完成这些编辑后,GridView 的标记应如下所示:
<asp:GridView ID="HighlightCheapProducts" AutoGenerateColumns="False"
DataKeyNames="ProductID" DataSourceID="ObjectDataSource1"
EnableViewState="False" runat="server">
<Columns>
<asp:BoundField DataField="ProductName" HeaderText="Product"
SortExpression="ProductName" />
<asp:BoundField DataField="CategoryName" HeaderText="Category"
ReadOnly="True" SortExpression="CategoryName" />
<asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}"
HeaderText="Price"
HtmlEncode="False" SortExpression="UnitPrice" />
</Columns>
</asp:GridView>
图 9 显示了通过浏览器查看时的进度。
图 9:GridView Lists每个产品的名称、类别和价格 (单击以查看全尺寸图像)
步骤 8:以编程方式确定 RowDataBound 事件处理程序中的数据值
ProductsDataTable
当 绑定到 GridView 时,会枚举其ProductsRow
实例,并为每个 ProductsRow
GridViewRow
创建 。 GridViewRow
的 DataItem
属性分配给特定的 ProductRow
,之后将引发 GridView 的RowDataBound
事件处理程序。 若要确定 UnitPrice
绑定到 GridView 的每个产品的值,我们需要为 GridView 的事件 RowDataBound
创建事件处理程序。 在此事件处理程序中, UnitPrice
我们可以检查当前 GridViewRow
的值,并为该行做出格式设置决策。
可以使用与 FormView 和 DetailsView 相同的一系列步骤创建此事件处理程序。
图 10:为 GridView 的事件 RowDataBound
创建事件处理程序
以这种方式创建事件处理程序将导致以下代码自动添加到 ASP.NET 页的代码部分:
protected void HighlightCheapProducts_RowDataBound(object sender, GridViewRowEventArgs e)
{
}
RowDataBound
当事件触发时,事件处理程序将作为其第二个参数传递给 类型GridViewRowEventArgs
的对象,该对象具有名为 的属性Row
。 此属性返回对 GridViewRow
仅绑定数据的 的引用。 若要访问绑定到 的ProductsRow
实例,GridViewRow
DataItem
请使用 如下所示的 属性:
protected void HighlightCheapProducts_RowDataBound(object sender, GridViewRowEventArgs e)
{
// Get the ProductsRow object from the DataItem property...
Northwind.ProductsRow product = (Northwind.ProductsRow)
((System.Data.DataRowView)e.Row.DataItem).Row;
if (!product.IsUnitPriceNull() && product.UnitPrice < 10m)
{
// TODO: Highlight the row yellow...
}
}
使用 RowDataBound
事件处理程序时,请务必记住 GridView 由不同类型的行组成,并且 会为所有行 类型触发此事件。 GridViewRow
的类型可以由其RowType
属性决定,并且可以具有一个可能的值:
DataRow
绑定到 GridView 中的记录的行DataSource
EmptyDataRow
如果 GridView 为DataSource
空,则显示的行Footer
页脚行;如果 GridView 的ShowFooter
属性设置为 ,则显示true
Header
标题行;如果 GridView 的 ShowHeader 属性设置为true
(默认)Pager
对于实现分页的 GridView,则为显示分页接口的行Separator
不用于 GridView,但由RowType
DataList 和 Repeater 控件的属性使用,我们将在未来教程中讨论两个数据 Web 控件
EmptyDataRow
由于 、Header
、 Footer
和 Pager
行不与DataSource
记录关联,因此它们始终具有null
其DataItem
属性的值。 出于此原因,在尝试使用当前 GridViewRow
的 DataItem
属性之前,我们首先必须确保处理 DataRow
的是 。 这可以通过检查 的 RowType
属性来实现,GridViewRow
如下所示:
protected void HighlightCheapProducts_RowDataBound(object sender, GridViewRowEventArgs e)
{
// Make sure we are working with a DataRow
if (e.Row.RowType == DataControlRowType.DataRow)
{
// Get the ProductsRow object from the DataItem property...
Northwind.ProductsRow product = (Northwind.ProductsRow)
((System.Data.DataRowView)e.Row.DataItem).Row;
if (!product.IsUnitPriceNull() && product.UnitPrice < 10m)
{
// TODO: Highlight row yellow...
}
}
}
步骤 9:当 UnitPrice 值小于 $10.00 时突出显示黄色行
最后一步是,如果该UnitPrice
行的值小于 $10.00,则以编程方式突出显示整个GridViewRow
。 用于访问 GridView 的行或单元格的语法与用于访问整个行GridViewID.Rows[index].Cells[index]
(用于访问特定单元格)的 DetailsView GridViewID.Rows[index]
相同。 但是,当事件处理程序触发时 RowDataBound
,绑定 GridViewRow
的数据尚未添加到 GridView 的 Rows
集合中。 因此,无法使用 Rows 集合从RowDataBound
事件处理程序访问当前GridViewRow
实例。
我们可以使用 GridViewID.Rows[index]
在事件处理程序中RowDataBound
引用当前GridViewRow
实例,而不是 。e.Row
也就是说,为了突出显示事件处理程序中的当前 GridViewRow
实例, RowDataBound
我们将使用:
e.Row.BackColor = System.Drawing.Color.Yellow;
与其直接设置 GridViewRow
的 BackColor
属性,不如继续使用 CSS 类。 我创建了一个名为 AffordablePriceEmphasis
的 CSS 类,该类将背景色设置为黄色。 完成 RowDataBound
的事件处理程序如下所示:
protected void HighlightCheapProducts_RowDataBound(object sender, GridViewRowEventArgs e)
{
// Make sure we are working with a DataRow
if (e.Row.RowType == DataControlRowType.DataRow)
{
// Get the ProductsRow object from the DataItem property...
Northwind.ProductsRow product = (Northwind.ProductsRow)
((System.Data.DataRowView)e.Row.DataItem).Row;
if (!product.IsUnitPriceNull() && product.UnitPrice < 10m)
{
e.Row.CssClass = "AffordablePriceEmphasis";
}
}
}
图 11:最实惠的产品突出显示黄色 (单击以查看全尺寸图像)
总结
本教程介绍了如何基于绑定到控件的数据设置 GridView、DetailsView 和 FormView 的格式。 为此,我们为 DataBound
或 RowDataBound
事件创建了一个事件处理程序,如果需要,会检查基础数据以及格式更改。 为了访问绑定到 DetailsView 或 FormView 的数据,我们在事件处理程序中使用 DataItem
DataBound
属性;对于 GridView,每个 GridViewRow
实例的属性 DataItem
都包含绑定到该行的数据,该数据在事件处理程序中 RowDataBound
可用。
以编程方式调整数据 Web 控件格式的语法取决于 Web 控件以及要设置格式的数据的显示方式。 对于 DetailsView 和 GridView 控件,可以通过序号索引访问行和单元格。 对于使用模板的 FormView, FindControl("controlID")
方法通常用于从模板中查找 Web 控件。
在下一教程中,我们将介绍如何将模板与 GridView 和 DetailsView 配合使用。 此外,我们还将看到另一种基于基础数据自定义格式设置的技术。
编程快乐!
关于作者
斯科特·米切尔是七本 ASP/ASP.NET 书籍的作者和 4GuysFromRolla.com 的创始人,自 1998 年以来一直在使用 Microsoft Web 技术。 Scott 担任独立顾问、培训师和作家。 他的最新一本书是 山姆斯在 24 小时内 ASP.NET 2.0。 可以在 上mitchell@4GuysFromRolla.com联系他,也可以通过他的博客(可在 中找到http://ScottOnWriting.NET)。
特别感谢
本教程系列由许多有用的审阅者审阅。 本教程的主要审阅者是 E.R. Gilmore、Dennis Patterson 和 Dan Jagers。 有兴趣查看我即将发布的 MSDN 文章? 如果是,请在 处mitchell@4GuysFromRolla.com放置一行。