Привязка к событию всех объектов в списке - PullRequest
6 голосов
/ 11 июля 2011

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

Желаемое решение: Итак, я пытаюсь придумать что-то вродеEventBindingList, который содержит подключаемые объекты и позволяет пользователю подключать сразу несколько объектов, а также добавлять и удалять объекты в списке.

Чтобы сделать его универсальным, необходимо использовать Reflection.В конструкторе списка пользователь может указать EventInfo или по имени события, какое событие перехватывается.Это казалось самым простым способом.

    private EventInfo _info;

    public EventBindingList(string EventName)
    {
        _info = typeof(T).GetEvents().Where(e => e.Name == EventName).First();
    }

    public EventBindingList(EventInfo info)
    {
        _info = info;
    }

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

Неудачное решение 1:

Я попробовал одно решение, которое не сработало, - использовать пользовательский метод доступа к событиям.Это будет событие в списке, содержащее объекты для подключения.Это связано с тем, что при добавлении EventHandler создается исключение ArgumentException: Object of type 'System.EventHandler' cannot be converted to type 'ExternalProject.CustomEventHandler'. Я попытался привести EventHandler к правильному типу (используя аргументы универсального типа, так как это обработчик событий внешнего проекта), но приведение завершится неудачей.

    public event EventHandler ElementEvent
    {
        add
        {
            _handlers.Add(value);
            foreach (T t in this)
            {
                _info.AddEventHandler(t, value);
            }
        }
        remove
        {
            foreach (T t in this)
            {
                _info.RemoveEventHandler(t, value);
            }
            _handlers.Remove(value);
        }
    }

Неудачное решение 2:

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

Запрос: Есть ли какие-либодругие идеи о том, как это может быть достигнуто?

Ответы [ 2 ]

5 голосов
/ 11 июля 2011

Редактировать:

public abstract class ManagedEventCollection<T,TEventArgs> : IList<T>
{
   private EventInfo m_event;
   public ManagedEventCollection(string eventName)
   {
      m_list = new ObservableCollection<T> ();
      m_list.CollectionChanged += CollectionChanged;
      m_event = typeof (T).GetEvent (eventName);
   }
   // Add/Remove/Indexer/Clear methods alter contents of m_list.

   public EventHandler<TEventArgs> Handler{get;set;}

   protected abstract void OnItemAdded(T item);
   protected abstract void OnItemRemoved(T item);

   private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs ea)
   {
      foreach (T item in ea.NewItems)
      {
         m_event.AddEventHandler (
            item, 
            Delegate.CreateDelegate (m_event.EventHandlerType, item, Handler.Method));
      }
      foreach (T item in ea.OldItems)
      {
         m_event.RemoveEventHandler (
            item, 
            Delegate.CreateDelegate (m_event.EventHandlerType, item, Handler.Method));
      }
   }
}

Оригинальный ответ: Вы можете использовать ObservableCollection<T>. В этом классе есть событие CollectionChanged, в котором вы можете подписаться / отписаться от событий по мере необходимости.

Я бы создал базовый класс (это из памяти, просто чтобы получить точку зрения).

public abstract class ManagedEventCollection<T> : IList<T>
{
   public ManagedEventCollection()
   {
      m_list = new ObservableCollection<T> ();
      m_list.CollectionChanged += CollectionChanged;
   }
   ... // Add/Remove/Indexer/Clear methods alter contents of m_list.
   protected abstract void OnItemAdded(T item);
   protected abstract void OnItemRemoved(T item);

   private ObservableCollection<T> m_list;
   private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs ea)
   {
      foreach (T item in ea.NewItems)
         OnItemAdded(item);
      foreach (T item in ea.OldItems)
         OnItemRemoved(item);
   }
}

Тогда ваш производный тип может сделать это:

public class DogManagedEventCollection : ManagedEventCollection<Dog>
{
   protected override OnItemAdded (Dog dog)
   {
      dog.Bark += Bark;
   }
   protected override OnItemRemoved (Dog dog)
   {
      dog.Bark -= Bark;
   }

   private void Bark(object sender, BarkEventArgs ea){...}
}

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

1 голос
/ 11 июля 2011

Не проверял это, поскольку я не могу в настоящее время, поэтому продолжайте с осторожностью:).

Не будет ли что-то подобное этой работой, если по какой-то причине ObservableCollection не то, что вы искалидля (который, кстати, я думаю, намного лучше и чище решение, чем мое)?

public class MyEventList<TElementType,TEventArgType>: IList<TElementType> where TEventArgType: EventArgs
{
    private EventInfo eventInfo;
    private EventHandler<TEventArgType> eventHandler;

    public MyEventList(string eventName, EventHandler<TEventArgType> eventHandler)
    {
        if (eventHandler == null)
            throw new ArgumentNullException("eventHandler");
        if (eventName == null)
            throw new ArgumentNullException("eventName");

        this.eventInfo = typeof(TElementType).GetEvent(eventName);

        if (this.eventInfo == null)
            throw new ArgumentException("Specified event not found.", "eventName");

        if (this.eventInfo.EventHandlerType != eventHandler.GetType())
            throw new ArgumentException("EventHandler type does not match specified event.", "eventHandler");

        this.eventHandler = eventHandler;
    }

    public void Add(TElementType item)
    {
        ...
        eventInfo.AddEventHandler(item, this.eventHandler);
        ...
    }

    public bool Remove(TElementType item)
    {
        ...
        eventInfo.RemoveEventHandler(item, this.eventHandler);
        ...
    }

    ...

 }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...