C# WPF 数据绑定实战:将 DataSet 数据转化为界面信息

小岛来信
小岛来信
发布时间:2026-10-14| 4人浏览过
原创

本文旨在演示如何在 C# WPF 应用程序中实现数据绑定,将底层数据集(DataSet)转化为直观的用户界面信息。

结果或目标

我们需要构建一个包含两个主要部分的应用程序界面:上方显示“客户”和“订单”的具体录入信息,下方分别以列表形式展示所有已添加的客户数据和订单数据。界面上的文本框应与输入字段双向绑定,而下方的列表视图(ListView)应能自动反映后台数据库的变化。核心目标是通过声明式的 XAML 语法将逻辑与界面分离,并验证 DataSet 中的父子表关系及计算列能否正确传递给 UI。

准备条件

本示例基于 .NET Framework 环境下的 Windows Presentation Foundation (WPF) 技术。需要创建一个包含主窗口(MainWindow)的 WPF 项目。UI 布局将通过 Grid、GroupBox 和 TextBox 等标准控件实现。数据层将使用 ADO.NET 的 DataSet 对象来模拟数据仓库,其中包含具有主外键关系的表以及用于汇总计算的派生列。

真实操作顺序

第1步:设计主界面的 XAML 结构

首先定义窗口的整体布局。我们使用一个主 Grid 容器划分四个行:Row 0 为客户信息组,Row 1 为订单信息组,Row 2 为客户列表展示区,Row 3 为订单列表展示区。为了更清晰地理解最终生成的界面效果,请参考以下运行后的界面截图:

第2步:配置客户信息组的控件与绑定

在第一个 GroupBox 内部,创建网格以便对齐标签与输入框。为了实现数据绑定,关键在于设置 Text 属性的 Binding 标记扩展。例如,用于输入客户编号的 TextBox,其 Text 属性绑定到数据源的 CID 路径;同理,客户名称绑定到 Name 路径。这种绑定模式不仅实现了数据显示,还允许用户在控件内修改数据后反向更新数据源。

XAML 代码片段:

<GroupBox Header="Customer" Grid.Row="0" Padding="5">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <!-- 标签 -->
        <TextBlock Grid.Row="0" Grid.Column="0" Text="CustomerID" Margin="2" HorizontalAlignment="Right" VerticalAlignment="Center"/>
        <!-- 绑定至 CID 属性 -->
        <TextBox Grid.Row="0" Grid.Column="1" Margin="2" Name="tbxCustomerID" Text="{Binding Path=CID}"/>
        <TextBlock Grid.Row="1" Grid.Column="0" Text="CustomerName" Margin="2" HorizontalAlignment="Right" VerticalAlignment="Center"/>
        <!-- 绑定至 Name 属性 -->
        <TextBox Grid.Row="1" Grid.Column="1" Margin="2" Name="tbxCustomerName" Text="{Binding Path=Name}"/>
        <!-- 添加按钮,触发事件 -->
        <Button Grid.Row="2" Grid.Column="1" Content="Add New Customer" Margin="2" Name="btnAddCustomer" Padding="2" Click="btnAddCustomer_Click" />
    </Grid>
</GroupBox>

第3步:配置订单信息组的控件与绑定

订单信息组的布局逻辑与客户组类似。我们在 Grid.Row="1" 的区域定义订单的明细字段。重点在于建立多个输入项与后端字段的对应关系:OrderID 绑定 OID,OrderName(此处指代客户关联名或订单名)绑定 Customer 字段,Subtotal 绑定 Subtotal,TaxRate 绑定 TaxRate。同样需要一个按钮触发后续的数据处理逻辑。

XAML 代码片段:

