C# - Winforms - Combobox - 避免选择更新数据源的第一项

C# - Winforms - Combobox - Avoid selecting the first item updating the datasource

我的应用程序中有一个组合框,其中项目根据您可以在文本字段中输入的搜索文本异步加载。

这工作正常,但每次在更新组合框的数据源期间自动选择第一项的文本。

这会导致意外行为,因为我需要让用户输入的搜索文本保留在组合框的文本字段中,直到用户完成选择,而不是自动覆盖第一个条目的文本。

这是我的代码:

public partial class ProductGroupDescription : UserControl, Interfaces.TabPages.ITabPageProductGroupDescription
{
    private Services.IProductGroupDescriptionService _ApplicationService;

    public BindingList<ProductGroup> ProductGroups { get; set; } = new BindingList<ProductGroup>();
    public string ProductGroupSearchText { get; set; } = string.Empty;

    public ProductGroupDescription(Services.IProductGroupDescriptionService applicationService)
    {
        InitializeComponent();
        InitialSetupControls();
        _ApplicationService = applicationService;
    }

    public void InitialSetupControls()
    {
        var pgBindingSource = new BindingSource();
        pgBindingSource.DataSource = ProductGroups;
        Cbo_ProductGroup.DataSource = pgBindingSource.DataSource;
        Cbo_ProductGroup.DataBindings.Add("Text", ProductGroupSearchText, "");
    }

    private async void Cbo_ProductGroup_TextChanged(object sender, EventArgs e)
    {
        if (Cbo_ProductGroup.Text.Length >= 2)
        {
            ProductGroupSearchText = Cbo_ProductGroup.Text;
            Cbo_ProductGroup.SelectedIndex = -1;
            bool withStopFlagged = Chk_StopFlag_PGs_Included.Checked;
            List<ProductGroup> list = await _ApplicationService.GetProductGroupBySearchString(ProductGroupSearchText, withStopFlagged);
            if (list != null && list.Count > 0)
            {
                ProductGroups.Clear();
                list.ForEach(item => ProductGroups.Add(item));
                Cbo_ProductGroup.DroppedDown = Cbo_ProductGroup.Items.Count > 0 && Cbo_ProductGroup.Focused;
            }
        }
    }
}

我试过设置Cbo_ProductGroup.SelectedIndex = -1,但没有解决我的问题。

我也在 SO 上看到了这个:Prevent AutoSelect behavior of a System.Window.Forms.ComboBox (C#)

但是这个问题真的没有更简单的解决方案吗?

我现在开始工作了。 当我删除组合框文本字段的绑定时,它起作用了

Cbo_ProductGroup.DataBindings.Add("Text", ProductGroupSearchText, "");

并将新(旧)值直接设置到组合框的文本字段。

Cbo_ProductGroup.Text = searchText;

在这种情况下,一个新事件 Text_Changed 被触发,因此应用程序有一个无限循环。所以我用了一个属性(ShouldTextChangedEventBeIgnored),如果Text_Changed事件应该被忽略。 感谢@CaiusJard 在评论中提供的许多提示。

这是我的最终代码:

public partial class ProductGroupDescription : UserControl, Interfaces.TabPages.ITabPageProductGroupDescription
{
    private ApplicationLogic.Interfaces.Services.IProductGroupDescriptionService _ApplicationService;

    public BindingList<ProductGroup> ProductGroups { get; set; } = new BindingList<ProductGroup>();
    public bool ShouldTextChangedEventBeIgnored { get; set; } = false;

    public ProductGroupDescription(ApplicationLogic.Interfaces.Services.IProductGroupDescriptionService applicationService)
    {
        _ApplicationService = applicationService;
        InitializeComponent();
        InitialSetupControls();
    }

    public void InitialSetupControls()
    {
        var pgBindingSource = new BindingSource();
        pgBindingSource.DataSource = ProductGroups;
        Cbo_ProductGroup.DataSource = pgBindingSource.DataSource;
    }

    private async Task<List<ProductGroup>> LoadProductGroupItems(string searchText)
    {
        bool withStopFlagged = Chk_StopFlag_PGs_Included.Checked;
        return await _ApplicationService.GetProductGroupBySearchString(searchText, withStopFlagged);
    }

    private async Task SetProductGroupSearchBoxItems(List<ProductGroup> list, string searchText)
    {
        await Task.Run(() =>
        {
            if (list != null && list.Count > 0)
            {
                ShouldTextChangedEventBeIgnored = true;
                Cbo_ProductGroup.Invoke((c) =>
                {
                    ProductGroups.Clear();
                    list.ForEach(item => ProductGroups.Add(item));
                    c.DroppedDown = c.Items.Count > 0 && c.Focused;
                    c.Text = searchText;
                    c.Select(c.Text.Length, 0);
                });
                ShouldTextChangedEventBeIgnored = false;
            }
        });
    }

    private async void Cbo_ProductGroup_TextChanged(object sender, EventArgs e)
    {
        try
        {
            if (Cbo_ProductGroup.Text.Length >= 2 && ShouldTextChangedEventBeIgnored == false)
            {
                string searchText = Cbo_ProductGroup.Text;
                List<ProductGroup> list = await LoadProductGroupItems(Cbo_ProductGroup.Text);
                await SetProductGroupSearchBoxItems(list, searchText);
            }
        }
        catch(Exception ex)
        {
            System.Diagnostics.Trace.Write(ex);
        } 
    }
}