2012-12-04 15 views
10

Mam niestandardową klasę kolejki dziedziczoną z klasy Queue. Ma wydarzenie ItemAdded. W obsłudze zdarzenia tego zdarzenia wykonuję metodę. Ale działa inaczej niż główny wątek, ale chcę go w głównym wątku. Nie wiem, jak to zrobić. Jakieś sugestie ?Wykonywanie metody w wątku głównym z obsługi zdarzeń

//My custom class 


using System; 
using System.Collections; //Required to inherit non-generic Queue class. 

namespace QueueWithEvent 
{ 
    public class SmartQueue:Queue 
    { 

     public delegate void ItemAddedEventHandler(object sender, EventArgs e); 

     public event ItemAddedEventHandler ItemAdded; 

     protected virtual void OnItemAdded(EventArgs e) 
     { 
      if (ItemAdded != null) 
      { 
       ItemAdded(this, e); 
      } 
    } 

    public override void Enqueue(object Item) 
    { 
     base.Enqueue(Item); 
     OnItemAdded(EventArgs.Empty); 
    }   

    } 
} 



//Winform application 

using System; 
using System.ComponentModel; 
using System.Windows.Forms; 
using QueueWithEvent; 

namespace TestApp 
{ 
    public partial class Form1 : Form 
    { 

    SmartQueue qTest = new SmartQueue(); 

    public Form1() 
    { 
     InitializeComponent(); 
     qTest.ItemAdded += new SmartQueue.ItemAddedEventHandler(this.QChanged); 
    } 

    private void btnStartBgw_Click(object sender, EventArgs e) 
    { 
     DisplayThreadName(); 
     bgwTest.RunWorkerAsync(); 
    } 

    private void bgwTest_DoWork(object sender, DoWorkEventArgs e) 
    { 
     try 
     { 
      for (int i = 0; i < 11; i++) 
      { 
       string valueTExt = i.ToString(); 
       qTest.Enqueue(valueTExt); 
       System.Threading.Thread.Sleep(5000); 
      } 
     } 
     catch (Exception Ex) 
     { 
      MessageBox.Show(Ex.Message); 

     } 
    } 


    private void DisplayThreadName() 
    { 
     string tName = System.Threading.Thread.CurrentThread.ManagedThreadId.ToString(); 
     txtThreadName.Text = tName;   
    } 

    private void QChanged(object sender, EventArgs e) 
    { 
     //#########I want this method to run on main thread ############# 
     DisplayThreadName(); 
    } 
} 
} 

Z góry dziękuję. SKPaul.

+1

Windows Forms lub WPF? – Tigran

Odpowiedz

19

Wpisujesz elementy do wątku w tle (obsługa zdarzeń DoWork działa w wątku w tle), dzięki czemu Twoje wydarzenie jest również wątkiem tła.

Użyj metody InvokeRequired, aby sprawdzić, czy jesteś w wątku UI. A jeśli nie, to użyj Invoke do uruchomienia kodu w wątku UI:

private void QChanged(object sender, EventArgs e) 
{ 
    if (InvokeRequired) 
    { 
     Invoke((MethodInvoker)delegate { QChanged(sender, e); }); 
     return; 
    } 
    // this code will run on main (UI) thread 
    DisplayThreadName(); 
} 

Inną opcją dla ciebie - użyj ProgressChanged zdarzenie enqueue przedmiotów (nie zapomnij ustawić WorkerReportsProgress true). Ta procedura obsługi zdarzenia działa na wątku UI:

private void bgwTest_DoWork(object sender, DoWorkEventArgs e) 
{ 
    BackgroundWorker worker = (BackgroundWorker)sender; 

    for (int i = 0; i < 11; i++) 
    { 
     // use user state for passing data 
     // which is not reflecting progress percentage 
     worker.ReportProgress(0, i); 
     System.Threading.Thread.Sleep(5000); 
    } 
} 

private void bgwTest_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    string valueTExt = e.UserState.ToString(); 
    qTest.Enqueue(valueTExt); 
} 
+1

Dzięki lazyberezovsky, teraz działa. Wielkie dzięki. –

+7

Co zrobić, jeśli główny wątek nie ma formularza? Na przykład klasa ApplicationContext dla aplikacji typu tray. Następnie Wywołanie jest niedostępne. – xr280xr

1

Można wykorzystać takie samo podejście jak BackgroundWorker, że jest włączenie AsyncOperation jako członek w swojej klasie, który może wywoływać operacje wątku został utworzony w

protected AsyncOperation AsyncOp; 

Utwórz instancję w swoim konstruktorze z argumentem "null". To tworzy operację asynchroniczną "związaną" z bieżącym wątkiem.

public SmartQueue() 
{ 
    AsyncOp = AsyncOperationManager.CreateOperation(null); 
} 

Następnie możesz użyć AsyncOp do Post swojego wydarzenia.

public override void Enqueue(object Item) 
{ 
    base.Enqueue(Item); 
    AsyncOp.Post(OnItemAdded, EventArgs.Empty); 
} 

To będzie wykonywał OnItemAdded teleskopowe (subskrybentów) na tym samym wątku, który stworzył instancję SmartQueue.

+0

OP nie korzysta z WPF - spójrz na przykład kodu. –

-1

Spróbuj z tego kodu w głównym wątku:

SmartQueue smartQueue = new SmartQueue(); 

public Form1() 
{ 
    InitializeComponent(); 
    smartQueue.ItemAdded += new SmartQueue.ItemAddedEventHandler(smartQueue_ItemAdded); 
} 

void smartQueue_ItemAdded(object sender, EventArgs e) 
{ 
    // add your code in here 
} 
+0

To jest prosta subskrypcja zdarzeń, już wykonana przez OP. Jak rozwiązuje problem wielowątkowości? –

+0

W przypadku wielowątkowości należy przypisać delegata do zmiennej tymczasowej przed testowaniem i wywoływaniem w celu zabezpieczenia wątku. Może tak: 'chroniona wirtualna pustka OnItemAdded (EventArgs e) {var temp = ItemAdded; if (temp! = null) {temp (this, e);}} ' – YD4

+0

W jaki sposób sprawia, że ​​program obsługi zdarzeń działa na głównym wątku? –

Powiązane problemy