2009-12-08 57 views
2

我正在構建一個WPF應用程序,該應用程序調用Web服務並顯示在由我的應用程序分解和分析後從服務返回的數據。我面臨的問題是多線程。其中一個API調用是每60秒使用一次DispatcherTimer。問題是,當這個事件觸發時,它會阻塞UI線程。我嘗試過(以我能想到的所有方式)使用BackgroundWorker和Dispatcher對象(也是委託)從後臺線程更新UI,我無法弄清楚。我需要一個示例來顯示後臺線程正在更新的UI線程上的標籤。任何與此有關的幫助將是太棒了,因爲我正要嚇壞了:)。WPF多線程

我看了其他文章,它只是沒有讓我感覺可怕的數量。請耐心等待,因爲我對此很新穎。這是我想要做的一個例子。我在名爲lblCase的窗口上有一個標籤。我每60秒調用一次pullData(),我想用返回的數據更新lblCase而不會阻塞UI。

private void pullData() 
{ 
    //API call goes here... 
    lblCase.Content = iCase; 
} 

public MainWindow() 
{ 
    InitializeComponent(); 
    DispatcherTimer timer = new DispatcherTimer(); 
    timer.Tick += new EventHandler(timer_Tick); 
    timer.Interval = new TimeSpan(0,0,60); 
    timer.Start(); 
} 

private void timer_Tick(object sender, EventArgs e) 
{ 
    pullData(); 
} 

回答

4

看一看this問題...

歡呼聲,

編輯:

喬 - 不知道,如果你得到任何接近groking這一點,所以我想我試圖將BackgroundWorker的一個簡單的用法展示出來,以演示這個類是多麼的簡單和強大!

第一 - 在你的構造......

public MainWindow() 
{ 
    InitializeComponent(); 

    BackgroundWork worker = new BackgroundWorker(); 
    worker = new BackgroundWorker(); 
    worker.DoWork += new DoWorkEventHandler(worker_DoWork); 
    worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); 

    System.Timers.Timer t = new System.Timers.Timer(10000); // 10 second intervals 
    t.Elapsed += (sender, e) => 
    { 
     // Don't try to start the work if it's still busy with the previous run... 
     if (!worker.IsBusy) 
      worker.RunWorkerAsync(); }; 
    } 
} 

所以我們設立了一些委派在後臺線程一些工作(在方法「worker_DoWork」)......在什麼方法將happends不會影響到UI線程,它應該是這個樣子:

private void worker_DoWork(object sender, DoWorkEventArgs e) 
{ 
    // Whatever comes back from the lengthy process, we can put into e.Result 
    e.Result = DoMyBigOperation(); 
} 

這個線程完成現在時,將觸發RunWorkerCompleted事件,這是我們爲這樣的處理:

private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    // First, handle the case where an exception was thrown. 
    if (e.Error != null) 
    { 
    // handle the System.Exception 
     MessageBox.Show(e.Error.Message); 
    } 
    else if (e.Cancelled) 
    { 
     // now handle the case where the operation was cancelled... 
     lblCase.Content = "The operation was cancelled"; 
    } 
    else 
    { 
     // Finally, handle the case where the operation succeeded 
     lblCase.Content = e.Result.ToString(); 
    } 
} 

希望這會有所幫助! IanR

+0

(我的)另一樣品,其確實很多類似的話: http://stackoverflow.com/questions/ 1794402 /異步等待,而-C-功能被執行的/ 1794947#1794947 伊恩的代碼是好一點,雖然;-)唯一的評論我會做的是,在匿名委託使用可變捕捉可以是一個當你第一眼看到它的時候,心靈就不會彎曲爲了讓代碼更加明顯一些,我會讓「worker」成爲MainWindow類的成員。 – donovan

+0

好點,多諾萬和感謝您的親切話:) – kiwipom

+0

感謝您的答覆,先生們。我將很快開始測試這些建議,並會讓你知道什麼對我有用。 – Joe