<GroupBox Header="Order" Grid.Row="1" Padding="5">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <TextBlock Grid.Row="0" Grid.Column="0" Text="OrderID" Margin="2" HorizontalAlignment="Right" VerticalAlignment="Center"/>
        <TextBox Grid.Row="0" Grid.Column="1" Margin="2" Name="tbxOrderID" Text="{Binding Path=OID}"/>
        <TextBlock Grid.Row="1" Grid.Column="0" Text="OrderName" Margin="2" HorizontalAlignment="Right" VerticalAlignment="Center"/>
        <TextBox Grid.Row="1" Grid.Column="1" Margin="2" Name="tbxOrderName" Text="{Binding Path=Customer}"/>
        <TextBlock Grid.Row="2" Grid.Column="0" Text="Subtotal" Margin="2" HorizontalAlignment="Right" VerticalAlignment="Center"/>
        <TextBox Grid.Row="2" Grid.Column="1" Margin="2" Name="tbxSubtotal" Text="{Binding Path=Subtotal}"/>
        <TextBlock Grid.Row="3" Grid.Column="0" Text="TaxRate" Margin="2" HorizontalAlignment="Right" VerticalAlignment="Center"/>
        <TextBox Grid.Row="3" Grid.Column="1" Margin="2" Name="tbxTaxRate" Text="{Binding Path=TaxRate}"/>
        <Button Grid.Row="4" Grid.Column="1" Content="Add New Order" Margin="2" Name="btnAddOrder" Padding="2" Click="btnAddOrder_Click" />
    </Grid>
</GroupBox>

第4步:配置底部列表视图的数据呈现

为了将分散的数据整合展示,我们使用两个 ListView 控件。它们位于底部的两行中。关键点在于 ItemsSource 属性设置为 {Binding},这意味着列表将从其父级 DataContext 获取集合数据。
此外,为了美化表格显示,我们为每个 ListView 定义了 GridView 视图,并为每一列指定 DisplayMemberBinding 以显示对应的数据字段(如客户的总金额、订单的含税总额等)。

XAML 代码片段:

<ListView Name="lstDisplayCustomer" ItemsSource="{Binding}" Grid.Row="2" Margin="2" MinHeight="150">
    <ListView.View>
        <GridView>
            <GridView.Columns>
                <GridViewColumn Header="CustomerID" DisplayMemberBinding="{Binding CID}"/>
                <GridViewColumn Header="CustomerName" DisplayMemberBinding="{Binding Name}"/>
                <!-- 显示子表的聚合数据 -->
                <GridViewColumn Header="Total" DisplayMemberBinding="{Binding OrderTotals}" />
            </GridView.Columns>
        </GridView>
    </ListView.View>
</ListView>

<ListView Name="lstDisplayOrder" ItemsSource="{Binding}" Grid.Row="3" Margin="2" MinHeight="150">
    <ListView.View>
        <GridView>
            <GridView.Columns>
                <GridViewColumn Header="OrderID" DisplayMemberBinding="{Binding OID}"/>
                <GridViewColumn Header="Customer" DisplayMemberBinding="{Binding Customer}"/>
                <GridViewColumn Header="Subtotal" DisplayMemberBinding="{Binding Subtotal}"/>
                <GridViewColumn Header="TaxRate" DisplayMemberBinding="{Binding TaxRate}" />
                <GridViewColumn Header="Total" DisplayMemberBinding="{Binding Total}" />
            </GridView.Columns>
        </GridView>
    </ListView.View>
</ListView>

第5步:编写 C# 后端逻辑建立数据模型

在 Code-Behind 文件中,我们需要构建并初始化数据上下文。这里采用一个静态方法 NewData 来生成示例用的 DataSet。

  1. 构建父表(客户表): 实例化 DataTable,添加 ID 和姓名列。
  2. 构建子表(订单表): 实例化另一个 DataTable,包含订单 ID、客户 ID(外键)、金额和税率。
  3. 建立关系与计算:
    • 将两张表加入同一个 DataSet。
    • 通过 Relations.Add 建立 CID(客户ID)到 Customer(订单客户ID)的主外键关系。
    • 在客户表中添加一个计算列 OrderTotals,使用表达式 Sum(Child.Total)。这将使得每个客户数据自带其名下所有订单总额的统计结果。
    • 在订单表中添加一列 Total,使用表达式 Subtotal*(1+TaxRate) 进行实时计算。

C# 代码逻辑:

// 全局变量或类成员
DataSet business = new DataSet(); // 实际应在构造函数或Init中初始化

