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

C# WinForms控件存储调用异常及数据恢复故障排查

控件保存与加载问题的解决方案

场景描述

  • 父窗体:包含自定义控件列表,以及打开子窗体的按钮
  • 子窗体:支持向TableLayoutPanel添加/移除自定义控件、将控件状态保存到父窗体列表、调用已保存状态填充TableLayoutPanel
  • 自定义控件:包含一行ComboBox和TextBox的TableLayoutPanel

需求与问题

需求

点击子窗体保存按钮时,将控件状态保存到父窗体列表并关闭子窗体;打开新子窗体时,用保存的状态填充其TableLayoutPanel。

问题

  1. 直接保存控件实例到父窗体列表后,关闭子窗体再打开新子窗体加载时,触发System.ObjectDisposedException: 'Cannot access a disposed object. Object name: 'uEligibilityCondition'.'
  2. 尝试新建控件实例复制属性时,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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 10:34:54