C# WinForms控件存储调用异常及数据恢复故障排查
控件保存与加载问题的解决方案
场景描述
- 父窗体:包含自定义控件列表,以及打开子窗体的按钮
- 子窗体:支持向TableLayoutPanel添加/移除自定义控件、将控件状态保存到父窗体列表、调用已保存状态填充TableLayoutPanel
- 自定义控件:包含一行ComboBox和TextBox的TableLayoutPanel
需求与问题
需求
点击子窗体保存按钮时,将控件状态保存到父窗体列表并关闭子窗体;打开新子窗体时,用保存的状态填充其TableLayoutPanel。
问题
- 直接保存控件实例到父窗体列表后,关闭子窗体再打开新子窗体加载时,触发
System.ObjectDisposedException: 'Cannot access a disposed object. Object name: 'uEligibilityCondition'.' - 尝试新建控件实例复制属性时,TextBox值无法恢复,ComboBox选中项赋值失败,且保存时TextBox值未正确存入列表。
代码与尝试过程
初始代码(未关闭子窗体时可正常工作)
private void SaveSessionConditions() { for (int i = 0; i < tableLayoutPanelConditions.RowCount - 1; i++) { uCustomControl custom = tableLayoutPanelConditions.GetControlFromPosition(0, i) as uCustomControl; parent.sessionConditions.Add(custom); // 原代码笔误为control } } private void CallSessionConditions() { foreach (uCustomControl custom in parent.conditions) { int row = tableLayoutPanelConditions.RowCount - 1; // 报错行:控件已被Dispose tableLayoutPanelConditions.Controls.Add(custom, 0, row); tableLayoutPanelConditions.RowCount++; tableLayoutPanelConditions.RowStyles.Add(new RowStyle(SizeType.Absolute, 40F)); } }
尝试新建控件实例(控件数量正确,但状态无法恢复)
private void CallSessionConditions() { foreach (uCustomControl custom in conditions) { uCustomControl newCustom = new uCustomControl(); newCustom.comA = custom.comA; newCustom.comB = custom.comB; newCustom.texA = custom.texA; newCustom.texB = custom.texB; int row = tableLayoutPanelConditions.RowCount - 1; tableLayoutPanelConditions.Controls.Add(newCustom, 0, row); tableLayoutPanelConditions.RowCount++; tableLayoutPanelConditions.RowStyles.Add(new RowStyle(SizeType.Absolute, 40F)); } }
自定义控件属性(存在笔误)
public string comA { get { return comboBoxA.SelectedItem.ToString(); } set { comboBoxA.SelectedItem = value; } } // 原代码get访问的是comB而非comboBoxB,导致错误 public string comB { get { return comboBoxB.SelectedItem.ToString(); } set { comboBoxB.SelectedItem = value; } } public string texA { get { return textBoxA.Text; } set { textBoxA.Text = value; } } public string texB { get { return textBoxB.Text; } set { textBoxB.Text = value; } }
尝试自定义赋值方法(ComboBox恢复,但TextBox仍为空)
public void SetFromControlInput(uEligibilityCondition input) { comboBoxA.SelectedItem = input.comboBoxA.SelectedItem; comboBoxB.SelectedItem = input.comboBoxB.SelectedItem; textBoxA.Text = input.textBoxA.Text; textBoxB.Text = input.textBoxB.Text; }
解决方案
核心问题:不能直接保存控件实例,子窗体关闭时控件会被自动Dispose,导致后续访问异常。正确做法是用数据模型存储控件状态,而非控件本身。
1. 创建数据模型类
// 用于存储自定义控件的状态数据 public class ControlState { public string ComboBoxAValue { get; set; } public string ComboBoxBValue { get; set; } public string TextBoxAValue { get; set; } public string TextBoxBValue { get; set; } }
2. 修改父窗体列表类型
将父窗体的sessionConditions类型从List<uCustomControl>改为List<ControlState>。
3. 修正保存方法(存储状态而非控件)
private void SaveSessionConditions() { parent.sessionConditions.Clear(); // 清空旧数据 for (int i = 0; i < tableLayoutPanelConditions.RowCount - 1; i++) { uCustomControl custom = tableLayoutPanelConditions.GetControlFromPosition(0, i) as uCustomControl; if (custom == null) continue; // 提取控件状态存入模型 ControlState state = new ControlState { ComboBoxAValue = custom.comA, ComboBoxBValue = custom.comB, TextBoxAValue = custom.texA, TextBoxBValue = custom.texB }; parent.sessionConditions.Add(state); } this.Close(); // 安全关闭子窗体 }
4. 修正加载方法(根据状态创建新控件)
private void CallSessionConditions() { foreach (ControlState state in parent.sessionConditions) { uCustomControl newCustom = new uCustomControl(); // 恢复文本框内容 newCustom.texA = state.TextBoxAValue; newCustom.texB = state.TextBoxBValue; // 恢复ComboBox选中项:先判断值是否存在于Items中,避免赋值失败 if (newCustom.comboBoxA.Items.Contains(state.ComboBoxAValue)) { newCustom.comA = state.ComboBoxAValue; } if (newCustom.comboBoxB.Items.Contains(state.ComboBoxBValue)) { newCustom.comB = state.ComboBoxBValue; } // 添加到TableLayoutPanel int row = tableLayoutPanelConditions.RowCount - 1; tableLayoutPanelConditions.Controls.Add(newCustom, 0, row); tableLayoutPanelConditions.RowCount++; tableLayoutPanelConditions.RowStyles.Add(new RowStyle(SizeType.Absolute, 40F)); } }
5. 修复自定义控件属性的笔误
确保comB属性的get访问器正确指向comboBoxB(原代码存在笔误):
public string comB { get { return comboBoxB.SelectedItem.ToString(); } set { comboBoxB.SelectedItem = value; } }
关键说明
- 控件实例属于子窗体,关闭子窗体时会被Dispose,因此不能直接保存控件实例到父窗体。
- 数据模型仅存储状态值,不依赖控件生命周期,是跨窗体传递状态的正确方式。
- ComboBox赋值时需先检查值是否在Items集合中,否则
SelectedItem无法正确设置。
内容的提问来源于stack exchange,提问作者RyanCode_4012
相关产品推荐
相关产品推荐

