2016-03-14 5 views
0

У меня есть класс, который наследуется от ObservableCollection<T>. В этом классе у меня есть метод, который меняет коллекцию внутри и для которой я хотел бы подавить события CollectionChanged.Неплохо ли использовать список вызовов унаследованного события?

public class ContentBlockList : ObservableCollection<int> { 
    public void SomeMethod() { 
     var handlers = CollectionChanged.GetInvocationList(); 

     foreach (NotifyCollectionChangedEventHandler handler in handlers) { 
      CollectionChanged -= handler; 
     } 

     // do stuff here 

     foreach (NotifyCollectionChangedEventHandler handler in handlers) { 
      CollectionChanged += handler; 
     } 
    } 
} 

Интуитивно кажется, что это должно работать, так как я получаю доступ к событию из его содержащего объекта. К сожалению, компилятор говорит

событие «ObservableCollection.CollectionChanged» может появиться только на левой стороне + = или - =

я могу получить код для работы, если я переопределить как CollectionChanged и OnCollectionChanged(), по существу заменяя версии .NET копиями моих собственных. Однако делать что-то подобное вызывает у меня подозрение, что я игнорирую причину, почему это плохое решение. Спасибо за любые мысли по этому поводу.

+3

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

+0

Это отличная идея и более показатель того, какое поведение я ищу. Я буду рад принять это за ответ, если вы опубликуете его. Благодарю. –

+1

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

ответ

1

Поскольку отмена подписки и повторная подписка на мероприятие относительно (не очень болезненная, но я не знаю, сколько абонентов, вероятно, будет) медленным процессом, я бы порекомендовал вам взглянуть на переопределение как OnCollectionChanged, так и OnPropertyChanged методы для основания ObservableCollection.

Так что-то, что напоминает:

public class ContentBlockList : ObservableCollection<int> 
{ 
    private bool internallyUpdating; 

    public void SomeMethod() 
    { 
     this.internallyUpdating = true; 

     // Do Stuff (Add to base collection) 

     this.internallyUpdating = false; 
     this.OnPropertyChanged(new PropertyChangedEventArgs(@"Count"); 
     this.OnPropertyChanged(new PropertyChangedEventArgs(@"Item[]"); 
     this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); 
    } 

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) 
    { 
     if(this.internallyUpdating) 
     { 
      return; 
     } 

     base.OnCollectionChanged(e); 
    } 

    protected override void OnPropertyChanged(PropertyChangedEventArgs e) 
    { 
     if(this.internallyUpdating) 
     { 
      return; 
     } 

     base.OnPropertyChanged(e); 
    } 
} 

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

При добавлении в эту коллекцию обычно (то есть с contentBlockList.Add(1)), вы попадете прямо к вызову базового события. Но когда вы пытаетесь обновить внутренне, вы будете подавлять эти события, пока не закончите. Я бы сказал, что это более эффективно с точки зрения производительности, но также и гораздо более аккуратный код, чем то, на что вы смотрели.

В последнем примечании я также скажу, что NotifyCollectionChangedEventAction, который вы предоставляете, - Reset.Вы, вероятно, внесли немало изменений в коллекцию и для обработки, вам нужно, чтобы любой подписчик должен был обновить свой вид в коллекции, будь то элемент управления в представлении WPF или даже другой класс, который использует коллекцию.

0

лучше использовать это:

public class ContentBlockList : ObservableCollection<int> 
    { 
     ContentBlockList() 
     { 
      this.CollectionChanged += ContentBlockList_CollectionChanged; 
     } 

     void ContentBlockList_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) 
     { 

     } 
    } 

, если вы поддерживаете свой код попробовать это

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 
     ContentBlockList pp = new ContentBlockList(); 
     pp.CollectionChanged += pp_CollectionChanged; 
     pp.CollectionChanged += pp_CollectionChanged1; 
     pp.Add(11112); 

     pp.SomeMethod(); 
    } 

    void pp_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) 
    { 

    } 

    void pp_CollectionChanged1(object sender, NotifyCollectionChangedEventArgs e) 
    { 

    } 


} 

public class ContentBlockList : ObservableCollection<int> 
{ 
    public void SomeMethod() 
    { 
     var handlers = CollectionChanged.GetInvocationList(); 

     foreach (NotifyCollectionChangedEventHandler handler in handlers) 
     { 
      CollectionChanged -= handler; 
     } 

     // do stuff here 

     foreach (NotifyCollectionChangedEventHandler handler in handlers) 
     { 
      CollectionChanged += handler; 
     } 
    } 

    public override event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged; 
} 
+0

События не срабатывают, если я не переопределяю 'OnCollectionChanged()'. Вне всякого сомнения, вопрос заключается не в том, могу ли я заставить это работать - я могу - это то, что я не должен делать. Благодарю. –

+0

Рекомендуется унаследовать ObservableCollection , если у вас есть определенная логика. – Coding4Fun

0

Насколько я понял, вам нужно прервать стрельбу CollectionChanged делать какую-то работу молча. Таким образом, вы можете создать логическое поле, как __FireCollectionChanged и затем переопределить OnCollectionChanged() сделать:

protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) 
{ 
    if (__FireCollectionChanged) 
     base.OnCollectionChanged(e); 
} 

Затем вы можете контролировать, является ли событие обстрелян, что логическое поле.

И, отвечая на фактический вопрос: вы не можете использовать список вызовов напрямую, потому что это событие не является полем типа делегата. Это всего лишь два метода add и remove для поведения подписки/отписки. Основное поле делегата создается за кулисами, и вы, как правило, не хотите его использовать.

Смежные вопросы