2017-06-29 30 views
1

我想有2個列表,並希望它們是我的組合框的數據源, 有沒有辦法做到這一點,沒有第三個列表結合每個變化的其他2個列表,如:綁定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); 
    } 
+1

你可以做的嘗試:'comboBox1.DataSource = list1.Concat(列表2);' – Mederic

+0

尼斯一個,謝謝 – Pedro

+0

我將其添加爲一個答案 – Mederic

回答

1

這是一個很好的例子,您可能想考慮切換到WPF。 WPF API具有內置的此功能,其格式爲CompositeCollection類。

這就是說,它是可能實現的WinForms類似的效果。你「只是」必須編寫自己的綁定源實現,它可以聚合現有的源。我將「just」放在引號中,因爲正確地以全功能的方式實現綁定源是不平凡的。

這就是說,這裏,將簡單的情況下工作的例子(即那些不涉及排序源數據):

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 
} 

注:以上

  1. 要求您原始數據包含在BindingList<T>對象中,而不是List<T>對象。原因主要是,嘗試使用List<T>對象嘗試執行此類操作並沒有什麼意義,因爲List<T>未提供任何種類的列表更改通知,因此不會有一種有效的方式無論如何,控制都要綁定到數據更新。
  2. 此實現使用C#中的新值元組功能。要編譯此代碼,您需要使用C#7,並且需要使用NuGet程序包管理器來安裝System.ValueTuple程序包。

上面的實現維護源對象的集合,並實現與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類&hellip;我認爲你可以得到成立你自己):

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; 
} 
相關問題