如何让我的组合框停止自动更改值
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);
也许我们很幸运,这也行得通。
找不到我的问题的答案。所以希望有人能帮忙。我必须在一个表单上组合框,它们都填充了来自同一个数据表的数据。问题是当我 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
.
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);
也许我们很幸运,这也行得通。