我正在修改我們在團隊中用作構建工具的較早的WPF應用程序。一個組件是一個LoggingWindow類,它包含一個FlowDocument,我們將構建日誌文本傳遞給MSBuild ILogger。在構建完成時,文檔通常最終會超過1000頁,隨着構建的進行接收超過10萬行日誌文本。由於文本傳遞量很大,因此表現不佳。這是現在我們如何處理這個問題:將字符串集合綁定到FlowDocument
這是LoggingWindow類的XAML:
<FlowDocumentPageViewer Name="LogPageViewer" Width="Auto" Height="Auto">
<FlowDocument Name="LogDocument" ColumnWidth="800" Foreground="LightGray" Background="Black" FontSize="12" FontFamily="Consolas" TextAlignment="Left">
<FlowDocument.Resources>
<Style TargetType="{x:Type Paragraph}">
<Setter Property="Margin" Value="0"></Setter>
</Style>
</FlowDocument.Resources>
</FlowDocument>
</FlowDocumentPageViewer>
我們旋轉了一個任務輪詢消息ConcurrentQueue。正如你所看到的,我不知道如何向文檔中添加消息,所以如果有消息,我會分批抓取消息並休眠10毫秒,如果沒有消息,則休眠100毫秒不要太多地阻塞主線程。因爲這是擁有該FlowDocument的線程
public void Start()
{
_cts = new CancellationTokenSource();
_uiProcessor = Task.Factory.StartNew(() => UpdateUi(_cts.Token));
}
private void UpdateUi(CancellationToken context)
{
while (!context.IsCancellationRequested)
{
if (_messageQueue.Count > 0)
{
var count = Math.Min(_messageQueue.Count, 10);
for (var i = 0; i < count; i++)
{
LogMessage message;
_messageQueue.TryDequeue(out message);
AddText(message);
}
// there are likely to be more messages, so only sleep for 10 ms
Thread.Sleep(10);
}
else
{
// there aren't likely to be more messages yet, so we can sleep for 100 ms
Thread.Sleep(100);
}
}
}
的AddText方法必須在主線程上運行,因此我們需要增加一款之前檢查調度。
private void AddText(LogMessage message)
{
if (Dispatcher.CheckAccess())
{
try
{
var timestampText = $"{message.Timestamp.ToString("MM/dd/yyyy HH:mm:ss.fff")}:{new string(' ', message.Indent * 2)}";
var span = new Span
{
FontFamily = new FontFamily("Consolas"),
FontStyle = FontStyles.Normal,
FontWeight = FontWeights.Normal,
FontStretch = FontStretches.Normal,
FontSize = 12,
Foreground = new SolidColorBrush(Color.FromArgb(0xff, 0xd3, 0xd3, 0xd3))
};
span.Inlines.Add(new Run(timestampText) { Foreground = new SolidColorBrush(Colors.White) });
span.Inlines.Add(new Run(message.Message) { Foreground = new SolidColorBrush(message.Color), FontWeight = message.Weight });
var paragraph = new Paragraph(span);
LogDocument.Blocks.Add(paragraph);
if (AutoScrollMenuItem.IsChecked)
{
LogPageViewer.LastPage();
}
}
catch (Exception ex)
{
_errorIndex++;
using (var fs = File.OpenWrite($"FormatError-{_errorIndex:00}.txt"))
{
var sw = new StreamWriter(fs)
{
AutoFlush = true
};
sw.WriteLine("Error: ");
sw.WriteLine(ex);
sw.WriteLine();
sw.Write(message.Message);
fs.Close();
}
}
}
else
{
Dispatcher.Invoke(new Action<LogMessage>(AddText), message);
}
}
我想重構這個的解決方案,使利用WPF數據綁定,這樣我可以添加的LogMessage到一個ObservableCollection並推遲實際UI更新WPF,這可能會處理它優於我可以手動。不過,我是WPF新手,甚至更新綁定,所以我不太確定我會如何去做這件事。另外,如果任何人有什麼更好的建議,以如何執行我想要做的事情,這將是偉大的。我的目標是能夠儘可能地跟上日誌消息的添加速度,同時不會阻塞主線程。