In a previous question, I asked how to get real-time logging output in a WPF textbox-like element (WPF append text blocks UI thread heavily but WinForms doesn't?). The a
I execute the sample you've provided, apart from few threading issues the biggest issue is amount of data. which slows down the text rendering as the it grows.
I tried to rewrite your code in a different way. I used Tasks, BlockingCollection and Virtualization to improve the performance with assumptions that main interest of the application is logging speed.
Bridge.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Documents;
using System.Threading;
using System.Collections.Concurrent;
using System.Threading.Tasks;
namespace PerformanceTest
{
public class Bridge
{
int counterLimit;
public BlockingCollection<string> output;
public BlockingCollection<string> impOutput;
public BlockingCollection<string> errors;
public BlockingCollection<string> impErrors;
public BlockingCollection<string> logs;
protected static Bridge controller = new Bridge();
public static Bridge Controller
{
get
{
return controller;
}
}
public MainWindow Window
{
set
{
if (value != null)
{
output = value.outputProducer;
impOutput = value.impOutputProducer;
errors = value.errorProducer;
impErrors = value.impErrorProducer;
logs = value.logsProducer;
}
}
}
public bool Running
{
get;
set;
}
private Bridge()
{
//20000 lines seems to slow down tabbing enough to prove my point.
//increase this number to get even worse results.
counterLimit = 40000;
}
public void PrintLotsOfText()
{
Task.Run(() => GenerateOutput());
Task.Run(() => GenerateError());
}
private void GenerateOutput()
{
//There is tons of output text, so print super fast if possible.
int counter = 1;
while (Running && counter < counterLimit)
{
PrintOutput("I will never say this horrible word again as long I live. This is confession #" + counter++ + ".");
//Task.Delay(1).Wait();
}
}
private void GenerateError()
{
int counter = 1;
while (Running && counter < counterLimit)
{
PrintError("I will never forgive your errors as long I live. This is confession #" + counter++ + ".");
//Task.Delay(1).Wait();
}
}
#region Printing
delegate void StringArgDelegate(String s);
delegate void InlineArgDelegate(Inline inline);
public void PrintOutput(String s)
{
output.TryAdd(s);
PrintLog("d " + s);
}
public void PrintImportantOutput(String s)
{
impOutput.TryAdd(s);
PrintLog("D " + s);
}
public void PrintError(String s)
{
errors.TryAdd(s);
PrintLog("e " + s);
}
public void PrintImportantError(String s)
{
impErrors.TryAdd(s);
PrintLog("E " + s);
}
public void PrintLog(String s)
{
String text = s;
logs.TryAdd(text);
}
#endregion
}
}
MainWindow.xaml
<Window x:Class="PerformanceTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
WindowStartupLocation="CenterScreen">
<Grid>
<TabControl>
<TabControl.Resources>
<Style TargetType="ListBox">
<Setter Property="TextElement.FontFamily"
Value="Consolas" />
<Setter Property="TextElement.FontSize"
Value="12" />
<Setter Property="VirtualizingPanel.IsVirtualizing"
Value="True" />
<Setter Property="VirtualizingPanel.VirtualizationMode"
Value="Recycling" />
</Style>
</TabControl.Resources>
<TabItem Header="Bridge">
<StackPanel Orientation="Vertical"
HorizontalAlignment="Left">
<Button Content="Start Test"
Click="StartButton_Click" />
<Button Content="End Test"
Click="EndButton_Click" />
</StackPanel>
</TabItem>
<TabItem Header="Output">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListBox Grid.Column="0"
ItemsSource="{Binding Output,RelativeSource={RelativeSource FindAncestor,AncestorType=Window}}" />
<GridSplitter Grid.Column="1"
Width="5"
ResizeBehavior="PreviousAndNext" />
<ListBox Grid.Column="2"
ItemsSource="{Binding Errors,RelativeSource={RelativeSource FindAncestor,AncestorType=Window}}" />
</Grid>
</TabItem>
<TabItem Header="Log">
<Grid>
<ListBox Grid.Column="0"
ItemsSource="{Binding Logs,RelativeSource={RelativeSource FindAncestor,AncestorType=Window}}" />
</Grid>
</TabItem>
</TabControl>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
namespace PerformanceTest
{
///
/// Interaction logic for MainWindow.xaml
///
public partial class MainWindow : Window
{
public BlockingCollection<string> outputProducer = new BlockingCollection<string>();
public BlockingCollection<string> impOutputProducer = new BlockingCollection<string>();
public BlockingCollection<string> errorProducer = new BlockingCollection<string>();
public BlockingCollection<string> impErrorProducer = new BlockingCollection<string>();
public BlockingCollection<string> logsProducer = new BlockingCollection<string>();
public ObservableCollection<object> Output { get; set; }
public ObservableCollection<object> Errors { get; set; }
public ObservableCollection<object> Logs { get; set; }
Dispatcher dispatcher;
public MainWindow()
{
Bridge.Controller.Window = this;
try
{
InitializeComponent();
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException.ToString());
Console.WriteLine(ex.StackTrace);
}
dispatcher = Dispatcher;
Output = new ObservableCollection<object>();
Errors = new ObservableCollection<object>();
Logs = new ObservableCollection<object>();
Task.Run(() => Print(outputProducer, Output));
Task.Run(() => Print(errorProducer, Errors));
Task.Run(() => Print(logsProducer, Logs));
}
public void Print(BlockingCollection<string> producer, ObservableCollection<object> target)
{
try
{
foreach (var str in producer.GetConsumingEnumerable())
{
dispatcher.Invoke(() =>
{
target.Insert(0, str);
}, DispatcherPriority.Background);
}
}
catch (TaskCanceledException)
{
//window closing before print finish
}
}
private void StartButton_Click(object sender, RoutedEventArgs e)
{
if (!Bridge.Controller.Running)
{
Bridge.Controller.Running = true;
Bridge.Controller.PrintLotsOfText();
}
}
private void EndButton_Click(object sender, RoutedEventArgs e)
{
Bridge.Controller.Running = false;
}
}
}
For a full working sample download PerformanceTest.zip and see if this is close to what you need. I have only rewritten only some portion. If this example is heading to the desired direction, we can implement rest of the functionality. un-comment Task.Delay(1).Wait();
in Bridge.cs if you may want to slow down the production to see the mixed logs otherwise the logs generation is too fast so it appear one after other in logs tab.
Using FlowDocumentPageViewer will help the performance since it loads document asynchronously.