2010-05-04 16 views
10

Ho un metodo che crea thread in background per fare qualche azione. In questo thread in background creo l'oggetto. Ma questo oggetto durante la creazione in runtime mi dà un'eccezione:Come posso creare i controlli WPF in un thread in background?

Il thread chiamante deve essere STA, perché molti componenti dell'interfaccia utente richiedono questo.

So che devo usare Dispatcher per far riflettere qualcosa all'interfaccia utente. Ma in questo caso creo solo un oggetto e non lo faccio con l'interfaccia utente. Questo è il mio codice:

public void SomeMethod() 
     { 
     BackgroundWorker worker = new BackgroundWorker(); 
     worker.DoWork += new DoWorkEventHandler(Background_Method); 
     worker.RunWorkerAsync(); 
     } 

    void Background_Method(object sender, DoWorkEventArgs e) 
     { 
     TreeView tv = new TreeView(); 
     } 

Come posso creare oggetti nel thread in background?

Ho usare l'applicazione WPF

+0

Un'altra domanda: è possibile che il metodo Background Worker restituisca un valore di tipo specifico? – Polaris

+2

controlla la proprietà e.Result nel metodo RunWorkerCompleted. http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx. – Amsakanna

risposta

6

TreeView è un controllo dell'interfaccia utente. Puoi solo creare e manipolare i controlli dell'interfaccia utente in un thread dell'interfaccia utente, quindi ciò che stai cercando di fare non è possibile.

Quello che vuoi fare è fare tutto il lavoro che richiede tempo sul thread in background e quindi "richiamare" sul thread dell'interfaccia utente per manipolare l'interfaccia utente. Questo è in realtà abbastanza semplice:

void Background_Method(object sender, DoWorkEventArgs e) 
{ 
    // ... time consuming stuff... 

    // call back to the window to do the UI-manipulation 
    this.BeginInvoke(new MethodInvoker(delegate { 
     TreeView tv = new TreeView(); 
     // etc, manipulate 
    })); 
} 

forse ho avuto la sintassi sbagliata per BeginInvoke (è fuori dalla parte superiore della mia testa), ma ci si va comunque ...

+0

Ottengo alcuni dati dal servizio web e il runtime impiega molto tempo per questo. Questo è il motivo per cui voglio ottenere i dati in background e generare il mio treeView quando i dati saranno pronti. – Polaris

+0

Ho aggiornato la mia risposta con altri commenti su come è possibile eseguire operazioni nel thread dell'interfaccia utente da un thread di lavoro. –

0

Per rendere il codice semplicemente lavorare , è necessario unire un appartamento STA COM chiamando Thread.SetApartmentState(ApartmentState.STA). Poiché BackgroundWorker utilizza probabilmente un pool di thread condiviso, l'aggiunta a un determinato apartment potrebbe interessare altri utenti di questo pool di thread o potrebbe anche non riuscire se è già stato impostato ad es. MTA prima. Anche se tutto ha funzionato, il tuo nuovo TreeView verrà bloccato su questo thread di lavoro. Non sarebbe possibile utilizzarlo nel thread principale dell'interfaccia utente.

Se hai spiegato un po 'più in dettaglio le tue vere intenzioni, avresti sicuramente un aiuto migliore.

0

Prova seguente codice:

public void SomeMethod() 
{ 

System.ComponentModel.BackgroundWorker myWorker = new System.ComponentModel.BackgroundWorker(); 

myWorker.DoWork += myWorker_DoWork; 

myWorker.RunWorkerAsync(); 

} 

private void myWorker_DoWork(object sender, 
    System.ComponentModel.DoWorkEventArgs e) 
{ 
    // Do time-consuming work here 
} 
3

HTH:

void Background_Method(object sender, DoWorkEventArgs e) 
    { 
     // Time Consuming operations without using UI elements 
     // Result of timeconsuming operations 
     var result = new object(); 
     App.Current.Dispatcher.Invoke(new Action<object>((res) => 
      { 
       // Working with UI 
       TreeView tv = new TreeView(); 
      }), result); 
    } 
0
void Background_Method(object sender, DoWorkEventArgs e) 
{ 
    TreeView tv = new TreeView(); 
    // Generate your TreeView here 
    UIDispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => 
    { 
     someContainer.Children.Add(tv); 
    }; 
} 
0

ho risolto il mio problema. Ho appena usato la proprietà e.Result del metodo RunWorkerCompleted. Ottengo i dati in background thread e quindi utilizzare questi dati quando thread completato. Ringrazia ogni corpo per i metodi utili. Un ringraziamento speciale a Veer per dare una raccomandazione sulla proprietà e.Result.

+0

Se si desidera aggiornare l'interfaccia utente a intervalli regolari, è possibile utilizzare il parametro UserState del metodo ReportProgress per inviare i dati e utilizzarli nel metodo ProgressChanged mediante la trasmissione di e.UserState al tipo richiesto. – Amsakanna

0

vedere la risposta su questa questione: How to run something in the STA thread?

Quando si definisce il tuo thread, impostare l'ApartmentState a STA:

thread.SetApartmentState(ApartmentState.STA); 

Questo dovrebbe fare il trucco!

0

Nessuno sta discutendo il caso di un thread STA separato nei dettagli (anche se il concetto è esattamente lo stesso).

Quindi immaginiamo un semplice controllo della linguetta aggiunto in uno scatto del tasto

private void button_Click(object sender, RoutedEventArgs e) 
    { 
     TabItem newTab = new TabItem() { Header = "New Tab" }; 
     tabMain.Items.Add(newTab); 
    } 

Se ci spostiamo in un altro thread STA

private void button_Click(object sender, RoutedEventArgs e) 
    { 
     Thread newThread = new Thread(new ThreadStart(ThreadStartingPoint)); 
     newThread.SetApartmentState(ApartmentState.STA); 
     newThread.IsBackground = true; 
     newThread.Start(); 
    } 
    private void ThreadStartingPoint() 
    { 
     TabItem newTab = new TabItem() { Header = "New Tab" }; 
     tabMain.Items.Add(newTab); 
    } 

ovviamente abbiamo un System.InvalidOperationException

Ora, cosa succede se aggiungiamo il controllo

private void AddToParent(string header) 
    { 
     TabItem newTab = new TabItem() { Header = header }; 
     tabMain.Items.Add(newTab); 
    } 

utilizzando un metodo delegato?

public void DelegateMethod(string header) 
    { 
     tabMain.Dispatcher.BeginInvoke(
       new Action(() => { 
        this.AddToParent(header); 
       }), null); 
    } 

funziona se lo si chiama

private void button_Click(object sender, RoutedEventArgs e) 
    { 
     Thread newThread = new Thread(new ThreadStart(ThreadStartingPoint)); 
     newThread.SetApartmentState(ApartmentState.STA); 
     newThread.IsBackground = true; 
     newThread.Start(); 
    } 
    private void ThreadStartingPoint() 
    { 
     DelegateMethod("new tab"); 
    } 

perché ovviamente ora manteniamo l'albero visuale nello stesso thread originale.

Problemi correlati