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

基于DRY原则重构老旧C# WinForms应用:解决控件事件问题

WinForms多窗体重复控件与逻辑优化方案

问题背景

维护老旧C# Windows Forms项目时,12个窗体包含相同控件(如手机号输入框),需执行重复的手机号验证逻辑。已抽离CheckPatinetMobileNumber验证方法,但仍需在每个窗体重复编写调用逻辑;尝试将控件移至父窗体类导致控件OnTextChange等事件失效。

核心问题原因

之前的操作中,删除子窗体设计器的控件定义并在父窗体声明控件实例,导致WinForms设计器无法自动生成事件绑定代码(如TextChanged事件),进而引发事件异常。

可行解决方案

方案一:抽象父窗体+子类实现控件属性

通过抽象父窗体封装通用逻辑,子窗体保留设计器控件并通过属性暴露给父类,既复用逻辑又保留事件绑定:

  1. 定义抽象父窗体,封装验证逻辑并声明抽象控件属性
public abstract class BasePatientForm : Form
{
    // 抽象属性,由子窗体返回自身设计器中的控件
    protected abstract Ema.CustomeTextBox.EmaTextBox PatientMobileTextBox { get; }
    protected abstract Ema.CustomeTextBox.EmaTextBox BaseInsuranceIdTextBox { get; }

    // 统一封装手机号验证逻辑
    protected bool ValidatePatientMobile(Patient patient, out string message)
    {
        var patientMobileData = new PatientData
        {
            PatientID = patient.SickID,
            PatientMobileNumber = PatientMobileTextBox.Text,
            InsuranceOrganization = Convert.ToInt32(BaseInsuranceIdTextBox.Text)
        };

        bool isMobileNumberValid = AppointmentInputsChecker.CheckPatinetMobileNumber(patientMobileData, out message);

        if (!isMobileNumberValid)
        {
            PatientMobileTextBox.Focus();
            return false;
        }
        return true;
    }
}
  1. 子窗体继承父窗体,实现抽象属性并保留设计器控件
public partial class PatientRegistrationForm : BasePatientForm
{
    // 返回设计器中已存在的控件,无需修改设计器代码
    protected override Ema.CustomeTextBox.EmaTextBox PatientMobileTextBox => txbPatientMobileNumber;
    protected override Ema.CustomeTextBox.EmaTextBox BaseInsuranceIdTextBox => txbBaseInsuranceID;

    // 业务逻辑中直接调用父类验证方法
    private bool SavePatient()
    {
        if (!ValidatePatientMobile(currentPatient, out string msg))
        {
            MessageBox.Show(msg);
            return false;
        }
        // 后续保存逻辑
        return true;
    }
}

方案二:接口+扩展方法(解耦优先)

若项目已有复杂继承体系,可通过接口定义控件契约,用扩展方法封装逻辑,无需修改窗体继承关系:

  1. 定义接口,声明所需控件属性
public interface IPatientMobileForm
{
    Ema.CustomeTextBox.EmaTextBox txbPatientMobileNumber { get; }
    Ema.CustomeTextBox.EmaTextBox txbBaseInsuranceID { get; }
}
  1. 子窗体实现接口,返回设计器控件
public partial class AppointmentForm : Form, IPatientMobileForm
{
    // 直接返回设计器生成的控件实例
    public Ema.CustomeTextBox.EmaTextBox txbPatientMobileNumber => this.txbPatientMobileNumber;
    public Ema.CustomeTextBox.EmaTextBox txbBaseInsuranceID => this.txbBaseInsuranceID;
}
  1. 编写扩展方法封装验证逻辑
public static class PatientMobileValidationExtensions
{
    public static bool ValidatePatientMobile(this IPatientMobileForm form, Patient patient, out string message)
    {
        var patientMobileData = new PatientData
        {
            PatientID = patient.SickID,
            PatientMobileNumber = form.txbPatientMobileNumber.Text,
            InsuranceOrganization = Convert.ToInt32(form.txbBaseInsuranceID.Text)
        };

        bool isMobileNumberValid = AppointmentInputsChecker.CheckPatinetMobileNumber(patientMobileData, out message);

        if (!isMobileNumberValid)
        {
            form.txbPatientMobileNumber.Focus();
            return false;
        }
        return true;
    }
}
  1. 子窗体调用扩展方法完成验证
private bool SubmitForm()
{
    if (!this.ValidatePatientMobile(currentPatient, out string msg))
    {
        MessageBox.Show(msg);
        return false;
    }
    // 提交逻辑
    return true;
}

方案优势

两种方案均保留子窗体设计器中的控件实例,WinForms设计器可正常生成事件绑定代码,解决OnTextChange等事件异常问题;同时实现验证逻辑的统一封装,避免12个窗体重复编写相同代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 04:47:18