具有强类型项的组合框

本文关键字:组合 强类型 | 更新日期: 2023-09-27 18:06:01

我想创建一个继承自ComboBox的控件。我想要一个只接受特定类型项目的组合框。

所以我需要否决Items属性。(注意新关键字)。但是我在这个被否决的项目属性的集合区域中放什么呢?

    new public List<TimeSpanItemClass> Items
    {
        get
        {
            return base.Items.Cast<TimeSpanItemClass>().ToList();
        }
        set
        {
            ?
        }
    }

我想不出来,在谷歌上搜索几乎一无所获。

具有强类型项的组合框

这个组合框已经支持了。

将元素列表放入属性DataSource:

var persons = new List<Person>();
// ToDo: fill list with some values...
myComboBox.DataSource = persons;

在属性DisplayMember中输入对象的属性,表示用户应该看到的内容。如果你不设置它,组合框将调用所选元素的.ToString()

myComboBox.DisplayMember = "FullName";

在属性ValueMember中输入您希望在代码中从对象接收的属性。如果你不设置它,组合框将返回对象本身。

myComboBox.ValueMember = "Born";

要将当前选中的对象从组合框中取出,只需将属性SelectedValue强制转换为所需的类型。

private void OnComboBoxFormatSelectedIndexChanged(object sender, EventArgs e)
{
    DateTime born = (DateTime)comboBox.SelectedValue
}

修改赋值后的列表

如果您需要更改列表,或者如果其项之后,您已将数据源分配给组合框,则必须将此更改通知组合框。最简单的方法是将数据源重新分配给组合框:

myComboBox.DataSource = persons;

一个更简洁的方法是如果列表本身可以在发生任何更改时触发一个事件。这个特性是由BindingList<T>实现的,如果你通过添加或删除一个元素来改变列表,那么这个组合框就会自动更新。

信息流的下一步将是通知组合框,如果项目本身已经更改(在我们的示例中,例如,一个人的姓)。要实现这一点,列表中的对象必须要么实现PropertyNameChanged事件(在我们的示例中,这将是LastNameChanged,因为属性名称将是LastName),要么必须在类中实现INotifyPropertyChanged。如果你这样做,你使用绑定列表,这些事件将自动转发到组合框,值也将在那里更新。

警告:在第一步中,BindingListNotifyPropertyChanged的使用效果很好,但是如果您要在另一个线程中更改列表或对象属性(导致跨线程异常),则可能会遇到麻烦。但也有可能避免这种情况。

你只需要在你的ComboBox和BindingList之间再加一层;a BindingSource;这具有挂起和恢复通知链的能力,以便您可以从另一个线程更改列表:

var persons = new BindingList<Person>();
var bindingSource = new BindingSource();
bindingSource.DataSource = persons;
comboBox.DataSource = bindingSource;
// Suspend change the list from another thread,
// and resume on the gui thread.
bindingSource.SuspendBinding();
Task.Factory.StartNew(() => persons.Add(Person.GetRandomFromDatabase()))
            .ContinueWith(finishedTask => bindingSource.ResumeBinding(),
                            TaskScheduler.FromCurrentSynchronizationContext());
set {
    throw new NotImplementedException("I don't know why I put a setter here, because it doesn't really make sense");
}

我已经实现了我想要的:

  1. 创建一个只允许添加TimeSpan值的自定义组合框控件
  2. 以自定义格式显示时间跨度值。

创建一个只接受一种类型的自定义组合框似乎毫无用处。但我需要这在一个大项目,其中组合框与时间跨度值经常使用。因此,把所有必要的东西放在一个地方(一个类,一个文件)是最方便的。

通过使用BindingList(以前从未使用过)。这是我的代码。

public partial class ComboBoxTimeSpan : ComboBox
{
    private BindingList<TimeSpanItemClass> _BindingList = new BindingList<TimeSpanItemClass>();
    public ComboBoxTimeSpan()
    {
        InitializeComponent();
        Items = new BindingList<TimeSpan>();
        this.Items.ListChanged += Items_ListChanged;
        this.DataSource = _BindingList;
    }
    void Items_ListChanged(object sender, ListChangedEventArgs e)
    {
        _BindingList.Clear();
        foreach (TimeSpan ts in Items)
        {
            _BindingList.Add(new TimeSpanItemClass(ts));
        }
    }
    /// <summary>
    /// The items in this combobox need to be of the type TimeSpan as this combobox is designed for showing time span values in easy to read text.
    /// </summary>
    new public BindingList<TimeSpan> Items
    {
        get;
        private set;
    }
    /// <summary>
    /// The ComboBoxTimeSpan has items that can all be converted to a time span.
    /// They will display as 1 hour, 2 hours, 1 minute, 1 hour and 2 minutes, 1 day, 2 weeks and 3 days, 3 days, etc...
    /// Its precise on the microsecond, no less
    /// </summary>
    private class TimeSpanItemClass : Object
    {
        /// <summary>
        /// The timespan that this object represents
        /// </summary>
        public TimeSpan timespan
        {
            get;
            set;
        }
        /// <summary>
        /// The constructor of this class needs a TimeSpan object
        /// </summary>
        public TimeSpanItemClass(TimeSpan ts)
        {
            timespan = ts;
        }
        /// <summary>
        /// The textual represention of the time span that this object represents.
        /// </summary>
        /// <returns>A string by a simple format</returns>
        public override string ToString()
        {
            //Specify your custom format here
            return timespan.ToString();
        }
    }
}  

现在组合框可以用作

cbts = new ComboBoxTimeSpan();
ctbs.Add(TimeSpan.FromDays(1));

谢谢你的帮助!