2017-02-24 1 views
0

우리 팀에서 빌드 도구로 사용하는 이전 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에 익숙하지 않고 바인딩에 새로운 것이므로이 작업을 어떻게 수행 할 것인지 잘 모르겠습니다.

또한 누구나 내가하려고하는 것을 효율적으로 수행하는 방법에 대해 더 좋은 제안이 있다면, 그렇게 좋을 것입니다. 내 목표는 가능한 한 많이 추가되는 로그 메시지의 속도를 따라갈 수 있고 주 스레드를 차단하지 않는 것입니다.

답변

0

WPF RichTextBoxBinding에서 IEnumerable을 지원하지 않습니다. 그러나 일반 텍스트로 작업하는 경우 필요하지 않을 수도 있습니다. 대신, 그 ItemTemplateTextBlock, 그 ItemsPanelVirtualizingStackPanel와 더불어, ItemsControl를 사용할 수 있습니다 쉽게 문자열 (메시지)의 ObservableCollection에이를 결합 할 수

<ItemsControl ItemsSource="{Binding Messages}" > 
    <ItemsControl.ItemTemplate> 
     <DataTemplate> 
      <TextBlock Text="{Binding}" TextWrapping="Wrap" /> 
     </DataTemplate> 
    </ItemsControl.ItemTemplate> 
    <ItemsControl.ItemsPanel> 
     <ItemsPanelTemplate> 
      <VirtualizingStackPanel IsItemsHost="True"/> 
     </ItemsPanelTemplate> 
    </ItemsControl.ItemsPanel> 
</ItemsControl> 

. 쉽게 관찰 수집을 열거하고 전체 텍스트를 얻을 수 있지만, 텍스트 선택에 관한 이 제한되어 기능, & 붙여 넣기 등이 그렇다

, based on this answer 복사, 당신이 (첨부 DocumentXaml를 만들 수 있다는 것 또는 DocumentRTF) 속성을 사용하면 RichTextBox의 문서를 바인딩 할 수 있습니다.

관련 문제