如果在另一个 Combobox 中选择,则隐藏特定的 Combobox 项

Posted

技术标签:

【中文标题】如果在另一个 Combobox 中选择,则隐藏特定的 Combobox 项【英文标题】:Hiding a specific Combobox Item if selected in another Combobox 【发布时间】:2021-11-21 01:59:00 【问题描述】:

我正在尝试实现,如果用户在 Combobox 1 中选择(例如)Value 1,那么 Value 1 不应该可见,或者至少不能在 Combobox 2/3/4/etc. 中被选中。如果 Value 1 未从 Combobox 1 中选择它应该可以再次选择。

我有一个 Combobox Items 的自定义类,如下所示:

    public class ComboboxItem
    
        public string Text   get; set; 
        public int    Value  get; set; 

        public override string ToString()
        
            return Text;
        
    

我的这部分代码从 DataGridView Cells 中挑选值并将它们插入到多个 comboboxes 中:

    private void button1_Click(object sender, EventArgs e)
         int numberofcolumns = dataGridView1.Columns.Count + 1, numberofrows = dataGridView1.Rows.Count;
                for (int i = 1; i < numberofcolumns; i++)
                
                    ComboboxItem item = new ComboboxItem();
                    item.Text = "Spalte " + i;
                    item.Value = i - 1;

                    CBKaNr.Items.Add(item);
                    CBKaLa.Items.Add(item);
                    CBLeTy.Items.Add(item);
                    CBKaMe.Items.Add(item);
                    CBRoVe.Items.Add(item);
                    CBLeNr.Items.Add(item);
                    CBFlNr.Items.Add(item);
                    CBChNr.Items.Add(item);
                    CBAnf1.Items.Add(item);
                    CBAnf2.Items.Add(item);
                    CBEnd1.Items.Add(item);
                    CBEnd2.Items.Add(item);

然后可以在我的界面中选择它们,然后由程序读取,然后将选定的值导出到生成的 .txt 文件中

【问题讨论】:

我不明白……如果用户从“组合框 1”中选择“值 1”……那么“值 1”将在任何其他组合框中不可用,除非用户更改“组合框 1”中的“值 1”? ... 换句话说,ANY 组合框中的任何项目只能选择一次? @JohnG 是的,这正是我想要实现的目标。 【参考方案1】:

