2012-07-23 78 views
1

我想實現一種方法,通過計算密集型方法將消息傳遞給UI,以便向用戶通知計算的狀態和進度。在執行此操作時,UI應該保持響應,即計算在另一個線程上執行。我已經閱讀了代表,背景工作者等,但是我發現他們非常混亂,並且無法在我的應用程序中實現它們。這裏有一個簡單的應用程序,它與我的應用程序具有相同的總體思路完成計算密集型的方法後,在UI的文本框在這裏更新:從對象更新UI文本框

<Window x:Class="UpdateTxtBox.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="UpdateTxtBox" Height="350" Width="525"> 
<Grid> 
    <Grid.ColumnDefinitions> 
     <ColumnDefinition Width="1*" /> 
     <ColumnDefinition Width="1*" /> 
    </Grid.ColumnDefinitions> 
    <Button Content="Start" Height="23" HorizontalAlignment="Left" Margin="94,112,0,0" Name="btnStart" VerticalAlignment="Top" Width="75" Click="btnStart_Click" /> 
    <TextBox Grid.Column="1" HorizontalAlignment="Stretch" Margin="0,0,0,0" Name="txtBox" VerticalAlignment="Stretch" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible" /> 
</Grid> 

namespace UpdateTxtBox 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
     } 

     private void btnStart_Click(object sender, RoutedEventArgs e) 
     { 
      MyTextProducer txtProducer = new MyTextProducer(); 
      txtProducer.ProduceText(); 
      txtBox.Text = txtProducer.myText; 
     } 
    } 
} 

計算密集型類:

namespace UpdateTxtBox 
{ 
    public class MyTextProducer 
    { 
     public string myText { get; private set; } 

     public MyTextProducer() 
     { 
      myText = string.Empty; 
     } 

     public void ProduceText() 
     { 
      string txt; 

      for (int i = 0; i < 10; i++) 
      { 
       txt = string.Format("This is line number {0}", i.ToString()); 
       AddText(txt); 
       Thread.Sleep(1000); 
      } 
     } 

     private void AddText(string txt) 
     { 
      myText += txt + Environment.NewLine; 
     } 
    } 
} 

如何能修改此代碼,以便每次調用AddText方法時都會更新文本框?

回答

2

這裏的基本問題是,您正在UI線程上進行計算密集型操作,從而鎖定了用戶界面(就像您自己想象的那樣)。解決方案是啓動一個單獨的線程,然後從中更新UI。但是當時你面臨的問題是隻有UI線程被允許更新UI。這可以通過使用Dispatcher類來解決,該類爲您處理所有這些瑣事。

這是一個很好的,充實我們的調度文章,以及如何使用它:http://msdn.microsoft.com/en-us/magazine/cc163328.aspx

注意,還有其他的方法來處理這類UI與延遲/慢速任務更新,但我要說這是解決您的問題的充分方法。

+0

我試圖玩弄這個,但無法得到它的工作。如果您可以修改上面的代碼以便它可以使用Dispatcher類,它將會有很大的幫助。 – Elfendahl 2012-07-23 13:53:22

+0

嗨!我很遺憾聽到這個消息。我和Dispatcher(和那篇文章)有同樣的問題。解決方案是實際閱讀整篇文章,而不是僅僅瀏覽「魔法代碼」。如果您想要一個可以工作的剪切和粘貼解決方案,請參閱paul的答案。它由本書完成,並很好地融入到一個合適的WPF應用程序中。 – 2012-07-23 13:56:16

1

當您使用WPF,我會建議你使用數據綁定,這裏是一個示例實現你的代碼:

<Window x:Class="UpdateTxtBox.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="UpdateTxtBox" Height="350" Width="525"> 
    <Grid> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition Width="1*" /> 
      <ColumnDefinition Width="1*" /> 
     </Grid.ColumnDefinitions> 
     <Button Content="Start" Height="23" HorizontalAlignment="Left" Margin="94,112,0,0" Name="btnStart" VerticalAlignment="Top" Width="75" Click="btnStart_Click" /> 
     <TextBox Grid.Column="1" HorizontalAlignment="Stretch" Margin="0,0,0,0" Name="txtBox" VerticalAlignment="Stretch" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible" Text="{Binding Path=myText}" /> 
    </Grid> 
</Window> 

說明該文本框的內容屬性是現在數據綁定。

public partial class MainWindow : Window 
{ 
    MyTextProducer txtProducer; 

    public MainWindow() 
    { 
     InitializeComponent(); 

     txtProducer = new MyTextProducer(); 
     this.DataContext = txtProducer; 
    } 

    private void btnStart_Click(object sender, RoutedEventArgs e) 
    { 
     Task.Factory.StartNew(txtProducer.ProduceText); 
     txtBox.Text = txtProducer.myText; 
    } 
} 

注意this.DataContext = txtProducer行,你這是怎麼告訴結合到哪裏尋找值

public class MyTextProducer : INotifyPropertyChanged 
{ 
    private string _myText; 
    public string myText { get { return _myText; } set { _myText = value; RaisePropertyChanged("myText"); } } 

    public MyTextProducer() 
    { 
     myText = string.Empty; 
    } 

    public void ProduceText() 
    { 
     string txt; 

     for (int i = 0; i < 10; i++) 
     { 
      txt = string.Format("This is line number {0}", i.ToString()); 
      AddText(txt); 
      Thread.Sleep(1000); 
     } 
    } 

    private void AddText(string txt) 
    { 
     myText += txt + Environment.NewLine; 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    public void RaisePropertyChanged(string propName) 
    { 
     if (PropertyChanged != null) 
      PropertyChanged(this, new PropertyChangedEventArgs(propName)); 
    } 
} 

MyTextProducer現在實現INotifyPropertyChanged,所以對myText性質的任何改變都會自動在用戶界面中反映出來。

+0

這工作非常好!它非常簡單!我實際上已經嘗試過實施INotifyPropertyChanged,但是如果不凍結界面就無法使它工作。謝謝你的幫助! – Elfendahl 2012-07-23 13:57:31