2010-03-03 3 views
2

Есть ли простой способ создания обертки BindingList (с проецированием), которая будет обновляться в качестве первоначального обновления списка?BindingList projection wrapper

Например, у меня есть изменяемый список чисел, и я хочу представить их как шестнадцатеричные строки в ComboBox. Используя эту обертку я мог бы сделать что-то вроде этого:

BindingList<int> numbers = data.GetNumbers(); 
comboBox.DataSource = Project(numbers, i => string.Format("{0:x}", i)); 

Я мог бы обернуть список в новый BindingList, обрабатывать все исходные события, обновить список и снова сгореть эти события, но я чувствую, что есть более простой способ уже.

ответ

2

Я только что наткнулся на этот вопрос, и я понял, что могу опубликовать код, в котором я оказался.

Поскольку я хотел быстрое решение, я сделал своего рода реализацию бедного человека. Он работает как оболочка вокруг существующего исходного списка, но он создает полный проецируемый список элементов и обновляет его по мере необходимости. Сначала я надеялся, что смогу сделать проекцию «на лету», поскольку элементы доступны, но для этого потребуется реализовать весь интерфейс IBindingList с нуля.

Что имеется: любые обновления в списке источников также будут обновлять целевой список, поэтому связанные элементы управления будут соответствующим образом обновлены.

Что он не делает: он не обновляет список источников при изменении целевого списка. Для этого потребуется инвертированная проекционная функция, и мне все равно не нужна эта функциональность. Поэтому элементы всегда должны быть добавлены, изменены или удалены в исходном списке.

Пример использования следует. скажем, у нас есть список номеров, но мы хотим, чтобы отобразить их квадраты значений в сетке данных:

// simple list of numbers 
List<int> numbers = new List<int>(new[] { 1, 2, 3, 4, 5 }); 

// wrap it in a binding list 
BindingList<int> sourceList = new BindingList<int>(numbers); 

// project each item to a squared item 
BindingList<int> squaredList = new ProjectedBindingList<int, int> 
    (sourceList, i => i*i); 

// whenever the source list is changed, target list will change 
sourceList.Add(6); 
Debug.Assert(squaredList[5] == 36); 

А вот исходный код:

public class ProjectedBindingList<Tsrc, Tdest> 
    : BindingList<Tdest> 
{ 
    private readonly BindingList<Tsrc> _src; 
    private readonly Func<Tsrc, Tdest> _projection; 

    public ProjectedBindingList(
     BindingList<Tsrc> source, 
     Func<Tsrc, Tdest> projection) 
    { 
     _projection = projection; 
     _src = source; 
     RecreateList(); 
     _src.ListChanged += new ListChangedEventHandler(_src_ListChanged); 
    } 

    private void RecreateList() 
    { 
     RaiseListChangedEvents = false; 
     Clear(); 

     foreach (Tsrc item in _src) 
      this.Add(_projection(item)); 

     RaiseListChangedEvents = true; 
    } 

    void _src_ListChanged(object sender, ListChangedEventArgs e) 
    { 
     switch (e.ListChangedType) 
     { 
      case ListChangedType.ItemAdded: 
       this.InsertItem(e.NewIndex, Proj(e.NewIndex)); 
       break; 

      case ListChangedType.ItemChanged: 
       this.Items[e.NewIndex] = Proj(e.NewIndex); 
       break; 

      case ListChangedType.ItemDeleted: 
       this.RemoveAt(e.NewIndex); 
       break; 

      case ListChangedType.ItemMoved: 
       Tdest movedItem = this[e.OldIndex]; 
       this.RemoveAt(e.OldIndex); 
       this.InsertItem(e.NewIndex, movedItem); 
       break; 

      case ListChangedType.Reset: 
       // regenerate list 
       RecreateList(); 
       OnListChanged(e); 
       break; 

      default: 
       OnListChanged(e); 
       break; 
     } 
    } 

    Tdest Proj(int index) 
    { 
     return _projection(_src[index]); 
    } 
} 

Я надеюсь, что кто-то найдет это полезным ,