构造函数传值最直接,适合父窗体打开子窗体并传入初始数据;公共属性回传数据最常用且安全;跨窗体访问应封装属性而非暴露控件;复杂或实时场景用事件,注意及时解绑防泄漏。

用构造函数传值最直接,但要注意窗体生命周期
新建窗体时传参是最自然的方式,适合「父窗体打开子窗体并传入初始数据」的场景。关键点在于:子窗体的构造函数要接收参数,并在内部保存为字段或属性。
常见错误是传入了引用类型(比如 List<string>)后,在子窗体里直接修改,结果父窗体看到数据也变了——这不是 bug,是引用共享的正常行为。如果不想影响原数据,得手动深拷贝或只传只读视图。
示例:
public partial class ChildForm : Form
{
private readonly string _title;
private readonly int _id;
public ChildForm(string title, int id)
{
InitializeComponent();
_title = title;
_id = id;
Text = _title;
}
}调用时:var child = new ChildForm("编辑用户", 1024); child.ShowDialog();
用公共属性回传数据,比事件更轻量
子窗体关闭前把结果存到公共属性里,父窗体在 ShowDialog() 返回后立刻读取——这是最常用、最不容易出错的回传方式。
注意两点:一是属性必须是 public 且有 get(不需要 set);二是一定要在子窗体关闭前赋值,比如放在按钮点击事件末尾或 FormClosing 里。
- 不要在子窗体的
Load事件里赋值,那时还没到用户操作阶段 - 如果子窗体可能被
Show()非模态打开,就得额外判断DialogResult或加标志位,否则读到的可能是旧值 - 值类型(
int、bool)直接赋值即可;引用类型建议用readonly字段 + 属性封装,避免外部篡改
示例子窗体属性:
public string ResultText { get; private set; } = string.Empty;父窗体中:if (child.ShowDialog() == DialogResult.OK) { MessageBox.Show(child.ResultText); }
跨窗体访问控件值?别直接暴露 TextBox,用属性封装
新手常犯的错:在子窗体里写 public TextBox NameTextBox;,然后父窗体直接读 child.NameTextBox.Text。这破坏封装,还容易因控件未初始化或已被释放而抛 NullReferenceException。
正确做法是提供语义化属性,内部做空值和状态检查:
public string UserName => NameTextBox?.Text?.Trim() ?? string.Empty;public bool IsConfirmed => ConfirmCheckBox?.Checked == true;- 如果 TextBox 可能为空但业务上不允许,属性 getter 里可抛
InvalidOperationException,比让父窗体到处判空更清晰
复杂数据或需实时通知?用事件比轮询靠谱
当父窗体需要在子窗体操作过程中就响应变化(比如进度更新、选项切换),或者数据结构太重不适合塞进属性时,事件是合理选择。
定义事件时别用 EventHandler 硬套,优先用泛型 EventHandler<TEventArgs> 或自定义委托。事件名用动词过去式(DataSubmitted、SelectionChanged),别叫 OnDataSubmit——那是供子类重写的保护方法名。
容易忽略的一点:事件订阅后必须在父窗体合适时机(如子窗体关闭后)取消订阅,否则造成内存泄漏。用 using 或 Dispose 模式管理不现实,稳妥做法是在子窗体的 FormClosed 事件里触发一次“完成”事件,然后由父窗体主动 -= 解绑。


















