【问题标题】:Combobox with strong typed items具有强类型项目的组合框
【发布时间】:2013-03-27 12:46:49
【问题描述】:

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

所以我需要否决 Items 属性。 (注意新的关键字)。 但是我应该在这个被否决的 Items 属性的设置区域中放置什么?

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

我想不通,在 Google 上搜索几乎一无所获。

【问题讨论】:

  • ComboBox 本身不支持set 上的Items。为什么需要它?
  • 你不能只遍历 setter 中的列表并将它们添加到组合框中,还是我错过了什么?
  • 也许你被误导了;属性需要指定bothgetset。如果你愿意,你可以只声明一个。
  • 我认为你正在努力解决这个问题..我认为最简单的方法是创建一个 ListClass or Specific DataTypeBind that List 到 Combobox`,除非我我没有正确阅读您的问题..另一种方法是创建一个Class,例如public class TimeSpanItemEx&lt;T&gt;: System.Windows.Forms.ComboBox{ }

标签: c# winforms combobox


【解决方案1】:

组合框已经支持这个。

将您的元素列表放入属性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&lt;T&gt; 实现,如果您通过添加或删除元素来更改列表,组合框会自动更新。

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

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

您只需要在 ComboBox 和 BindingList 之间再增加一层; 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());

【讨论】:

  • +1 更清洁的答案,再次成为cat 皮肤的众多方法之一
  • 我只能在填充List&lt;T&gt;(); 后设置CustomCombbox.DataSource = List&lt;T&gt;(); 时才能使其工作但是当我在CustomCombobox 的构造函数中设置this.DataSource = this.Items 并稍后添加项目时打开,项目不显示。
  • 所以现在我有这个this.cbtsSelectPresetPeriod.Items.Add(TimeSpan.FromDays(2 * 7)); this.cbtsSelectPresetPeriod.Items.Add(TimeSpan.FromDays(4 * 7)); this.cbtsSelectPresetPeriod.Items.Add(TimeSpan.FromDays(8 * 7)); this.cbtsSelectPresetPeriod.Items.Add(TimeSpan.FromDays(16 * 7)); this.cbtsSelectPresetPeriod.DataSource = this.cbtsSelectPresetPeriod.Items; this.cbtsSelectPresetPeriod.Items.Add(TimeSpan.FromDays(32 * 7)); 但是第五个值没有显示。为什么?
【解决方案2】:
set {
    throw new NotImplementedException("I don't know why I put a setter here, because it doesn't really make sense");
}

【讨论】:

  • 那么我如何将项目放入 Item 属性。当我将自定义组合框放在表单上时。 Designer.cs 调用 customcombobox.Items = null;而且您的答案并不是真正的答案,因此没有太大帮助。
  • 这并没有提供问题的答案。要批评或要求作者澄清,请在其帖子下方发表评论。
  • @MikedeKlerk 你不需要二传手。您没有物理列表,而是从基本项目列表中提取列表。要向其中添加项目,请将它们添加到基本项目列表中。
  • @SztupY 如果他坚持要有一个 setter,这是唯一可行的实现。
  • @MikeCaron 你有没有在编程时有一个物理列表:D 我的总是在内存中。我很好奇您用于处理物理列表的计算机和语言:P。
【解决方案3】:

我已经实现了我想要的:

  1. 创建只允许添加 TimeSpan 值的自定义 Combobox 控件
  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));

感谢大家的帮助!

【讨论】:

    猜你喜欢
    • 2018-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-13
    • 2015-02-02
    • 1970-01-01
    • 1970-01-01
    • 2010-11-14
    相关资源
    最近更新 更多