2014-07-09 33 views
5

我最近將我的應用程序從WinForms轉換爲WPF,並且我對大部分新功能感到滿意。但是,我遇到了一個主要的絆腳石。當我將文本不斷添加到我的文本框中時,UI線程變得如此受阻,以至於我無法做任何事情,只能看它追加文本!我需要能夠切換選項卡在我的選項卡控制,單擊按鈕等等。奇怪的是,我絕對沒有在WinForms的UI線程放緩!因此,這裏是我的應用程序的一個小背景:它作爲「動作隊列」的一部分運行其他進程,並將這些進程的stdout和stderr分配到兩個單獨的文本框中,以及日誌文本框(這些是受影響的文本框)。在低輸出的過程中,沒有減速,但是當我使用像SVN checkout和文件複製這樣的進程時,我立即得到如此多的文本輸出,它所能做的就是追加文本。WPF附加文本塊UI線程嚴重,但WinForms不?

這裏是我的打印代碼:


public void PrintOutput(String s) 
{ 
    String text = s + Environment.NewLine; 
    Window.Dispatcher.Invoke(new StringArgDelegate(Window.PrintOutput), text); 
    Debug.Log("d " + text); 
} 

public void PrintLog(String s) 
{ 
    ClearLogButtonEnabled = true; 
    String text = s + Environment.NewLine; 
    Window.Dispatcher.Invoke(new StringArgDelegate(Window.PrintLog), text); 
} 

和匹配的代碼隱藏:


public void PrintOutput(String s) 
{ 
    outputTextBox.AppendText(s); 
    outputTextBox.ScrollToEnd(); 
    if (!clearOutputButton.IsEnabled) clearOutputButton.IsEnabled = true; 
} 

public void PrintLog(String s) 
{ 
    logTextBox.AppendText(s); 
    logTextBox.ScrollToEnd(); 
} 

只是讓我沒有得到一堆指責說,我做我的工作在UI線程以及,這裏是我的代碼啓動單獨的工作線程:


Thread actionThread = new Thread(new ThreadStart(ActionManager.Instance().ExecuteActions)); 
actionThread.Name = "Action Manager Work Thread"; 
actionThread.Start(); 

這是處理啓動,運行和清理所有輔助進程的線程。這些進程使用上面顯示的打印方法來打印它們的stdout/stderr輸出。另外,每個進程都有自己的線程!


Thread procThread = new Thread(new ThreadStart(StartProcess)); 
procThread.Name = action.Name + "_" + Guid.NewGuid(); 
procThread.Start(); 

我擔心的是WPF在調用時比較慢,而且我被搞砸了。我花了很多工作將這個應用程序從WinForms切換到WPF,所以如果有人知道我爲什麼會在打印速度上出現如此巨大的放緩,請讓我知道!

編輯:

我還要補充一點,我使用的是RichTextBox,而不是一個TextBox,和我需要的RichTextBox的大膽某些文本的功能。如果有一種方法可以提供粗體TextBox類的粗體文本,請讓我知道。

+0

您是否嘗試過使用WPF性能套件http://msdn.microsoft.com/en-us/library/aa969767.aspx – 3dd

+0

也可能重複http://stackoverflow.com/questions/2465181/ is-this-slow-wpf-textblock-performance-expected – 3dd

+0

@ 3dd我在第二個鏈接上閱讀,這很有幫助。但是,對我來說,在進程輸出中獲得實時更新而不影響UI線程(這可以通過WinForms進行)是非常重要的。鏈接的答案和我的後續測試讓我相信WPF在這方面比WinForms慢很多,這非常不幸。我可能不得不考慮製作一個性能文本框類... – Darkhydro

回答

5

WPF RichTextBox是一個非常重量級的UI元素,因爲它不僅允許以WPF Document的形式提供豐富的內容,還具有編輯功能。

你真正需要在這種情況下是FlowDocumentScrollViewer

這是我的Log Viewer Sample的一個小改編,它使用FlowDocumentScrollViewer而不是ItemsControl。其優點是,這個UI元素允許文字選擇和複製,同時保留了豐富的文本功能,你需要:背後

<Window x:Class="WpfApplication1.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 
    <FlowDocumentScrollViewer Document="{Binding}"/> 
</Window> 

代碼:

public partial class MainWindow : Window 
    { 
     private System.Random random; 
     private string TestData = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum"; 
     private List<string> words; 
     private int maxword; 
     private int index; 

     private FlowDocument doc; 
     private Paragraph paragraph; 

     public MainWindow() 
     { 
      InitializeComponent(); 

      DataContext = doc = new FlowDocument(); 

      doc.Blocks.Add(paragraph = new Paragraph()); 

      Task.Factory.StartNew(AddDataLoop); 
     } 

     private void AddDataLoop() 
     { 
      random = new Random(); 
      words = TestData.Split(' ').ToList(); 
      maxword = words.Count - 1; 

      while (true) 
      { 
       Thread.Sleep(10); 
       Dispatcher.BeginInvoke((Action) (AddRandomEntry)); 
      } 
     } 

     private void AddRandomEntry() 
     { 
      var run = new Run(string.Join(" ", Enumerable.Range(5, random.Next(10, 50)) 
                 .Select(x => words[random.Next(0, maxword)]))); 

      var isBold = random.Next(1, 10) > 5; 

      if (isBold) 
       paragraph.Inlines.Add(new Bold(run)); 
      else 
       paragraph.Inlines.Add(run); 

      paragraph.Inlines.Add(new LineBreak()); 
     } 
    } 

結果:

enter image description here

  • 再一次證明這絕對是nothing你可以在WPF中無法實現的winforms中實現,而顯然不能這麼說。這使winforms實際上已經過時的技術,字面上replaced由更新的,更有能力的技術。

  • 請注意,我在每個新條目之間放置了一個10毫秒延遲。這實際上是實時的,用戶界面不會顯示任何放緩或閃爍或任何類型的質量下降。

  • 和我的另一個例子一樣,請注意,背後的大部分代碼是生成隨機文本的字面樣板,唯一相關的代碼行是paragraph.Inlines.Add(...)

  • WPF Rocks。 - 只需將我的代碼複製並粘貼到File -> New Project -> WPF Application中,然後查看結果。

  • 讓我知道你是否需要進一步的幫助。

+1

該解決方案工作得很好。性能甚至比使用帶一堆TextBlocks的ItemsControl更好,我可以粗體顯示我需要的文本以及複製粘貼。我遇到的唯一絆腳石是讓自動滾動工作。我現在還在處理這個問題,所以如果你有建議讓我知道。我用ScrollViewer將它自動滾動(因爲我似乎無法訪問FlowDocumentScrollViewer的ScrollViewer,它有0個孩子?!),但是這會禁用鼠標滾動。我最終可能會做一些事件詭計。 – Darkhydro

+0

我解決了這個問題,方法是處理孩子中的事件,並在父代中手動提高事件。有點煩人,我似乎無法訪問FlowDocumentScrollViewer的ScrollViewer,但這是有效的。 – Darkhydro

+0

@Darkhydro看到[這個答案](http://stackoverflow.com/a/10279201/643085)它使用'VisualTreeHelper'來獲取任何給定的UI元素的可視化的孩子。另請參閱[MSDN](http://msdn.microsoft.com/zh-cn/library/ms753391(v = vs.110).aspx)以瞭解Visual Tree和Logical Trees在WPF中的工作原理。 –