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

调用Items.Clear()触发OnCreateControl()递归调用致ComboBox重复填充?

问题:自定义ComboBox子类填充列表出现重复条目

我自定义了继承自ComboBox的类,遇到异常:下拉列表会被填充两次,最终列表长度是预期的两倍,包含重复条目(明明已经调用了Items.Clear())。

代码如下:

protected override void OnCreateControl()
{
    base.OnCreateControl();
    PopulatePayees();
}

// 填充列表项
private void PopulatePayees()
{
    Document document = Program.GetDocument();
    Items.Clear();
    foreach (ListItem listItem in document.Payees.GetListItems())
        Items.Add(listItem);
}

调试发现,执行PopulatePayees()里的Items.Clear()时,会再次触发OnCreateControl()方法,导致PopulatePayees()被第二次调用。如果注释掉Items.Clear(),列表只会填充一次。

想知道为什么执行Items.Clear()会触发OnCreateControl()的单次递归调用?

调用栈:

BankAccounts.dll!BankAccounts.Controls.PayeeComboBox.PopulatePayees() Line 25   C#  
BankAccounts.dll!BankAccounts.Controls.PayeeComboBox.OnCreateControl() Line 19  C# [External Code]  
BankAccounts.dll!BankAccounts.Controls.PayeeComboBox.PopulatePayees() Line 25   C#  
BankAccounts.dll!BankAccounts.Controls.PayeeComboBox.OnCreateControl() Line 19  C# [External Code]  
BankAccounts.dll!BankAccounts.MainForm.AddTransaction_Click(object sender, System.EventArgs e) Line 191 C#  
BankAccounts.dll!BankAccounts.MainForm.Transactions_AddTransaction(object sender, System.EventArgs e) Line 235 C#  
BankAccounts.dll!BankAccounts.Controls.TransactionListBox.AddTransactionMenu_Click(object sender, System.EventArgs e) Line 153 C# [External Code]  
BankAccounts.dll!BankAccounts.Program.Main() Line 16 C#
分析与解决方案

原因

这是WinForms控件的内部机制导致的:当ComboBox的Items集合被清空时,控件会触发状态变更或重绘操作,而此时如果控件还未完全完成创建流程(OnCreateControl执行过程中),就会再次触发OnCreateControl方法,形成单次递归调用。

解决办法

可以通过以下几种方式避免重复调用填充逻辑:

方法1:添加标志位控制

private bool _isPopulating = false;

protected override void OnCreateControl()
{
    base.OnCreateControl();
    if (!_isPopulating)
    {
        _isPopulating = true;
        try
        {
            PopulatePayees();
        }
        finally
        {
            _isPopulating = false;
        }
    }
}

private void PopulatePayees()
{
    Document document = Program.GetDocument();
    Items.Clear();
    foreach (ListItem listItem in document.Payees.GetListItems())
        Items.Add(listItem);
}

方法2:判断控件创建状态

利用IsHandleCreated属性判断控件是否已完成底层句柄创建,避免在未完全初始化时执行操作:

protected override void OnCreateControl()
{
    base.OnCreateControl();
    if (IsHandleCreated)
    {
        PopulatePayees();
    }
}

private void PopulatePayees()
{
    Document document = Program.GetDocument();
    Items.Clear();
    foreach (ListItem listItem in document.Payees.GetListItems())
        Items.Add(listItem);
}

方法3:移至Load事件触发

将填充逻辑绑定到Load事件,该事件只会在控件完全创建完成后触发一次,不会出现递归问题:

public PayeeComboBox()
{
    Load += PayeeComboBox_Load;
}

private void PayeeComboBox_Load(object sender, EventArgs e)
{
    PopulatePayees();
}

private void PopulatePayees()
{
    Document document = Program.GetDocument();
    Items.Clear();
    foreach (ListItem listItem in document.Payees.GetListItems())
        Items.Add(listItem);
}

内容的提问来源于stack exchange,提问作者Jonathan Wood

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 05:52:42