我希望有2个列表并希望它们成为我的组合框的数据源, 有没有办法做到这一点,没有第三个列表组合每个变化的其他2个列表,如:
List<string> list1 = new List<string>(),
list2 = new List<string>(),
list3 = new List<string>();
private void Init()
{
comboBox1.DataSource = list3;
}
private void ListsChanged()
{
list3.Clear();
list3.AddRange(list1);
list3.AddRange(list2);
}
答案 0 :(得分:0)
这是您可能想要考虑切换到WPF的一个很好的例子。 WPF API以CompositeCollection
类的形式内置了此功能。
也就是说,可以在Winforms中实现类似的效果。你&#34;只是&#34;必须编写自己的绑定源实现,可以聚合现有的源。我把&#34;只是&#34;在引号中,因为以完全功能的方式正确实现绑定源是非常重要的。
也就是说,这是一个适用于简单案例的例子(即那些不涉及对源数据进行排序的案例):
class CompositeBindingList<T> : IList<T>, IBindingList, ICancelAddNew
{
private const string _kstrIndexOutOfRange = "index must be non-negative and less than Count";
private readonly List<BindingList<T>> _sources = new List<BindingList<T>>();
#region CompositeBindingList<T>-specific members
public void AddBindingList(BindingList<T> list)
{
list.AddingNew += _OnAddingNew;
list.ListChanged += _OnListChanged;
_sources.Add(list);
ListChanged?.Invoke(this, new ListChangedEventArgs(ListChangedType.Reset, 0));
}
public void RemoveBindingList(int index)
{
_sources.RemoveAt(index);
ListChanged?.Invoke(this, new ListChangedEventArgs(ListChangedType.Reset, 0));
}
public int BindingListCount
{
get { return _sources.Count; }
}
#endregion
#region BindingList-mirroring members
public event AddingNewEventHandler AddingNew;
public T AddNew()
{
if (_sources.Count == 0)
{
_sources.Add(new BindingList<T>());
}
return _sources[_sources.Count - 1].AddNew();
}
#endregion
#region IList<T> members
public T this[int index]
{
get { return _sources[_GetSourceIndexOrThrow(ref index)][index]; }
set { _sources[_GetSourceIndexOrThrow(ref index)][index] = value; }
}
public int Count => _sources.Sum(s => s.Count);
public bool IsReadOnly => _sources.Cast<ICollection<T>>().All(s => s.IsReadOnly);
public void Add(T item)
{
if (_sources.Count == 0)
{
_sources.Add(new BindingList<T>());
}
_sources[_sources.Count - 1].Add(item);
}
public void Clear() => _sources.Clear();
public bool Contains(T item) => _sources.Any(s => s.Contains(item));
public void CopyTo(T[] array, int arrayIndex)
{
foreach (BindingList<T> source in _sources)
{
source.CopyTo(array, arrayIndex);
arrayIndex += source.Count;
}
}
public IEnumerator<T> GetEnumerator() => _sources.SelectMany(s => s).GetEnumerator();
public int IndexOf(T item)
{
var (source, index, baseIndex) = _GetIndexOf(item);
return index >= 0 ? baseIndex + index : -1;
}
private (BindingList<T> source, int index, int baseIndex) _GetIndexOf(T item)
{
int baseIndex = 0;
foreach (BindingList<T> source in _sources)
{
int index = source.IndexOf(item);
if (index >= 0)
{
return (source, index, baseIndex);
}
baseIndex += source.Count;
}
return (null, -1, -1);
}
public void Insert(int index, T item)
{
int sourceIndex = _GetSourceIndex(ref index);
if (sourceIndex == -1)
{
if (index != 0)
{
throw new IndexOutOfRangeException(_kstrIndexOutOfRange);
}
sourceIndex = _sources.Count - 1;
index = _sources[sourceIndex].Count;
}
_sources[sourceIndex].Insert(index, item);
}
public bool Remove(T item)
{
var (source, index, baseIndex) = _GetIndexOf(item);
if (index < 0)
{
return false;
}
else
{
source.RemoveAt(baseIndex + index);
return true;
}
}
public void RemoveAt(int index) => _sources[_GetSourceIndex(ref index)].RemoveAt(index);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
#endregion
#region ICollection members
object ICollection.SyncRoot => throw new NotSupportedException();
bool ICollection.IsSynchronized => false;
void ICollection.CopyTo(Array array, int index)
{
CopyTo((T[])array, index);
}
#endregion
#region IList members
bool IList.IsFixedSize => _sources.Cast<IList>().All(s => s.IsFixedSize);
object IList.this[int index]
{
get => this[index];
set => this[index] = (T)value;
}
int IList.Add(object value)
{
if (value is T)
{
Add((T)value);
return Count - 1;
}
else
{
return -1;
}
}
bool IList.Contains(object value)
{
return Contains((T)value);
}
int IList.IndexOf(object value)
{
return value is T ? IndexOf((T)value) : -1;
}
void IList.Insert(int index, object value)
{
Insert(index, (T)value);
}
void IList.Remove(object value)
{
if (value is T)
{
Remove((T)value);
}
}
#endregion
#region IBindingList members
public event ListChangedEventHandler ListChanged;
bool IBindingList.AllowNew => _sources.All(s => s.AllowNew);
bool IBindingList.AllowEdit => _sources.All(s => s.AllowEdit);
bool IBindingList.AllowRemove => _sources.All(s => s.AllowRemove);
bool IBindingList.SupportsChangeNotification => _sources.Cast<IBindingList>().All(s => s.SupportsChangeNotification);
bool IBindingList.SupportsSearching => _sources.Cast<IBindingList>().All(s => s.SupportsSearching);
bool IBindingList.SupportsSorting => false;
bool IBindingList.IsSorted => false;
PropertyDescriptor IBindingList.SortProperty => throw new NotSupportedException();
ListSortDirection IBindingList.SortDirection => throw new NotSupportedException();
object IBindingList.AddNew()
{
return AddNew();
}
void IBindingList.AddIndex(PropertyDescriptor property)
{
foreach (IBindingList list in _sources)
{
list.AddIndex(property);
}
}
void IBindingList.ApplySort(PropertyDescriptor property, ListSortDirection direction)
{
throw new NotSupportedException();
}
int IBindingList.Find(PropertyDescriptor property, object key)
{
int baseIndex = 0;
foreach (IBindingList list in _sources)
{
int index = list.Find(property, key);
if (index >= 0)
{
return baseIndex + index;
}
baseIndex += list.Count;
}
return -1;
}
void IBindingList.RemoveIndex(PropertyDescriptor property)
{
foreach (IBindingList list in _sources)
{
list.RemoveIndex(property);
}
}
void IBindingList.RemoveSort()
{
throw new NotSupportedException();
}
#endregion
#region ICancelAddNew
public void CancelNew(int itemIndex)
{
_sources[_sources.Count - 1].CancelNew(itemIndex);
}
public void EndNew(int itemIndex)
{
_sources[_sources.Count - 1].EndNew(itemIndex);
}
#endregion
#region Private implementation details
private void _OnListChanged(object sender, ListChangedEventArgs e)
{
int baseIndex = 0, sourceIndex = -1;
for (int i = 0; i < _sources.Count; i++)
{
if (_sources[i] == sender)
{
sourceIndex = i;
break;
}
baseIndex += _sources[i].Count;
}
if (sourceIndex == -1)
{
throw new Exception("internal exception -- unknown sender of ListChanged event");
}
ListChangedEventArgs e2;
switch (e.ListChangedType)
{
case ListChangedType.ItemAdded:
case ListChangedType.ItemChanged:
case ListChangedType.ItemDeleted:
e2 = new ListChangedEventArgs(e.ListChangedType, e.NewIndex + baseIndex);
break;
case ListChangedType.ItemMoved:
if (e.PropertyDescriptor != null)
{
e2 = new ListChangedEventArgs(e.ListChangedType, e.NewIndex + baseIndex, e.PropertyDescriptor);
}
else
{
e2 = new ListChangedEventArgs(e.ListChangedType, e.NewIndex + baseIndex, e.OldIndex + baseIndex);
}
break;
case ListChangedType.PropertyDescriptorAdded:
case ListChangedType.PropertyDescriptorChanged:
case ListChangedType.PropertyDescriptorDeleted:
e2 = new ListChangedEventArgs(e.ListChangedType, e.PropertyDescriptor);
break;
case ListChangedType.Reset:
e2 = new ListChangedEventArgs(e.ListChangedType, e.NewIndex + baseIndex);
break;
default:
throw new ArgumentException("invalid value for e.ListChangedType");
}
ListChanged?.Invoke(this, e2);
}
private void _OnAddingNew(object sender, AddingNewEventArgs e)
{
AddingNew?.Invoke(this, e);
}
private int _GetSourceIndexOrThrow(ref int index)
{
int sourceIndex = _GetSourceIndex(ref index);
if (sourceIndex >= 0)
{
return sourceIndex;
}
throw new IndexOutOfRangeException(_kstrIndexOutOfRange);
}
private int _GetSourceIndex(ref int index)
{
int sourceIndex = 0;
while (sourceIndex < _sources.Count && index > _sources[sourceIndex].Count)
{
index -= _sources[sourceIndex].Count;
sourceIndex++;
}
if (sourceIndex < _sources.Count)
{
return sourceIndex;
}
return -1;
}
#endregion
}
注意:
BindingList<T>
个对象中,而不是List<T>
个对象中。其原因主要是尝试使用List<T>
个对象进行此类操作并不合理,因为List<T>
并未提供任何类型的列表更改通知,所以无论如何都不会有一种有效的方法让控件绑定到数据更新。上面的实现维护了一个源BindingList<T>
对象的集合,并实现了BindingList<T>
所做的相同接口。它将对其进行的操作代理到适当的源列表,并且当任何源列表发生更改时,它会引发绑定到DataSource
属性的相应事件。
我尝试正确实现Add()
和ICancelAddNew
接口。这在使用例如DataGridView
。但是,我没有费心去测试代码的任何部分,因为它与你的问题没有直接关系。
请注意,我不实现了任何排序功能。这样做需要更多的开销,包括在实现和它使用的源列表之间需要额外的间接层(即维护聚合数据的排序视图)。
我确实实际上实现了比严格必要的更多。我可以在任何突变成员(插入,添加,删除等)中抛出NotSupportedException()
,并要求所有修改都要通过原始源列表。那么上面只会转发ListChangedEvent
。但是,那里的乐趣在哪里? :)
将上述课程添加到项目后,使用它非常简单。只需创建此类的实例,使用要包含的每个源AddBindingList()
对象调用BindingList<T>
,然后将comboBox1.DataSource
设置为此新CompositeBindingList<T>
实例。控件将观察对CompositeBindingList<T>
或其源列表的任何更改,并更新其内容以进行匹配。 (修改CompositeBindingList<T>
对象将相应地修改其中一个基础源列表。)
请参阅下面的完整Winforms示例。
对于它的价值,如果您坚持使用List<T>
作为源列表对象,那么您也可以在源列表发生更改时从头开始重新创建数据源。顺便说一句,你可以用以下的东西:
comboBox1.DataSource = list1.Concat(list2).ToArray();
正如所承诺的,这里有完整的Winforms代码来演示(不包括Program
类......我假设您可以自己设置):
public class Form1 : Form
{
private readonly BindingList<string> _list1 = new BindingList<string>();
private readonly BindingList<string> _list2 = new BindingList<string>();
private readonly CompositeBindingList<string> _composite = new CompositeBindingList<string>();
private readonly ViewModel _model = new ViewModel();
class ViewModel : INotifyPropertyChanged
{
private int _index;
public int Index
{
get { return _index; }
set { _UpdateField(ref _index, value); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void _UpdateField<T>(ref T field, T newValue,
Action<T> onChangedCallback = null,
[CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, newValue))
{
return;
}
T oldValue = field;
field = newValue;
onChangedCallback?.Invoke(oldValue);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public Form1()
{
InitializeComponent();
textBox1.DataBindings.Add("Text", _model, "Index", false, DataSourceUpdateMode.OnPropertyChanged);
listBox1.DataSource = _list1;
listBox2.DataSource = _list2;
_composite.AddBindingList(_list1);
_composite.AddBindingList(_list2);
listBox3.DataSource = _composite;
comboBox1.DataSource = _composite;
}
private void button1_Click(object sender, EventArgs e)
{
_list1.Add($"_list1: {_list1.Count + 1}");
}
private void button2_Click(object sender, EventArgs e)
{
_list2.Add($"_list2: {_list2.Count + 1}");
}
private void button3_Click(object sender, EventArgs e)
{
comboBox1.DataSource = _list2.Concat(_list1).ToArray();
}
private void button4_Click(object sender, EventArgs e)
{
_composite.Insert(_model.Index, $"global: {_composite.Count + 1}");
}
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.listBox1 = new System.Windows.Forms.ListBox();
this.button2 = new System.Windows.Forms.Button();
this.listBox2 = new System.Windows.Forms.ListBox();
this.comboBox1 = new System.Windows.Forms.ComboBox();
this.listBox3 = new System.Windows.Forms.ListBox();
this.button3 = new System.Windows.Forms.Button();
this.button4 = new System.Windows.Forms.Button();
this.textBox1 = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(13, 13);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(160, 53);
this.button1.TabIndex = 0;
this.button1.Text = "Add";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// listBox1
//
this.listBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)));
this.listBox1.FormattingEnabled = true;
this.listBox1.IntegralHeight = false;
this.listBox1.ItemHeight = 31;
this.listBox1.Location = new System.Drawing.Point(13, 72);
this.listBox1.Name = "listBox1";
this.listBox1.Size = new System.Drawing.Size(315, 506);
this.listBox1.TabIndex = 1;
//
// button2
//
this.button2.Location = new System.Drawing.Point(343, 12);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(160, 53);
this.button2.TabIndex = 0;
this.button2.Text = "Add";
this.button2.UseVisualStyleBackColor = true;
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// listBox2
//
this.listBox2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)));
this.listBox2.FormattingEnabled = true;
this.listBox2.IntegralHeight = false;
this.listBox2.ItemHeight = 31;
this.listBox2.Location = new System.Drawing.Point(343, 71);
this.listBox2.Name = "listBox2";
this.listBox2.Size = new System.Drawing.Size(315, 507);
this.listBox2.TabIndex = 1;
//
// comboBox1
//
this.comboBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.comboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBox1.FormattingEnabled = true;
this.comboBox1.Location = new System.Drawing.Point(729, 20);
this.comboBox1.Name = "comboBox1";
this.comboBox1.Size = new System.Drawing.Size(286, 39);
this.comboBox1.TabIndex = 2;
//
// listBox3
//
this.listBox3.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.listBox3.FormattingEnabled = true;
this.listBox3.IntegralHeight = false;
this.listBox3.ItemHeight = 31;
this.listBox3.Location = new System.Drawing.Point(674, 72);
this.listBox3.Name = "listBox3";
this.listBox3.Size = new System.Drawing.Size(338, 506);
this.listBox3.TabIndex = 1;
//
// button3
//
this.button3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.button3.Location = new System.Drawing.Point(574, 12);
this.button3.Name = "button3";
this.button3.Size = new System.Drawing.Size(149, 53);
this.button3.TabIndex = 3;
this.button3.Text = "Refresh";
this.button3.UseVisualStyleBackColor = true;
this.button3.Click += new System.EventHandler(this.button3_Click);
//
// button4
//
this.button4.Location = new System.Drawing.Point(12, 598);
this.button4.Name = "button4";
this.button4.Size = new System.Drawing.Size(177, 52);
this.button4.TabIndex = 4;
this.button4.Text = "Add Global";
this.button4.UseVisualStyleBackColor = true;
this.button4.Click += new System.EventHandler(this.button4_Click);
//
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(195, 606);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(154, 38);
this.textBox1.TabIndex = 5;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(16F, 31F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(1027, 660);
this.Controls.Add(this.textBox1);
this.Controls.Add(this.button4);
this.Controls.Add(this.button3);
this.Controls.Add(this.comboBox1);
this.Controls.Add(this.listBox3);
this.Controls.Add(this.listBox2);
this.Controls.Add(this.listBox1);
this.Controls.Add(this.button2);
this.Controls.Add(this.button1);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button button1;
private System.Windows.Forms.ListBox listBox1;
private System.Windows.Forms.Button button2;
private System.Windows.Forms.ListBox listBox2;
private System.Windows.Forms.ComboBox comboBox1;
private System.Windows.Forms.ListBox listBox3;
private System.Windows.Forms.Button button3;
private System.Windows.Forms.Button button4;
private System.Windows.Forms.TextBox textBox1;
}