我在这种特殊情况下看到的一个问题最好用一个例子来解释。假设有四 (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&lt;ComboboxItem&gt; 项目,以便所有组合框项目都以相同的排序方式显示。由于代码将在更改时向每个组合框添加/删除项目,因此我们希望为所有组合框项目保持一致的顺序,并且每次对列表进行排序是一个简单的解决方案。

因此,我们将让ComboboxItem 类实现IComparable 接口,然后我们可以通过调用Sort() 方法对List&lt;ComboboxItem&gt; 进行排序。此外,我们希望重写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&lt;ComboboxItem&gt; 组合框项目列表。由于每个组合框都需要自己的数据源,因此我们将调用此方法一次以设置一个全局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&lt;ComboboxItem&gt;。我们将使用此列表来获取我们可以使用的所有组合框项目。换句话说,这个方法会给我们一个我们不能使用的项目列表,因为组合框已经选择了该项目。需要注意的是,在调用此代码之前,我们已经设置了一个名为AllCombos 的全局变量List&lt;ComboBox&gt;,它将保存所有使用的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&lt;ComboboxItem&gt;,其中包含尚未在任何组合框中选择的所有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 本身、它的NameDataSource 之外,所有属性都相同。

最后,将所有这些组合在一起的事件/方法是组合框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 ");

下面是上述代码的一个小例子。

我希望这有意义并有所帮助。

【讨论】:

感谢您的解决方案,它不会使程序崩溃。耶。我有一些需要解决的小错误,所以如果最后一切正常,我会将您的解决方案标记为答案:D 再次感谢。 嘿@JohnG,在玩了一会儿代码后,我的程序遇到了瓶颈,不得不将“BlankComboItem's Text”编辑为“N/A”,现在“equals”抛出我出现“System.InvalidCastException:无法将 System.string 转换为 'ComboboxItem'”错误。我不得不更改它,以便以后可以轻松过滤掉空的组合框,因为程序无法正确过滤掉“”。任何想法如何解决它? 出现在“ComboboxItem that = (ComboboxItem)obj;”行中 @DieserTBZ ...您将不得不展示您所做的更改。该错误表明代码正在尝试将stringComboboxItem 进行比较。如果空白ComboboxItem 是“N/A”而不是空字符串应该没有区别。【参考方案2】:

使用DataSource 将数据绑定到组合框,然后在没有给定项目的情况下重新绑定它。

基于 cmets 中的讨论,我给你完整的解决方案。

您需要更改某些部分以与您的代码兼容,因此请在测试之前这样做。 由于我没有您的数据,因此我没有对其进行任何测试并全部从头开始编写,但它应该都可以工作。


public class ComboboxItem

    public string Text   get; set; 
    public int Value  get; set; 

    public override string ToString()
    
        return Text;
    



public class YourForm : Form

    private List<ComboboxItem> baseCmbItems = new List<ComboboxItem>();
    
    // In second part of tuple we will store item selected inside given combobox.
    // I could use dictionary but i think this will be easier for you
    private List<Tuple<ComboBox, ComboboxItem>> comboBoxes = new List<Tuple<ComboBox, ComboboxItem>>(); 

    public YourForm()
    
        InitializeComponents();

        // Populate your datagridview

        // Populate list with items that will be displayed in comboboxes
        // Usually I add first item as < Select Value > so I will do it here but remove it if not needed
        
        baseCmbItems.Add(new ComboboxItem() 
            Text = " < Select Value >",
            Value = -1
        );
        
        for(int i = 0; i < dataGridView1.Columns.Count(); i++)
        
            baseCmbItems.Add(new ComboboxItem() 
                Text = "Spalte" + (i + 1),
                Value = i
            );
        
        
        // Add all combobox controls which shares data to this list
        comboBoxes.Add(new Tuple<ComboBox, ComboboxItem>("Combobox which shares data with others", null));
        comboBoxes.Add(new Tuple<ComboBox, ComboboxItem>("Another combobox which shares data with others", null));
        comboBoxes.Add(new Tuple<ComboBox, ComboboxItem>("Another combobox which shares data with others", null));
        comboBoxes.Add(new Tuple<ComboBox, ComboboxItem>("Another combobox which shares data with others", null));
        
        // Bind base data to all comboboxes inside this list
        foreach(Tuple<ComboBox, ComboboxItem> cb in comboBoxes)
        
            cb.Item1.DataSource = baseCmbItems;
            cb.Item1.DisplayMember = "Text";
            cb.Item1.ValueMember = "Value";
            
            // Binding event
            cb.Item1.SelectedIndexChanged += ComboBox_SelectedIndexChanged;
        
    

    private void ComboBox_SelectedIndexChanged(object sender, EventArgs e)
    
        // Duplicate base combobox items (all)
        List<ComboboxItem> copyItems = new List<ComboboxItem>(baseCmbItems);
        
        // Getting combobox which started this event
        ComboBox sender_cmb = sender as ComboBox;
        
        // Getting newly selected item from combobox which started this event
        ComboboxItem selectedItem = sender_cmb.SelectedItem as ComboboxItem;
        
        // Removing all already selected items from duplicated list of items
        // Also assigning newly selected comboboxitem to sender combobox inside our comboBoxes list
        foreach(Tuple<ComboBox, ComboboxItem> cb in comboBoxes)
        
            if(cb.Item1 == sender_cmb)
                cb.Item2 = selectedItem;
                
            if(cb.Item2 != null)
                copyItems.RemoveAll(x => x.Value == cb.Item2.Value);
        
        
        // Now we have filtered copyItems without any already selected item
        // Now we rebind this data to all comboboxes with addition of that combobox already selected item
        
        foreach(Tuple<ComboBox, ComboboxItem> b in comboBoxes)
        
            if(b.Item2 != null)
            
                List<ComboboxItem> cItems = new List<ComboBoxItem>(copyItems);
                cItems.Add(b.Item2);
                b.Item1.DataSource = cItems;
                b.Item1.SelectedValue = b.Item2.Value;
            
            else
            
                b.Item1.DataSource = copyItems;
                b.Item1.SelectedValue = -1;
            
        
    

【讨论】:

我编辑了我的问题以包含更多信息,这是否会改变您的解决方案? @DieserTBZ 是 ValueTextComboboxItem 唯一吗? Text 存储其中 column 被选中,Value 存储所有 Cell's Values 。我试图隐藏在另一个Comboboxes 中的是Text 部分。希望这能澄清一点...... @DieserTBZ 查看已编辑的答案。我给你写了完整的解决方案

以上是关于如果在另一个 Combobox 中选择,则隐藏特定的 Combobox 项的主要内容,如果未能解决你的问题,请参考以下文章

ComboBox:如果操作员键入文本,然后按回车,则发生哪个事件

如何在另一个类中填充来自foreach的Combobox

如果我在另一个下拉列表中选择特定值,我想将值添加到下拉列表

WPF的ComboBox如果选择另一个值时报错则界面显示上一个值.MVVM

如何令comboBox不能输入,只能选择

从 C# 中的 Combobox 中选择特定项目时,将 Label 值增加 1