private static DataSet NewData()
{
    // 1. build the parent table and add some data
    DataTable customer = new DataTable("Customer");
    customer.Columns.Add("CID", typeof(Int32));
    customer.Columns.Add("Name", typeof(string));

    // 2. build the child table and add some data.
    DataTable orders = new DataTable("Order");
    orders.Columns.Add("OID", typeof(int));
    orders.Columns.Add("Customer", typeof(Int32));
    orders.Columns.Add("Subtotal", typeof(decimal));
    orders.Columns.Add("TaxRate", typeof(decimal));
    // 计算列:小计 * (1 + 税率)
    orders.Columns.Add("Total",typeof(decimal),"Subtotal*(1+TaxRate)");

    // 3. Link the table within a Dataset.
    business = new DataSet();
    business.Tables.Add(customer);
    business.Tables.Add(orders);
    // 建立主外键关系,便于跨表查询和聚合
    business.Relations.Add(customer.Columns["CID"], orders.Columns["Customer"]);
    
    // 4. 在父表中添加聚合列,引用子表数据的总和
    customer.Columns.Add("OrderTotals", typeof(decimal), "Sum(Child.Total)");
    
    return business;
}

第6步:响应按钮点击并刷新数据上下文

当用户点击“Add New Customer”时,程序需要读取文本框的值,将其转换为新行数据插入到 customer 表中,然后重新绑定 DataContext 以确保下方的 ListView 能够刷新看到新记录。对于订单,逻辑完全一致,只是操作的是 orders 表。

C# 交互逻辑:

private void btnAddCustomer_Click(object sender, RoutedEventArgs e)
{
    // 访问客户表数据源
    DataTable customer = business.Tables["Customer"];
    AddCustomerToTable(customer); // 提取出的通用方法
    
    // 关键步骤:将当前数据表赋给 ListView 的 DataContext
    lstDisplayCustomer.DataContext = customer;
}

// 通用辅助方法:从UI取值并追加到表
private DataTable AddCustomerToTable(DataTable table)
{
    DataRow newRow = table.NewRow();
    newRow["CID"] = tbxCustomerID.Text;
    newRow["Name"] = tbxCustomerName.Text;
    table.Rows.Add(newRow);
    return table;
}

// 同理处理订单
private DataTable AddOrderToTable(DataTable table)
{
    DataRow newRow = table.NewRow();
    newRow["OID"] = tbxOrderID.Text;
    newRow["Customer"] = tbxOrderName.Text; // 注意:原代码此处对应tbxOrderName,需确保数据类型匹配
    newRow["Subtotal"] = tbxSubtotal.Text;
    newRow["TaxRate"] = tbxTaxRate.Text;
    table.Rows.Add(newRow);
    return table;
}

private void btnAddOrder_Click(object sender, RoutedEventArgs e)
{
    // 访问订单表数据源
    DataTable order = business.Tables["Order"];
    AddOrderToTable(order);
    
    // 关键步骤:刷新订单列表视图
    lstDisplayOrder.DataContext = order;
}

完成判断

程序启动后,若界面上方出现清晰的输入框和按钮,下方则显示带有“CustomerID”、“CustomerName”、“Total”等列头的空白表格。尝试在上方输入框填入数值(如 CID: 1, Name: Alice, Subtotal: 100),点击按钮后,若下方列表区域成功渲染出该条记录,且系统按照设定规则自动计算了相应的 Total 值和子表聚合值,则说明数据绑定流程已正确打通,原始数据已成功转化为可视化的界面信息。

简要总结

本示例展示了 WPF 的强大之处——通过声明式的绑定机制简化了繁琐的 UI 刷新逻辑。利用 DataSet 内置的关系引擎和表达式功能,我们可以轻松地在数据结构层处理复杂的层级与计算,而无需手动维护多张表格间的同步。开发者只需关注 DataContext 的设置与 XAML 属性映射即可实现高效的数据可视化。以上就是 C# WPF 数据绑定之数据转化为信息的详细内容,更多关于 C# WPF 编程的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。