07-数据窗体开发
第七章:数据窗体开发
7.1 数据窗体概述
7.1.1 什么是数据窗体
数据窗体开发是SOD框架的三大核心功能之一(D = Data Controls),它提供了一套统一的数据控件接口,实现了:
- 表单数据绑定:将实体类数据自动填充到窗体控件
- 表单数据收集:将窗体控件的值自动收集到实体类
- 数据验证:内置基础的数据验证功能
- 统一接口:WebForm和WinForm使用相同的开发模式
7.1.2 支持的平台
| 平台 | 程序集 | 说明 |
|---|---|---|
| WebForm | PWMIS.Web | ASP.NET Web窗体 |
| WinForm | PWMIS.Windows | Windows窗体 |
| WPF | PWMIS.Windows (部分) | WPF窗体 (MVVM模式) |
7.1.3 核心接口
/// <summary>
/// 数据控件接口
/// </summary>
public interface IDataControl
{
/// <summary>
/// 控件关联的数据源字段名称
/// </summary>
string LinkProperty { get; set; }
/// <summary>
/// 控件关联的数据对象名称
/// </summary>
string LinkObject { get; set; }
/// <summary>
/// 控件的值(经过类型转换)
/// </summary>
object ControlValue { get; set; }
/// <summary>
/// 控件的文本值
/// </summary>
string ControlTextValue { get; set; }
/// <summary>
/// 是否只读
/// </summary>
bool Readonly { get; set; }
}
7.2 WebForm数据控件
7.2.1 引用程序集
// 引用PWMIS.Web程序集
using PWMIS.Web.Controls;
7.2.2 注册控件(Web.config)
<configuration>
<system.web>
<pages>
<controls>
<add tagPrefix="sod" namespace="PWMIS.Web.Controls" assembly="PWMIS.Web"/>
</controls>
</pages>
</system.web>
</configuration>
7.2.3 数据控件列表
| 控件 | 对应标准控件 | 说明 |
|---|---|---|
| DataTextBox | TextBox | 文本框 |
| DataLabel | Label | 标签 |
| DataCheckBox | CheckBox | 复选框 |
| DataRadioButton | RadioButton | 单选按钮 |
| DataDropDownList | DropDownList | 下拉列表 |
| DataListBox | ListBox | 列表框 |
| DataHiddenField | HiddenField | 隐藏字段 |
7.2.4 控件使用示例
ASPX页面:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="UserEdit.aspx.cs" Inherits="MyApp.UserEdit" %>
<%@ Register TagPrefix="sod" Namespace="PWMIS.Web.Controls" Assembly="PWMIS.Web" %>
<!DOCTYPE html>
<html>
<head>
<title>用户编辑</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<h2>用户信息编辑</h2>
<!-- 隐藏ID -->
<sod:DataHiddenField ID="hfID" runat="server" LinkProperty="ID" />
<p>
<label>用户名:</label>
<sod:DataTextBox ID="txtName" runat="server" LinkProperty="Name" />
</p>
<p>
<label>邮箱:</label>
<sod:DataTextBox ID="txtEmail" runat="server" LinkProperty="Email" />
</p>
<p>
<label>状态:</label>
<sod:DataDropDownList ID="ddlStatus" runat="server" LinkProperty="Status">
<asp:ListItem Value="1" Text="启用" />
<asp:ListItem Value="0" Text="禁用" />
</sod:DataDropDownList>
</p>
<p>
<label>VIP用户:</label>
<sod:DataCheckBox ID="chkVip" runat="server" LinkProperty="IsVip" />
</p>
<p>
<label>创建时间:</label>
<sod:DataLabel ID="lblCreateTime" runat="server" LinkProperty="CreateTime" />
</p>
<p>
<asp:Button ID="btnSave" runat="server" Text="保存" OnClick="btnSave_Click" />
</p>
</div>
</form>
</body>
</html>
后台代码:
using System;
using PWMIS.DataMap.Entity;
using PWMIS.Web.Controls;
using MyApp.Entities;
namespace MyApp
{
public partial class UserEdit : System.Web.UI.Page
{
private UserEntity _user;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
int userId = Convert.ToInt32(Request["id"] ?? "0");
if (userId > 0)
{
LoadUser(userId);
}
}
}
private void LoadUser(int id)
{
// 查询用户数据
_user = new UserEntity { ID = id };
var oql = OQL.From(_user).Select().Where(_user.ID).END;
_user = EntityQuery<UserEntity>.QueryObject(oql);
if (_user != null)
{
// 数据填充到控件
DataFormHelper.FillData(_user, this);
}
}
protected void btnSave_Click(object sender, EventArgs e)
{
// 从控件收集数据
_user = new UserEntity();
DataFormHelper.CollectData(_user, this);
// 保存数据
if (_user.ID > 0)
{
EntityQuery<UserEntity>.Instance.Update(_user);
}
else
{
_user.CreateTime = DateTime.Now;
EntityQuery<UserEntity>.Instance.Insert(_user);
}
Response.Write("<script>alert('保存成功!');</script>");
}
}
}
7.3 WinForm数据控件
7.3.1 引用程序集
// 引用PWMIS.Windows程序集
using PWMIS.Windows.Controls;
using PWMIS.Windows.Mvvm;
7.3.2 数据控件列表
| 控件 | 对应标准控件 | 说明 |
|---|---|---|
| DataTextBox | TextBox | 文本框 |
| DataLabel | Label | 标签 |
| DataCheckBox | CheckBox | 复选框 |
| DataRadioButton | RadioButton | 单选按钮 |
| DataComboBox | ComboBox | 下拉框 |
| DataListBox | ListBox | 列表框 |
| DataDateTimePicker | DateTimePicker | 日期选择器 |
| DataNumericUpDown | NumericUpDown | 数值输入框 |
7.3.3 控件使用示例
窗体设计(可视化设计器或代码):
// 窗体初始化代码
private void InitializeDataControls()
{
// 创建数据控件
this.txtName = new DataTextBox();
this.txtName.LinkProperty = "Name";
this.txtName.Location = new Point(100, 50);
this.Controls.Add(this.txtName);
this.txtEmail = new DataTextBox();
this.txtEmail.LinkProperty = "Email";
this.txtEmail.Location = new Point(100, 90);
this.Controls.Add(this.txtEmail);
this.cboStatus = new DataComboBox();
this.cboStatus.LinkProperty = "Status";
this.cboStatus.Location = new Point(100, 130);
this.cboStatus.Items.Add(new ComboItem { Value = 1, Text = "启用" });
this.cboStatus.Items.Add(new ComboItem { Value = 0, Text = "禁用" });
this.Controls.Add(this.cboStatus);
}
使用示例:
using System;
using System.Windows.Forms;
using PWMIS.DataMap.Entity;
using PWMIS.Windows.Controls;
using MyApp.Entities;
namespace MyApp
{
public partial class UserEditForm : Form
{
private UserEntity _user;
public UserEditForm()
{
InitializeComponent();
}
// 加载用户数据
public void LoadUser(int userId)
{
_user = new UserEntity { ID = userId };
var oql = OQL.From(_user).Select().Where(_user.ID).END;
_user = EntityQuery<UserEntity>.QueryObject(oql);
if (_user != null)
{
// 填充数据到控件
DataFormHelper.FillData(_user, this);
}
}
// 新建用户
public void NewUser()
{
_user = new UserEntity();
_user.CreateTime = DateTime.Now;
_user.Status = 1;
// 填充默认值
DataFormHelper.FillData(_user, this);
}
// 保存按钮点击
private void btnSave_Click(object sender, EventArgs e)
{
// 收集数据
DataFormHelper.CollectData(_user, this);
// 验证
if (string.IsNullOrEmpty(_user.Name))
{
MessageBox.Show("用户名不能为空!");
return;
}
// 保存
if (_user.ID > 0)
{
EntityQuery<UserEntity>.Instance.Update(_user);
}
else
{
EntityQuery<UserEntity>.Instance.Insert(_user);
}
MessageBox.Show("保存成功!");
this.DialogResult = DialogResult.OK;
this.Close();
}
// 清空表单
private void btnClear_Click(object sender, EventArgs e)
{
DataFormHelper.ClearData(this);
}
}
}
7.4 DataFormHelper类
7.4.1 核心方法
/// <summary>
/// 数据窗体帮助类
/// </summary>
public class DataFormHelper
{
/// <summary>
/// 将实体数据填充到窗体控件
/// </summary>
/// <param name="entity">实体对象</param>
/// <param name="container">控件容器(Form/Page/Panel等)</param>
public static void FillData(EntityBase entity, Control container);
/// <summary>
/// 从窗体控件收集数据到实体
/// </summary>
/// <param name="entity">实体对象</param>
/// <param name="container">控件容器</param>
public static void CollectData(EntityBase entity, Control container);
/// <summary>
/// 清空窗体控件的值
/// </summary>
/// <param name="container">控件容器</param>
public static void ClearData(Control container);
/// <summary>
/// 设置控件的只读状态
/// </summary>
/// <param name="container">控件容器</param>
/// <param name="readOnly">是否只读</param>
public static void SetReadonly(Control container, bool readOnly);
}
7.4.2 高级用法
// 1. 只填充特定控件
DataFormHelper.FillData(_user, panelBasic); // 只填充panelBasic内的控件
// 2. 设置只读模式
DataFormHelper.SetReadonly(this, true); // 所有数据控件变为只读
// 3. 使用LinkObject区分多个实体
// ASPX中:
// <sod:DataTextBox ID="txtUserName" LinkObject="User" LinkProperty="Name" />
// <sod:DataTextBox ID="txtDeptName" LinkObject="Dept" LinkProperty="Name" />
// 后台代码:
DataFormHelper.FillData(user, this, "User"); // 只填充LinkObject="User"的控件
DataFormHelper.FillData(dept, this, "Dept"); // 只填充LinkObject="Dept"的控件
7.5 MVVM模式支持
7.5.1 WinForm MVVM概述
SOD框架为WinForm提供了MVVM(Model-View-ViewModel)模式支持,实现了:
- 数据绑定:属性变化自动更新UI
- 命令绑定:按钮点击绑定到命令
- 视图模型:业务逻辑与UI分离
7.5.2 定义ViewModel
using PWMIS.Windows.Mvvm;
using System.ComponentModel;
namespace MyApp.ViewModels
{
public class UserViewModel : ViewModelBase
{
private UserEntity _user;
public UserViewModel()
{
_user = new UserEntity();
SaveCommand = new RelayCommand(Save, CanSave);
CancelCommand = new RelayCommand(Cancel);
}
// 属性绑定
public string Name
{
get { return _user.Name; }
set
{
if (_user.Name != value)
{
_user.Name = value;
OnPropertyChanged(nameof(Name));
SaveCommand.RaiseCanExecuteChanged();
}
}
}
public string Email
{
get { return _user.Email; }
set
{
if (_user.Email != value)
{
_user.Email = value;
OnPropertyChanged(nameof(Email));
}
}
}
public int Status
{
get { return _user.Status; }
set
{
if (_user.Status != value)
{
_user.Status = value;
OnPropertyChanged(nameof(Status));
}
}
}
// 命令
public RelayCommand SaveCommand { get; }
public RelayCommand CancelCommand { get; }
// 保存逻辑
private void Save()
{
if (_user.ID > 0)
{
EntityQuery<UserEntity>.Instance.Update(_user);
}
else
{
_user.CreateTime = DateTime.Now;
EntityQuery<UserEntity>.Instance.Insert(_user);
}
}
private bool CanSave()
{
return !string.IsNullOrEmpty(Name);
}
private void Cancel()
{
// 取消逻辑
}
// 加载数据
public void LoadUser(int id)
{
var user = new UserEntity { ID = id };
var oql = OQL.From(user).Select().Where(user.ID).END;
_user = EntityQuery<UserEntity>.QueryObject(oql) ?? new UserEntity();
// 通知所有属性变化
OnPropertyChanged(string.Empty);
}
}
}
7.5.3 绑定View
using System;
using System.Windows.Forms;
using PWMIS.Windows.Mvvm;
using MyApp.ViewModels;
namespace MyApp
{
public partial class UserEditForm : MvvmForm<UserViewModel>
{
public UserEditForm()
{
InitializeComponent();
// 创建ViewModel
ViewModel = new UserViewModel();
// 绑定属性
BindProperty(txtName, "Text", nameof(ViewModel.Name));
BindProperty(txtEmail, "Text", nameof(ViewModel.Email));
BindProperty(cboStatus, "SelectedValue", nameof(ViewModel.Status));
// 绑定命令
BindCommand(btnSave, ViewModel.SaveCommand);
BindCommand(btnCancel, ViewModel.CancelCommand);
}
public void LoadUser(int userId)
{
ViewModel.LoadUser(userId);
}
}
}
7.5.4 RelayCommand类
/// <summary>
/// 命令实现
/// </summary>
public class RelayCommand : ICommand
{
private readonly Action _execute;
private readonly Func<bool> _canExecute;
public RelayCommand(Action execute, Func<bool> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute();
}
public void Execute(object parameter)
{
_execute();
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
7.6 数据验证
7.6.1 内置验证
// 在收集数据时进行验证
DataFormHelper.CollectData(_user, this);
// 手动验证
if (string.IsNullOrEmpty(_user.Name))
{
ShowError(txtName, "用户名不能为空");
return;
}
if (!IsValidEmail(_user.Email))
{
ShowError(txtEmail, "邮箱格式不正确");
return;
}
7.6.2 自定义验证规则
public class ValidationRule
{
public static bool Required(string value, out string message)
{
message = string.IsNullOrEmpty(value) ? "此字段必填" : null;
return message == null;
}
public static bool Email(string value, out string message)
{
if (string.IsNullOrEmpty(value))
{
message = null;
return true;
}
var regex = new Regex(@"^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$");
if (!regex.IsMatch(value))
{
message = "邮箱格式不正确";
return false;
}
message = null;
return true;
}
public static bool Range(int value, int min, int max, out string message)
{
if (value < min || value > max)
{
message = $"值必须在{min}到{max}之间";
return false;
}
message = null;
return true;
}
}
// 使用
if (!ValidationRule.Required(_user.Name, out string nameError))
{
ShowError(txtName, nameError);
return;
}
if (!ValidationRule.Email(_user.Email, out string emailError))
{
ShowError(txtEmail, emailError);
return;
}
7.7 实战案例:用户管理模块
7.7.1 用户列表窗体
public partial class UserListForm : Form
{
private List<UserEntity> _users;
public UserListForm()
{
InitializeComponent();
LoadData();
}
private void LoadData()
{
var user = new UserEntity();
var oql = OQL.From(user)
.Select()
.OrderBy(o => o.Desc(user.ID))
.END;
_users = EntityQuery<UserEntity>.QueryList(oql);
dataGridView1.DataSource = _users;
}
private void btnAdd_Click(object sender, EventArgs e)
{
using (var form = new UserEditForm())
{
form.NewUser();
if (form.ShowDialog() == DialogResult.OK)
{
LoadData();
}
}
}
private void btnEdit_Click(object sender, EventArgs e)
{
if (dataGridView1.CurrentRow == null) return;
int userId = (int)dataGridView1.CurrentRow.Cells["ID"].Value;
using (var form = new UserEditForm())
{
form.LoadUser(userId);
if (form.ShowDialog() == DialogResult.OK)
{
LoadData();
}
}
}
private void btnDelete_Click(object sender, EventArgs e)
{
if (dataGridView1.CurrentRow == null) return;
if (MessageBox.Show("确定删除?", "确认", MessageBoxButtons.YesNo) == DialogResult.Yes)
{
int userId = (int)dataGridView1.CurrentRow.Cells["ID"].Value;
var user = new UserEntity { ID = userId };
EntityQuery<UserEntity>.Instance.Delete(user);
LoadData();
}
}
}
7.7.2 用户编辑窗体
public partial class UserEditForm : Form
{
private UserEntity _user;
public UserEditForm()
{
InitializeComponent();
InitializeControls();
}
private void InitializeControls()
{
// 设置状态下拉框
cboStatus.Items.Clear();
cboStatus.Items.Add(new { Value = 1, Text = "启用" });
cboStatus.Items.Add(new { Value = 0, Text = "禁用" });
cboStatus.DisplayMember = "Text";
cboStatus.ValueMember = "Value";
}
public void NewUser()
{
_user = new UserEntity();
_user.Status = 1;
_user.CreateTime = DateTime.Now;
DataFormHelper.FillData(_user, this);
this.Text = "新增用户";
}
public void LoadUser(int id)
{
_user = new UserEntity { ID = id };
var oql = OQL.From(_user).Select().Where(_user.ID).END;
_user = EntityQuery<UserEntity>.QueryObject(oql);
if (_user != null)
{
DataFormHelper.FillData(_user, this);
this.Text = "编辑用户 - " + _user.Name;
}
}
private void btnSave_Click(object sender, EventArgs e)
{
// 收集数据
DataFormHelper.CollectData(_user, this);
// 验证
if (!Validate()) return;
// 保存
try
{
if (_user.ID > 0)
{
EntityQuery<UserEntity>.Instance.Update(_user);
}
else
{
EntityQuery<UserEntity>.Instance.Insert(_user);
}
MessageBox.Show("保存成功!");
this.DialogResult = DialogResult.OK;
this.Close();
}
catch (Exception ex)
{
MessageBox.Show("保存失败:" + ex.Message);
}
}
private bool Validate()
{
if (string.IsNullOrEmpty(_user.Name))
{
MessageBox.Show("用户名不能为空!");
txtName.Focus();
return false;
}
if (string.IsNullOrEmpty(_user.Email))
{
MessageBox.Show("邮箱不能为空!");
txtEmail.Focus();
return false;
}
return true;
}
private void btnCancel_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
this.Close();
}
}
7.8 本章小结
本章介绍了SOD框架的数据窗体开发技术:
- 数据控件接口:IDataControl接口及其实现
- WebForm数据控件:ASP.NET下的数据控件使用
- WinForm数据控件:Windows窗体下的数据控件使用
- DataFormHelper:数据填充、收集、清空的核心类
- MVVM模式:WinForm下的MVVM支持
- 数据验证:表单验证的实现方式
- 实战案例:用户管理模块的完整实现
数据窗体开发技术让表单操作变得简单高效,是SOD框架的重要特性之一。
下一章预告:第八章将介绍SOD框架的企业级解决方案,包括分布式事务、数据同步、内存数据库等高级特性。

浙公网安备 33010602011771号