우리 팀에서 빌드 도구로 사용하는 이전 WPF 응용 프로그램을 적용하려고합니다. 하나의 구성 요소는 MSBuild ILogger를 통해 빌드 로그 텍스트를 전달하는 FlowDocument가 포함 된 LoggingWindow 클래스입니다. 이 문서는 일반적으로 빌드가 완료 될 때까지 1000 페이지가 넘게 끝나며 빌드가 진행됨에 따라 100,000 라인이 넘는 로그 텍스트가 수신됩니다. 엄청나게 많은 텍스트가 전달되기 때문에 잘 수행되지 않습니다.문자열 컬렉션을 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. 보시다시피, 나는 performantly 문서에 performantly 메시지를 추가하는 방법을 모르겠다 그래서 나는 메시지를 잡으면 10 밀리미터 및 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 방법은 주 스레드에서 실행해야합니다, 그래서 우리는 단락을 추가하기 전에 Dispatcher를 확인해야합니다.
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에 실제 UI 업데이트를 ObservableCollection에에 LogMessage를 추가하고 연기 할 수 있도록 WPF 데이터의 사용은 결합하게하는 솔루션이 리팩토링 싶습니다
나는 수동으로 할 수있다. 그러나 WPF에 익숙하지 않고 바인딩에 새로운 것이므로이 작업을 어떻게 수행 할 것인지 잘 모르겠습니다.
또한 누구나 내가하려고하는 것을 효율적으로 수행하는 방법에 대해 더 좋은 제안이 있다면, 그렇게 좋을 것입니다. 내 목표는 가능한 한 많이 추가되는 로그 메시지의 속도를 따라갈 수 있고 주 스레드를 차단하지 않는 것입니다.