使ComboBox只接受特定类型的内容

3
我目前在Windows表单应用程序中有一个ComboBox。为了指定ComboBox将包含哪些值,我将ComboBox的DataSource属性设置为某个数组,以便ComboBox包含来自该数组的值。我也可以使用Items.Add()向ComboBox添加新值。然而,我想确保ComboBox可以用某个特定类型的对象填充。因此,如果我有一个名为X的类,那么我希望只能使用X类型的数组作为ComboBox的数据源。现在,ComboBox接受类型为System.Object的对象。我该如何实现它?是否有一个要设置为我的数据类型名称的ComboBox属性?还是是否有一个事件将检查添加到我的ComboBox的对象是否为所需类型,并在不满足条件时引发异常?
我考虑创建一个新类作为ComboBox的子类型,并覆盖Items属性的Add方法,以便Add检查其参数是否为所需类型(不确定如何实现)。即使我这样做了,仍然有其他方法将新值添加到ComboBox(AddRangeCopyTo等),因此我认为应该有更优雅的解决方案来解决这个问题。

你想要的想法是可能的,但你想让我们中的某个人来完成吗?请尝试构建这个组合框,然后写下你遇到的问题。 - mybirthname
@mybirthname - 不,我只是想知道是否应该覆盖ComboBox.ObjectCollection的Add方法,或者是否有更优雅的方法来实现这一点,因为从技术上讲,我还需要覆盖其他可以向ComboBox添加新值的方法,例如AddRange。 - user3623874
你不能隐藏或覆盖Items接受你的新类型,即使你从它继承; 但是你可以尝试将其包装在UserControl中并公开一个新的Items集合。虽然会有很多代码来模拟现有的属性和集合。当然,CBo已经接受了你特定的类型,只是不是仅仅那种类型。 - Ňɏssa Pøngjǣrdenlarp
我问这个问题只是出于好奇。为什么你要阻止使用此“combobox”的其他类型?如果您在combobox事件中对项进行一些操作,则可以在您正在使用的每个事件处理程序中添加项类型检查...或为此控件命名为“ComboBoxOfMyType”,那么其他程序员将注意到这个combobox使用了某个类型。 - Fabio
@Fabio - 这是一项家庭作业任务,所以我必须按照要求完成。您提到了事件处理程序 - 是否有一个事件会在每次向ComboBox的Items添加内容时触发?如果是这样,那么我可以编写一个事件处理程序,当它看到ComboBox.Items中存在不正确类型的对象时,将抛出异常。 - user3623874
显示剩余2条评论
3个回答

2
如果您想控制ComboBox所包含的项目类型,可以尝试创建一个从ComboBox派生的新类,但是您会遇到问题,即它仍然有ComboBox.ObjectCollection Items属性,该属性仍然接受任何类型!而且(对于您覆盖的想法不幸的是),Add方法并不是虚拟的。
我能想到的唯一实际解决方案是以某种方式抽象ComboBox。如果这不是共享代码,我建议只需创建一个用于向ComboBox添加项目的方法。例如:
// NOTE: All items that are added to comboBox1 need to be of type `SomeType`.
private void AddItemToComboBox(SomeType item)
{
    comboBox1.Items.Add(item);
}

任何试图向ComboBox添加非SomeType对象的尝试都将遭到编译器错误的拒绝。不幸的是,没有简单的方法防止某人直接向ComboBox.Items添加非SomeType项。
再次强调,如果这不是共享代码,这实际上不应该成为问题。

非常感谢您的回复。这正是我所想的。我对C#还很陌生,所以我不知道这是否可行 - 我可以将我的派生ComboBox的Items属性设置为私有,以便只能从AddItemToComboBox()方法中访问Items吗?虽然我认为这是不可能的。 我还考虑过创建一个新的(空)'Items'属性的get方法实现,但在AddItemToComboBox()方法中添加时使用原始的Items属性的get方法。这可能吗? - user3623874

1
你可以通过自己的Items属性将原始ItemsCollection作为参数,来隐藏Items属性。
测试示例类:
public class Order
{
    public Int32 ID { get; set; }
    public string Reference { get; set; }

