我想创建一个继承自ComboBox的控件。我想要一个只接受某种类型物品的组合框。
所以我需要推翻Items属性。 (注意新关键字)。 但是我在这个被否决的Items属性的set区域中放了什么?
new public List<TimeSpanItemClass> Items
{
get
{
return base.Items.Cast<TimeSpanItemClass>().ToList();
}
set
{
?
}
}
我无法弄明白,在Google上搜索产量几乎为零。
答案 0 :(得分:4)
组合框已经支持了这一点。
将您的元素列表放入属性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
。如果您这样做并且使用了绑定列表,这些事件将自动转发到组合框,并且值也将在那里更新。
警告:在第一步中,BindingList
和NotifyPropertyChanged
的使用效果非常好,但如果您要更改列表,则可能会遇到麻烦或者另一个线程中的对象属性(导致跨线程异常)。但也可以避免这种情况。
您只需要在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 :(得分:0)
我达到了我想要的目标:
创建一个只接受一种类型的自定义组合框的方法似乎毫无用处。但是我需要在一个大型项目中使用它,其中经常使用具有时间跨度值的组合框。因此,在一个地方(一个班级,一个档案)保留所有必要的东西是最方便的。
使用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));
感谢您的帮助!
答案 2 :(得分:-1)
set {
throw new NotImplementedException("I don't know why I put a setter here, because it doesn't really make sense");
}