You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WinForm中跟踪Candidate实体未保存更改,关闭前提示用户的优雅实现

WinForm未保存数据提示与数据恢复的优雅实现方案

核心思路

实现需求的关键是维护实体的原始数据快照,通过对比当前窗体数据与快照的差异判断是否存在未保存修改,再根据用户选择执行保存、放弃或取消操作。

具体实现步骤

1. 保存原始数据快照

在窗体进入编辑模式时,创建Candidate实体的深拷贝作为原始快照,确保原始数据与当前编辑数据完全独立,避免引用共享导致的意外修改。

// 窗体类内声明私有字段
private Candidate _originalCandidate; // 原始数据快照
private Candidate _currentCandidate; // 绑定到窗体的当前实体

// 初始化编辑模式(可在窗体加载或Edit按钮点击事件中调用)
private void StartEdit(Candidate targetCandidate)
{
    // 深拷贝生成原始快照,这里用Json序列化实现通用深拷贝
    _originalCandidate = DeepCopy(targetCandidate);
    _currentCandidate = targetCandidate;
    // 启用所有编辑字段
    ToggleFieldsEnabled(true);
    // 同步实体数据到窗体控件
    SyncCandidateToForm(_currentCandidate);
}

// 通用深拷贝方法
private T DeepCopy<T>(T obj)
{
    var jsonString = System.Text.Json.JsonSerializer.Serialize(obj);
    return System.Text.Json.JsonSerializer.Deserialize<T>(jsonString)!;
}

2. 检测未保存修改

实现对比逻辑,判断当前实体与原始快照是否存在属性差异,以此确定是否有未保存的修改。

方式1:手动对比属性(适合属性较少的场景)

private bool HasUnsavedChanges()
{
    return !Equals(_originalCandidate.Id, _currentCandidate.Id)
           || !Equals(_originalCandidate.FirstName, _currentCandidate.FirstName)
           || !Equals(_originalCandidate.MiddleName, _currentCandidate.MiddleName)
           || !Equals(_originalCandidate.LastName, _currentCandidate.LastName)
           || !Equals(_originalCandidate.DateOfBirth, _currentCandidate.DateOfBirth)
           || !Equals(_originalCandidate.Gender, _currentCandidate.Gender)
           || !Equals(_originalCandidate.Nationality, _currentCandidate.Nationality)
           || !Equals(_originalCandidate.Education, _currentCandidate.Education);
}

方式2:反射自动对比(适合属性较多的场景)

private bool HasUnsavedChanges()
{
    var properties = typeof(Candidate).GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
    foreach (var prop in properties)
    {
        var originalValue = prop.GetValue(_originalCandidate);
        var currentValue = prop.GetValue(_currentCandidate);
        if (!Equals(originalValue, currentValue))
        {
            return true;
        }
    }
    return false;
}

3. 窗体关闭时的提示与处理

重写窗体的FormClosing事件,在事件中检查未保存修改,根据用户选择执行对应操作。

private void CandidateForm_FormClosing(object sender, FormClosingEventArgs e)
{
    if (!HasUnsavedChanges()) return;

    var dialogResult = MessageBox.Show("存在未保存的修改,是否保存?", "提示", 
        MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning);
    
    switch (dialogResult)
    {
        case DialogResult.Yes:
            // 执行保存逻辑,例如调用数据层更新数据库
            SaveCandidateToDatabase(_currentCandidate);
            break;
        case DialogResult.No:
            // 放弃修改,直接关闭窗体
            break;
        case DialogResult.Cancel:
            // 取消关闭操作,留在当前窗体
            e.Cancel = true;
            break;
    }
}

4. 取消编辑时恢复原始数据

如果需要在点击“取消”按钮或用户选择取消关闭时恢复原始数据,实现数据恢复方法:

private void RestoreOriginalData()
{
    // 将原始快照的值覆盖当前实体
    _currentCandidate.Id = _originalCandidate.Id;
    _currentCandidate.FirstName = _originalCandidate.FirstName;
    _currentCandidate.MiddleName = _originalCandidate.MiddleName;
    _currentCandidate.LastName = _originalCandidate.LastName;
    _currentCandidate.DateOfBirth = _originalCandidate.DateOfBirth;
    _currentCandidate.Gender = _originalCandidate.Gender;
    _currentCandidate.Nationality = _originalCandidate.Nationality;
    _currentCandidate.Education = _originalCandidate.Education;
    
    // 同步恢复后的实体数据到窗体控件
    SyncCandidateToForm(_currentCandidate);
    // 禁用编辑字段
    ToggleFieldsEnabled(false);
}

// 辅助方法:将实体数据同步到窗体控件
private void SyncCandidateToForm(Candidate candidate)
{
    txtFirstName.Text = candidate.FirstName;
    txtMiddleName.Text = candidate.MiddleName ?? string.Empty;
    txtLastName.Text = candidate.LastName;
    dtpDateOfBirth.Value = candidate.DateOfBirth ?? DateTime.Today;
    cboGender.Text = candidate.Gender ?? string.Empty;
    txtNationality.Text = candidate.Nationality ?? string.Empty;
    txtEducation.Text = candidate.Education ?? string.Empty;
}

// 辅助方法:切换字段编辑状态
private void ToggleFieldsEnabled(bool isEnabled)
{
    txtFirstName.Enabled = isEnabled;
    txtMiddleName.Enabled = isEnabled;
    txtLastName.Enabled = isEnabled;
    dtpDateOfBirth.Enabled = isEnabled;
    cboGender.Enabled = isEnabled;
    txtNationality.Enabled = isEnabled;
    txtEducation.Enabled = isEnabled;
}

优化建议

  • 使用BindingSource进行数据绑定,让实体属性与窗体控件自动同步,减少手动编写同步代码的工作量。
  • 将快照、对比、恢复逻辑封装成通用的EditStateManager类,可在多个WinForm中复用,提升代码复用性。

内容的提问来源于stack exchange,提问作者Unis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 14:57:21