我在这种特殊情况下看到的一个问题最好用一个例子来解释。假设有四 (4) 个ComboBoxes,每个组合框有四 (4) 个项目可供选择(“Spalte 1”、“Spalte 2”、“Spalte 3”和“Spalte 4”)。此外……我们还假设用户在组合框 1 中选择了“Spalte 1”,在组合框 2 中选择了“Spalte 2”,在组合框 3 中选择了“Spalte 3”,在组合框 4 中选择了“Spalte 4”……
在这种情况下,如果用户单击任何组合框……那么……每个 ComboBox 将仅包含一 (1) 个可供选择的项目……即……当前选定的项目。那么……如果使用了所有组合框项,用户将如何“更改”其中一个组合框选定项?我的意思是人们确实会犯错误。例如,用户犯了一个错误,想要交换两个组合框值。这种方法可能会导致用户最终陷入死胡同。
授予...“以编程方式”我们可以将组合框的“已选择”索引设置为 -1 并删除当前选定的项目并将其添加回其他组合框项目列表中...但是...我们不知道何时执行此操作.用户必须单击按钮或使用其他机制来“表示”他们想要“删除”当前选定的项目并将组合框的值设置为“空”值。
幸运的是,有一个简单而通用的解决方案。在大多数情况下,在处理组合框时,我倾向于将“空”项添加到组合框项目列表中,并且通常会将其设置为列表中的第一项。我喜欢将此“空白/空”组合框项视为用户说“我不想为此组合框选择任何项”的一种方式。这对用户来说很直观并将解决前面描述的问题。下面的解决方案使用了这种方法。
此外,考虑到每个组合框在其项目列表中都有不同的项目……那么……每个组合框都必须有自己的 OWN 数据源。对所有组合框使用“相同”数据源……将不起作用。因此,每当用户更改任何组合框值时,我们都会为每个组合框创建一个新的数据源。
我相信可能会有其他更好的方法来实现这一点,因此这可能被认为是一种“hacky”方法,但是它会起作用。这个想法是这样工作的……当组合框值发生变化时,我们想要获取当前未选择的组合框值的列表。换句话说……我们可以选择的“可用”项目。一旦我们有了这个列表……然后我们就可以遍历每个ComboBox 并检查它当前选择的值。如果所选值不是“空”值,那么我们只需将该项目添加到可用项目列表中,然后将该组合框数据源设置到此列表中。
例如,如果组合框选择了“Spalte 1”……那么我们会知道该项目不在我们的“可用项目”列表中,因为它已经在当前组合框中被选中。因此,我们需要将它添加到“可用项目”列表中,只是为了那个特定的组合框。我们继续以这种方式处理所有组合框。一种特殊情况是,如果用户从项目列表中选择了“空白”项目。在这种情况下,我们已经在“可用项目”列表中有一个“空白”项目,我们将简单地忽略这些组合框值,因为我们不想添加重复的“空白”值。
为组合框数据源设置一个特殊类并使用您当前的ComboboxItem 类是明智之举……我冒昧地添加了一些更改以简化主代码。一项更改是考虑我们将要“排序”List<ComboboxItem> 项目,以便所有组合框项目都以相同的排序方式显示。由于代码将在更改时向每个组合框添加/删除项目,因此我们希望为所有组合框项目保持一致的顺序,并且每次对列表进行排序是一个简单的解决方案。
因此,我们将让ComboboxItem 类实现IComparable 接口,然后我们可以通过调用Sort() 方法对List<ComboboxItem> 进行排序。此外,我们希望重写Equals 方法,以使代码能够使用List.Contains 方法来查看特定的ComboboxItem 是否已经在列表中。这用于防止重复项。
最后,由于我们想要一个“空白”ComboboxItem... 我们将实现一个“静态”BlankComboItem 属性,该属性将返回一个“空白”ComboboxItem,使其Value 属性设置为零 (0) 及其 Text 属性将设置为空字符串。这个修改后的类可能看起来像……
public class ComboboxItem : IComparable {
public int Value { get; set; }
public string Text { get; set; }
public override bool Equals(object obj) {
if (obj == DBNull.Value)
return false;
ComboboxItem that = (ComboboxItem)obj;
if (this.Value.Equals(that.Value) && this.Text.Equals(that.Text)) {
return true;
}
return false;
}
public override int GetHashCode() {
return (Value.ToString() + Text).GetHashCode();
}
public int CompareTo(object obj) {
ComboboxItem that = (ComboboxItem)obj;
return this.Value.CompareTo(that.Value);
}
public static ComboboxItem BlankComboItem {
get {
return new ComboboxItem() { Value = 0, Text = "" };
}
}
}
接下来,我们将创建三个简单的方法来帮助管理组合框并帮助我们确定哪些值属于哪个组合框。第一个方法GetComboList() 返回我们默认的List<ComboboxItem> 组合框项目列表。由于每个组合框都需要自己的数据源,因此我们将调用此方法一次以设置一个全局AllItems 列表,我们将在接下来的方法中使用该列表,然后我们将为每个组合框调用一次以将每个组合框初始设置为相同的值,但从技术上讲,它们将是“不同”的列表。这种方法可能看起来像……
private List<ComboboxItem> GetComboList() {
int numberofcolumns = dataGridView1.Columns.Count;
List<ComboboxItem> items = new List<ComboboxItem>();
ComboboxItem item = ComboboxItem.BlankComboItem;
items.Add(item);
for (int i = 1; i <= numberofcolumns; i++) {
item = new ComboboxItem();
item.Text = "Spalte " + i;
item.Value = i;
items.Add(item);
}
return items;
}
接下来,我们需要一个方法GetCurrentSelectedItems(),它遍历所有ComboBoxes,并返回所有当前“选定”组合框项目的List<ComboboxItem>。我们将使用此列表来获取我们可以使用的所有组合框项目。换句话说,这个方法会给我们一个我们不能使用的项目列表,因为组合框已经选择了该项目。需要注意的是,在调用此代码之前,我们已经设置了一个名为AllCombos 的全局变量List<ComboBox>,它将保存所有使用的ComboBoxes。这个GetCurrentSelectedItems 方法可能看起来像……
private List<ComboboxItem> GetCurrentSelectedItems() {
List<ComboboxItem> selectedItems = new List<ComboboxItem>();
foreach (ComboBox combo in AllCombos) {
if (combo.SelectedIndex != -1) {
if (!selectedItems.Contains((ComboboxItem)combo.SelectedItem)) {
selectedItems.Add((ComboboxItem)combo.SelectedItem);
}
}
}
return selectedItems;
}
请记住,此方法可能/将返回一个“空白”组合框项,因为组合框可以选择“空白”项。
最后,我们将创建一个方法GetAvailableComboItems,它将返回一个List<ComboboxItem>,其中包含尚未在任何组合框中选择的所有ComboboxItems。代码只是循环遍历AllItems 列表并检查该项目是否已经在上述方法中的usedItems 列表中。如果该项目不是已使用列表,那么我们会将其添加到此“可用项目”列表中。如果该项目已被使用,那么显然我们不想将其添加到“可用项目”列表中。这种方法可能看起来像……
private List<ComboboxItem> GetAvailableComboItems() {
List<ComboboxItem> availableItems = new List<ComboboxItem>();
List<ComboboxItem> usedItems = GetCurrentSelectedItems();
foreach (ComboboxItem item in AllItems) {
if (!usedItems.Contains(item)) {
availableItems.Add(item);
}
}
return availableItems;
}
要将所有这些放在一起,我们首先需要设置全局变量和所有组合框。这是在表单加载事件中完成的,可能看起来像……
private List<ComboboxItem> AllItems = new List<ComboboxItem>();
private List<ComboBox> AllCombos;
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
AllCombos = new List<ComboBox>();
AllCombos.Add(comboBox1);
AllCombos.Add(comboBox2);
AllCombos.Add(comboBox3);
AllCombos.Add(comboBox4);
AllItems = GetComboList();
SetComboBoxesSelectedIndexChangedEvent(false);
SetComboBoxProperties(comboBox1, "Combo1", GetComboList());
SetComboBoxProperties(comboBox2, "Combo2", GetComboList());
SetComboBoxProperties(comboBox3, "Combo3", GetComboList());
SetComboBoxProperties(comboBox4, "Combo4", GetComboList());
SetComboBoxesSelectedIndexChangedEvent(true);
}
private void SetComboBoxesSelectedIndexChangedEvent(bool Subscribed) {
foreach (ComboBox combo in AllCombos) {
if (Subscribed) {
combo.SelectedIndexChanged += new EventHandler(ComboBox_SelectedIndexChanged);
}
else {
combo.SelectedIndexChanged -= new EventHandler(ComboBox_SelectedIndexChanged);
}
}
}
private void SetComboBoxProperties(ComboBox combo, string name, List<ComboboxItem> data) {
combo.Name = name;
combo.DisplayMember = "Text";
combo.ValueMember = "Value";
combo.DataSource = data;
combo.SelectedIndex = 0;
}
SetComboBoxesSelectedIndexChanged(bool) 方法用于订阅/取消订阅每个组合框SelectedIndexChanged 事件。当组合框加载数据时,DataSource 被设置并且默认的“空白”项被设置为每个组合框中的默认选定项。加载数据时,我们不需要触发ComboBox_SelectedIndexChanged 事件,此方法打开/关闭事件。这同样适用于SelectedIndexChanged 事件本身。代码将更改数据源并重新设置选定的索引,在这种情况下,这将重新触发我们不需要也不需要的事件。
全局AllItems 变量被所有ComboboxItems 填充,并将由前面描述的方法使用。此外,全局AllCombos 列表填充了所有ComboBoxes,以便更轻松地循环遍历所有不同的组合框。最后是一个方法SetComboBoxProperties,它将设置每个组合框的属性。除了ComboBox 本身、它的Name 和DataSource 之外,所有属性都相同。
最后,将所有这些组合在一起的事件/方法是组合框SelectedIndexChanged 事件。我们将对所有组合框使用相同的事件。遍历此方法会在每个 ComboBox 中启动一个 foreach 循环。首先,我们得到一个可用项目的新列表,并为该组合框获取当前选择的ComboboxItem。如果当前选择的组合框索引不是-1,那么这意味着“某物”已经被选中,我们需要将该项目添加到可用项目列表中。接下来进行检查以确保“空白”项目在可用项目列表中,因为我们总是希望“空白”项目在可用项目列表中。最后更新组合框数据源并将选择设置为最初选择的项目。当我们更新其DataSource 时,我们将丢失组合框“选定”项,并且我们希望组合框保持其当前选定的项,因此我们需要将其设置回其原始值。这个ComboBox_SelectedIndexChanged 事件可能看起来像……
int sic = 0;
private void ComboBox_SelectedIndexChanged(object sender, EventArgs e) {
Debug.WriteLine("ComboBox_SelectedIndexChanged <- Enter " + ++sic);
List<ComboboxItem> AvailableItems;
ComboboxItem curItem;
foreach (ComboBox combo in AllCombos) {
AvailableItems = GetAvailableComboItems();
curItem = (ComboboxItem)combo.SelectedItem;
if (combo.SelectedIndex != -1) {
if (combo.SelectedItem != ComboboxItem.BlankComboItem) {
AvailableItems.Add((ComboboxItem)combo.SelectedItem);
}
}
// since we ignore the blank items we need to make sure at least one blank item is available
if (!AvailableItems.Contains(ComboboxItem.BlankComboItem)) {
AvailableItems.Add(ComboboxItem.BlankComboItem);
}
AvailableItems.Sort();
combo.SelectedIndexChanged -= new EventHandler(ComboBox_SelectedIndexChanged);
combo.DataSource = AvailableItems;
combo.SelectedItem = curItem;
combo.SelectedIndexChanged += new EventHandler(ComboBox_SelectedIndexChanged);
}
Debug.WriteLine("ComboBox_SelectedIndexChanged -> Leave ");
}
下面是上述代码的一个小例子。
我希望这有意义并有所帮助。