基于DRY原则重构老旧C# WinForms应用:解决控件事件问题
WinForms多窗体重复控件与逻辑优化方案
问题背景
维护老旧C# Windows Forms项目时,12个窗体包含相同控件(如手机号输入框),需执行重复的手机号验证逻辑。已抽离CheckPatinetMobileNumber验证方法,但仍需在每个窗体重复编写调用逻辑;尝试将控件移至父窗体类导致控件OnTextChange等事件失效。
核心问题原因
之前的操作中,删除子窗体设计器的控件定义并在父窗体声明控件实例,导致WinForms设计器无法自动生成事件绑定代码(如TextChanged事件),进而引发事件异常。
可行解决方案
方案一:抽象父窗体+子类实现控件属性
通过抽象父窗体封装通用逻辑,子窗体保留设计器控件并通过属性暴露给父类,既复用逻辑又保留事件绑定:
- 定义抽象父窗体,封装验证逻辑并声明抽象控件属性
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; } }
- 子窗体继承父窗体,实现抽象属性并保留设计器控件
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; } }
方案二:接口+扩展方法(解耦优先)
若项目已有复杂继承体系,可通过接口定义控件契约,用扩展方法封装逻辑,无需修改窗体继承关系:
- 定义接口,声明所需控件属性
public interface IPatientMobileForm { Ema.CustomeTextBox.EmaTextBox txbPatientMobileNumber { get; } Ema.CustomeTextBox.EmaTextBox txbBaseInsuranceID { get; } }
- 子窗体实现接口,返回设计器控件
public partial class AppointmentForm : Form, IPatientMobileForm { // 直接返回设计器生成的控件实例 public Ema.CustomeTextBox.EmaTextBox txbPatientMobileNumber => this.txbPatientMobileNumber; public Ema.CustomeTextBox.EmaTextBox txbBaseInsuranceID => this.txbBaseInsuranceID; }
- 编写扩展方法封装验证逻辑
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; } }
- 子窗体调用扩展方法完成验证
private bool SubmitForm() { if (!this.ValidatePatientMobile(currentPatient, out string msg)) { MessageBox.Show(msg); return false; } // 提交逻辑 return true; }
方案优势
两种方案均保留子窗体设计器中的控件实例,WinForms设计器可正常生成事件绑定代码,解决OnTextChange等事件异常问题;同时实现验证逻辑的统一封装,避免12个窗体重复编写相同代码。
内容的提问来源于stack exchange,提问作者Ali Majed
相关产品推荐
相关产品推荐

