znlgis 博客

GIS开发与技术分享

第七章:数据窗体开发

7.1 数据窗体概述

7.1.1 什么是数据窗体

数据窗体开发是SOD框架的三大核心功能之一(D = Data Controls),它提供了一套统一的数据控件接口,实现了:

  1. 表单数据绑定:将实体类数据自动填充到窗体控件
  2. 表单数据收集:将窗体控件的值自动收集到实体类
  3. 数据验证:内置基础的数据验证功能
  4. 统一接口: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)模式支持,实现了:

  1. 数据绑定:属性变化自动更新UI
  2. 命令绑定:按钮点击绑定到命令
  3. 视图模型:业务逻辑与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框架的数据窗体开发技术:

  1. 数据控件接口:IDataControl接口及其实现
  2. WebForm数据控件:ASP.NET下的数据控件使用
  3. WinForm数据控件:Windows窗体下的数据控件使用
  4. DataFormHelper:数据填充、收集、清空的核心类
  5. MVVM模式:WinForm下的MVVM支持
  6. 数据验证:表单验证的实现方式
  7. 实战案例:用户管理模块的完整实现

数据窗体开发技术让表单操作变得简单高效,是SOD框架的重要特性之一。


下一章预告:第八章将介绍SOD框架的企业级解决方案,包括分布式事务、数据同步、内存数据库等高级特性。