2015-12-10 2 views
0

Я пытаюсь написать метод расширения, который упростит обработку событий в поперечном потоке. Ниже я понял, что, по моему мнению, это должно работать; Однако я получаю исключение перекрестных потоков, когда вызывается метод EndInvoke ...Почему я вижу исключение перекрестной резьбы при вызове EventHandler.BeginInvoke через Delegate.Target?

using System; 
using System.Linq; 
using System.Runtime.Remoting.Messaging; 
using System.Threading.Tasks; 
using System.Windows; 
using System.Windows.Media; 

namespace SCV { 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window { 

     private static event EventHandler _Test; 

     public static event EventHandler Test { 
      add { MainWindow._Test += value; } 
      remove{ MainWindow._Test -= value; } 
     } 

     private static async Task OnTest() { 
      if (MainWindow._Test != null) 
       await MainWindow._Test.ExecuteAsync(null, EventArgs.Empty); 
     } 

     private LinearGradientBrush brshSomeBrush = new LinearGradientBrush(Colors.Red, Colors.Black, new Point(0, 0), new Point(1, 1)); 

     public MainWindow() { 
      InitializeComponent(); 
      MainWindow.Test += (S, E) => this.Background = this.brshSomeBrush; 
       this.Loaded += async (S, E) => await MainWindow.OnTest(); 
      } 
    } 

    static class Extensions { 
     public static async Task ExecuteAsync(this EventHandler eH, object sender, EventArgs e) { 
      await Task.WhenAll(eH.GetInvocationList().Cast<EventHandler>().Select(evnt => Task.Run(() => { 
       System.Windows.Controls.Control wpfControl; 
       System.Windows.Forms.Control formControl; 
       Action begin = () => evnt.BeginInvoke(sender, e, IAR => ((IAR as AsyncResult).AsyncDelegate as EventHandler).EndInvoke(IAR), null); 
       if (evnt.Target is System.Windows.Controls.Control && !(wpfControl = evnt.Target as System.Windows.Controls.Control).Dispatcher.CheckAccess()) 
        wpfControl.Dispatcher.Invoke(begin); 
       else if (evnt.Target is System.Windows.Forms.Control && (formControl = evnt.Target as System.Windows.Forms.Control).InvokeRequired) 
        formControl.Invoke(begin); 
       else 
        begin(); 
      }))); 
     } 
    } 
} 

Что бы быть причиной для этого еще бросить исключение? Как я это делаю неправильно?

+0

Вы ошиблись, метод BeginInvoke() делегата всегда работает в потоке threadpool. Invoke() достаточно хорош. Вы также делаете жесткое предположение, что подписчик событий является элементом Winforms или WPF, основным обломком, если он окажется просто обычным классом. Вы обнаружите, что через два года это очень болезненно. Не делайте это слишком фантазией. –

+0

@ На самом деле, я не делаю этого предположения. Если вы посмотрите на If/Else If/Else, вы увидите, что первая проверяет, является ли это элементом управления WPF, второй проверяет, является ли это элементом управления WinForms, а третий предполагает, что он не является и движется вперед. Если это ни одно из них, то программа предполагает, что это простой объект класса ..., который будет проблемой, если я когда-либо буду развиваться во что-либо, что не является winforms или wpf (в этот момент мне просто нужно добавить дополнительные ссылки на if/then chain или выяснить лучшее решение (я НЕ хочу писать, если InvokeRequired в каждый метод, поэтому я делаю это так: – Will

+0

Правильный способ сделать это с помощью SynchronizationContext.Current. Множество Q + A об этом –

ответ

2

Вы звоните делегат на правой нити - но сам делегат затем вызывает evnt.BeginInvoke, который выполняет evnt делегата в пуле потоков ... так что вы до сих пор в конечном итоге выполнение реального, лежащий в основе делегата (в этом case _Test, установит цвет фона) в потоке, отличном от UI.

Вы уже подготовили нужный поток для выполнения делегата - так что просто выполните его с evnt(sender, e).

+0

Это сделало. – Will