    public Order() { }
    public Order(Int32 inID, string inReference)
    {
        this.ID = inID;
        this.Reference = (inReference == null) ? string.Empty : inReference;
    }

    //Very important 
    //Because ComboBox using .ToString method for showing Items in the list
    public override string ToString()
    {
        return this.Reference;
    }

}

我尝试使用下一个类将ComboBox的项目集合包装在自己的类型中。添加项目必须是具体类型。 您可以在此处添加其他所需的方法/属性(删除)
public class ComboBoxList<TCustomType>
{
    private System.Windows.Forms.ComboBox.ObjectCollection _baseList;

    public ComboBoxList(System.Windows.Forms.ComboBox.ObjectCollection baseItems)
    {
        _baseList = baseItems;
    }

    public TCustomType this[Int32 index]
    {
        get { return (TCustomType)_baseList[index]; }
        set { _baseList[index] = value; }
    }

    public void Add(TCustomType item)
    {
        _baseList.Add(item);
    }

    public Int32 Count { get { return _baseList.Count; } }

}

这是一个派生自ComboBox的自定义组合框类 新增:泛型
public class ComboBoxCustomType<TCustomType> : System.Windows.Forms.ComboBox
{
    //Hide base.Items property by our wrapping class
    public new ComboBoxList<TCustomType> Items; 

    public ComboBoxCustomType() : base()
    {
        this.Items = new ComboBoxList<TCustomType>(base.Items);
    }

    public new TCustomType SelectedItem 
    { 
        get { return (TCustomType)base.SelectedItem; } 
    }
}

下面的代码用于表单中。
private ComboBoxCustomType<Order> _cmbCustom;

//this method used in constructor of the Form
private void ComboBoxCustomType_Initialize()
{
    _cmbCustom = new ComboBoxCustomType<Order>();
    _cmbCustom.Location = new Point(100, 20);
    _cmbCustom.Visible = true;
    _cmbCustom.DropDownStyle = ComboBoxStyle.DropDownList;
    _cmbCustom.Items.Add(new Order(0, " - nothing - "));
    _cmbCustom.Items.Add(new Order(1, "One"));
    _cmbCustom.Items.Add(new Order(2, "Three"));
    _cmbCustom.Items.Add(new Order(3, "Four"));
    _cmbCustom.SelectedIndex = 0;
    this.Controls.Add(_cmbCustom);
}

是的,这种方法非常有效。我可以补充一下,ComboBoxList并不是从ComboBox.ObjectCollection派生的,因此它不包含ObjectCollection的其他方法,例如AddRange或Remove。因此,为了真正模拟ComboBox功能,我们需要实现所有其他缺失的方法,就像您实现Add()方法一样。 - user3623874
您建议通过编程方式(设置位置、可见属性等)将此自定义ComboBox添加到表单中。由于它使用泛型,我无法在工具箱中看到这种派生的comboBox。是否可以使用工具箱将此自定义comboBox添加到表单中,而不是通过编程方式实现? - user3623874
尝试仅使用您的类型而非泛型来更改ComboBox。 - Fabio
是的,我只是需要在Visual Studio Designer中特别通用的comboBox。 - user3623874

0

不要覆盖ComboBox(如itsme86的答案所述,这样做是行不通的),您可以覆盖usercontrol,将ComboBox添加到其中,然后仅公开您希望使用的元素。类似于以下内容:

public partial class MyComboBox<T> : UserControl where T: class
{
    public MyComboBox()
    {
        InitializeComponent();
    }

    public void Add(T item)
    {
        comboBox1.Items.Add(item);
    }

    public IEnumerable<T> Items
    {
        get { return comboBox1.Items.Cast<T>(); }
    }
}

请注意,一些自动化软件的某些部分依赖于访问底层控件,因此可能会导致一些问题。
这种方法不会改变组合框的Items,因此它们仍将存储为objects,但是当您访问它们时,您将把它们转换为正确的类型,并且只允许添加该类型的对象。您可以通过以下方式创建新的组合框:
 var myCB = new MyComboBox<ItemClass>();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接