1

您只能從創建控件的線程更新控件。 話雖如此,只是使用後臺工作對象來獲取數據,並且一旦該作業完成,使用UI線程更新控件。

+0

更新了我的問題... – Joe

0

使用BeginInvoke()從另一個線程更新UI線程。這是一個article描述如何使用它。

編輯:我試着根據你在你的問題給出的代碼下面的程序,它更新了UI罰款(因爲一切都發生在UI線程上)。

using System; 
using System.Threading; 
using System.Windows.Threading; 

namespace BeginInvoke 
{ 
    public partial class Window1 
    { 
     public Window1() 
     { 
      InitializeComponent(); 
      x_tid.Text = Thread.CurrentThread.ManagedThreadId.ToString(); 

      DispatcherTimer timer = new DispatcherTimer(); 
      timer.Tick += timer_Tick; 
      timer.Interval = new TimeSpan(0, 0, 1); 
      timer.Start(); 
     } 

     private void timer_Tick(object sender, EventArgs e) 
     { 
      Thread.SpinWait(900); 
      x_text.Text = DateTime.Now.ToString(); 
      x_tid.Text = Thread.CurrentThread.ManagedThreadId.ToString(); 
     } 
    } 
} 

的.xaml是:

<Window x:Class="BeginInvoke.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="Window1" Height="300" Width="300"> 
    <StackPanel> 
     <TextBlock x:Name="x_text" /> 
     <TextBlock x:Name="x_tid" /> 
    </StackPanel> 
</Window> 
+0

我讀過這篇文章,並且對於整個事情的工作原理還很不清楚。您是否介意發佈一個示例以與我發佈的代碼片段一起使用? – Joe

0

要闡述我關於使用BeginInvoke的更新從非UI線程的UI原來的答案,這裏的工作的例子。 BeginInvoke使用調度程序。如果您使用Silverlight,則可以使用System.Windows.Deployment.Current.Dispatcher.BeginInvoke()中的BeginInvoke。

using System; 
using System.Threading; 
using System.Windows.Threading; 

namespace BeginInvoke2 
{ 
    public partial class Window1 
    { 
     public Window1() 
     { 
      InitializeComponent(); 
      ThreadPool.QueueUserWorkItem(Proc, Dispatcher); 
     } 

     private void Proc(object state) 
     { 
      var disp = (Dispatcher) state; 
      var tid = Thread.CurrentThread.ManagedThreadId.ToString(); 

      // Use BeginInvoke to do the operations on the UI thread 
      disp.BeginInvoke((Action)(() => 
      { 
       x_tid1.Text = "threadpool thread: " + Thread.CurrentThread.ManagedThreadId.ToString(); 
       x_tid2.Text = "  ui thread: " + tid; 
      })); 

      // Can't do the following operations because we are not in the UI 
      // thread 
      //x_text.Text = "In Proc"; 
      //x_tid.Text = Thread.CurrentThread.ManagedThreadId.ToString(); 
     } 
    } 
} 

.XAML文件

<Window x:Class="BeginInvoke2.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="Window1" Height="300" Width="300"> 
    <StackPanel> 
     <TextBlock x:Name="x_tid1" /> 
     <TextBlock x:Name="x_tid2" /> 
    </StackPanel> 
</Window> 
1

我希望創建一個工人階層,通過它的更新委託,並在一個單獨的線程啓動工人的DoSomething的方法。無論何時該線程需要更新UI,它都會調用更新委託,並通過控件的調度程序進行更新。在這個例子中,我還通過了控制,因爲我有多個線程的每個更新自己的文本塊:

private void UpdateTextBlock(TextBlock textBlockArg, string textArg) 
    { 
     textBlockArg.Dispatcher.Invoke(
      System.Windows.Threading.DispatcherPriority.Normal 
      , new System.Windows.Threading.DispatcherOperationCallback(delegate 
      { 
       textBlockArg.Text = textArg; 
       return null; 
      }), null); 
    }