如何让我的组合框停止自动更改值

How to make my comboboxes stop autochanging values

找不到我的问题的答案。所以希望有人能帮忙。我必须在一个表单上组合框,它们都填充了来自同一个数据表的数据。问题是当我 select 即 combobox1 的第一个值时,它会自动将 combobox2 设置为相同的值。我希望能够 select 两个不同的值,但不知道我的问题出在哪里。

private void CardWords_Load(object sender, EventArgs e)
    {
        
        try
        {        
            this.comboBox1.DataSource = this.dataSet.Tables["Languages"];
            //this.comboBox1.Items.AddRange(this.dataSet.Languages.ToArray());
            this.comboBox1.DisplayMember = "Language";
            this.comboBox1.ValueMember = "LanguageId";
            this.comboBox1.BindingContext = this.BindingContext;
            //this.comboBox2.Items.AddRange(this.dataSet.Languages.ToArray());
            this.comboBox2.DataSource = this.dataSet.Tables["Languages"];
            this.comboBox2.DisplayMember = "Language";
            this.comboBox2.ValueMember = "LanguageId";
            this.comboBox2.BindingContext = this.BindingContext;
        }
        catch (Exception)
        {

            throw;
        }            
        

    }

当我设置它的项目时,但我希望能够使用 LanguageID 列作为值。

好吧,也许您不应该将所有内容都放在一个过程中。除此之外,这将帮助您了解问题的原因,它还使您能够重用代码,对其进行单元测试,并在需要时更改它而无需更改大量代码。

首先,显然您的表单必须显示“从类似项目列表中 select 编辑的项目,以及 select 另一项目的视觉可能性”。它必须显示两次。这意味着您需要其中两个对象。一个对象可能有 selected 项目 X,另一个对象有 selected 项目 Y。两个对象有相同的项目集合可供选择不会影响这一点。

快速简单的方法

简单的方法是使用公共源数据创建两个BindingList,并将每个BindingList分配给一个ComboBox。

优点:实施速度快,易于使用。如果您想稍后更改它,可以轻松更改(组合框显示不同的集合)。

缺点:效率较低,您将有一个集合包含每个 ComboBox 的公共源数据。您必须对每个 ComboBox 进行一次单元测试。您将拥有一个复制粘贴解决方案,如果您将来添加第三个 ComboBox,则可能容易出错。

假设这些 ComboBox 都将显示类型为 MyItem 的相同对象序列。 ComboBox1 将 select Item1; ComboBox2 将 select Item2。所以 Item1 和 Item2 都是 MyItem 类型。

解决方案将有这样的过程:

// returns the sequence of items to display in the comboBoxes
private IEnumerable<MyItem> GetSourceData() {...}

// Get / Set the source data in ComboBox1
private BindingList<MyItem> DisplayData1
{
    get => (BindingList<MyItem>)this.ComboBox1.DataSource;
    set => this.ComboBox1.DataSource = value;
}

private MyItem SelectedItem1
{
    get => (MyItem)this.ComboBox1.SelectedItem;
    set => this.ComboBox1.SelectedItem = value;
}

ComboBox2 需要类似的方法。

private void InitComboBoxes()
{
    IEnumerable<MyItem> commonComboBoxData = this.GetSourceData();
    this.DisplayData1 = new BindingList<MyItem>(commonComboBoxData.ToList());
    this.DisplayData2 = new BindingList<MyItem>(commonComboBoxData.ToList());
}

如果操作员表示他已完成 selectComboBox 中的项目,例如按下 立即申请 按钮:

private void OnButtonApplyNow_Clicked(object sender, ...)
{
    MyItem selected1 = this.SelectedItem1;
    MyItem selected2 = this.SelectedItem2;
    ProcessSelectedItems(selected1, selected2);
}

就是这样。只有几个单行方法。

简洁的解决方案:创建一个特殊的 UserControl

尽管以前的解决方案可行。如果您的表单中需要 10 个这样的组合框,或者如果您需要在五个表单中执行此操作,那将是一项艰巨的工作。

巧妙的解决方案是创建一个包含 ComboBox 的 UserControl,它允许您设置公共源数据并获取 selected 项目。

为了更好地重用,此 UserControl 将是一个通用的 class,它将显示 T

类型的项目

使用 visual studio 设计器创建一个包含 Docked ComboBox 的 UserControl。

您可以从 ComboBox 派生而不是使用组合,但这会让 class 的用户访问您不希望他们访问的属性。毕竟您不想让 class 的用户将 Customers 放入应该显示 Products.

的 ComboBox 的数据源中
class MyComboBox<T> : UserControl         // TODO: invent a proper name
{
    // constructor and comboBox1 added by designer
    private MyComboBox() {...}
    private ComboBox comboBox1 = ...

添加以上方法:

    private BindingList<T> DisplayedItems
    {
        get => (BindingList<T>)this.ComboBox1.DataSource;
        set => this.ComboBox1.DataSource = value;
    }

    private T SelectedItem
    {
        get => (T)this.ComboBox1.SelectedItem;
        set => this.ComboBox1.SelectedItem = value;
    }

您还需要说明应该显示哪种 属性 类型的 T。

    public string DisplayMember
    {
        get => this.comboBox1.DisplayMember;
        set => this.comboBox1.DisplayMember = value;
    }

编译后您可以使用 visual studio 设计器将 MyComboBox 添加到您的表单中,类似于添加按钮的方式

用法:使用 visual studio 设计器,您添加了 10 个组合框,它们都将显示 Product 类型的项目。前五个 ComboBox 将显示产品的 Name,四个 ComboBox 将显示产品的 Id,一个 ComboBox 将显示 Price.

public MyClass() // constructor
{
    InitializeComponent();

    // Comboboxes that show the name
    this.ComboName1.DisplayMember = nameof(Product.Name);
    this.ComboName2.DisplayMember = nameof(Product.Name);
    this.ComboName3.DisplayMember = nameof(Product.Name);
    this.ComboName4.DisplayMember = nameof(Product.Name);
    this.ComboName5.DisplayMember = nameof(Product.Name);

    // ComboBoxes that show the Id:
    this.ComboId1.DisplayMember = nameof(Product.Id);
    this.ComboId2.DisplayMember = nameof(Product.Id);
    this.ComboId3.DisplayMember = nameof(Product.Id);
    this.ComboId4.DisplayMember = nameof(Product.Id);

    // ComboBox that shows the Price:
    this.ComboPrice1.DisplayMember = nameof(Product.Price);
}

初始化组合框:

private void InitComboboxes
{
    IEnumerable<Product> selectableProducts = GetSelectableProducts();

    var comboBoxesToInit = new MyComboBox<Product>[]
    {
        this.ComboName1, this.ComboName2, ..., this.ComboPrice;
    };

    foreach (MyComboBox comboBoxToInit in comboBoxesToInit)
    {
       comboBoxToInit.DisplayedItems = new BindingList<Product>(selectableProducts.ToList());
    };

做一些测试:如果只将 selecatableProducts 转换为列表一次会发生什么:

List<Product> selectableProducts = GetSelectableProducts().ToList();
foreach(...)
{
    comboBoxToInit.DisplayedItems = new BindingList<Product>(selectableProducts);

也许我们很幸运,这也行得通。