2010-08-12 4 views
5

WPF RichTextBox를 사용하는 워드 프로세서 유형 응용 프로그램에서 작업하고 있습니다.WPF RichTextBox SelectionChanged 성능

private void richTextBox_SelectionChanged(object sender, RoutedEventArgs e) 
    { 
     TextSelection selection = richTextBox.Selection; 

     if (selection.GetPropertyValue(FontFamilyProperty) != DependencyProperty.UnsetValue) 
     { 
      //we have a single font in the selection 
      SelectionFontFamily = (FontFamily)selection.GetPropertyValue(FontFamilyProperty); 
     } 
     else 
     { 
      SelectionFontFamily = null; 
     } 

     if (selection.GetPropertyValue(FontWeightProperty) == DependencyProperty.UnsetValue) 
     { 
      SelectionIsBold = false; 
     } 
     else 
     { 
      SelectionIsBold = (FontWeights.Bold == ((FontWeight)selection.GetPropertyValue(FontWeightProperty))); 
     } 

     if (selection.GetPropertyValue(FontStyleProperty) == DependencyProperty.UnsetValue) 
     { 
      SelectionIsItalic = false; 
     } 
     else 
     { 
      SelectionIsItalic = (FontStyles.Italic == ((FontStyle)selection.GetPropertyValue(FontStyleProperty))); 
     } 

     if (selection.GetPropertyValue(Paragraph.TextAlignmentProperty) != DependencyProperty.UnsetValue) 
     { 
      SelectionIsLeftAligned = (TextAlignment)selection.GetPropertyValue(Paragraph.TextAlignmentProperty) == TextAlignment.Left; 
      SelectionIsCenterAligned = (TextAlignment)selection.GetPropertyValue(Paragraph.TextAlignmentProperty) == TextAlignment.Center; 
      SelectionIsRightAligned = (TextAlignment)selection.GetPropertyValue(Paragraph.TextAlignmentProperty) == TextAlignment.Right; 
      SelectionIsJustified = (TextAlignment)selection.GetPropertyValue(Paragraph.TextAlignmentProperty) == TextAlignment.Justify; 
     }    
    } 

등 SelectionFontFamily, SelectionIsBold, 각각이다 : 나는 등 어떤 글꼴, 글꼴 두께, 스타일, 다음 코드를 사용하여 RTB의 현재 선택입니다을 파악하기 위해 인 selectionchanged 이벤트를 사용하고 있습니다 바인딩 모드가 OneWayToSource 인 Hosting UserControl의 DependencyProperty입니다. 그것들은 ViewModel에 바인딩됩니다. ViewModel에는 Font 콤보 상자, 굵게, 기울임 꼴, 밑줄 등의 컨트롤이있는 뷰가 있습니다. RTB의 선택 항목이 변경되면 해당 컨트롤도 업데이트되어 선택된 항목을 반영합니다. 이것은 잘 작동합니다.

많은 양의 텍스트를 선택할 때 성능에 영향을 미치지 만 불행히도 작동합니다. 모든 것을 선택하는 것이 눈에 띄게 느리며 Shift + Arrow 키를 사용하여 선택을 변경하는 것은 매우 느립니다. 허용하기에는 너무 느립니다.

내가 잘못 했나요? 프로세스에서 RTB의 성능을 죽이지 않고 RTB에서 선택한 텍스트의 속성을 바운드 컨트롤에 반영하는 방법에 대한 제안이 있습니까? 성능 문제의

답변

9

당신의 두 가지 주요 원인은 다음과 같습니다 당신은 선택이

GetPropertyValue 변경 때마다 재 계산 당신은 필요

  • 보다 selection.GetPropertyValue() 번 이상 전화

    1. () 메서드는 문서의 모든 요소를 ​​내부적으로 검사해야하므로 속도가 느려집니다. 설정을 업데이트하지 않습니다

      private void HandleSelectionChange() 
      { 
          var family = selection.GetPropertyValue(FontFamilyProperty); 
          var weight = selection.GetPropertyValue(FontWeightProperty); 
          var style = selection.GetPropertyValue(FontStyleProperty); 
          var align = selection.GetPropertyValue(Paragraph.TextAlignmentProperty); 
      
          var unset = DependencyProperty.UnsetValue; 
      
          SelectionFontFamily = family!=unset ? (FontFamily)family : null; 
          SelectionIsBold = weight!=unset && (FontWeight)weight == FontWeight.Bold; 
          SelectionIsItalic = style!=unset && (FontStyle)style == FontStyle.Italic; 
      
          SelectionIsLeftAligned = align!=unset && (TextAlignment)align == TextAlignment.Left;  
          SelectionIsCenterAligned = align!=unset && (TextAlignment)align == TextAlignment.Center;  
          SelectionIsRightAligned = align!=unset && (TextAlignment)align == TextAlignment.Right; 
          SelectionIsJustified = align!=unset && (TextAlignment)align == TextAlignment.Justify; 
      } 
      

      이 약 3 배 빨라집니다하지만 최종 사용자에게 정말 물어 느낄 수 있도록 : 그래서 그 대신 같은 인수를 여러 번 호출의 반환 값을 저장 모든 변화에 즉시. 대신, ContextIdle에 업데이트 :이 실제로 선택 변경을 처리 할 수있는 HandleSelctionChanged() 방법 (위) 호출하지만 ContextIdle 디스패처의 우선 순위가 될 때까지 호출을 지연도없이 변경 이벤트가 와서 얼마나 많은 선택 단지 하나의 업데이트를 큐에없는

      bool _queuedChange; 
      
      private void richTextBox_SelectionChanged(object sender, RoutedEventArgs e) 
      { 
          if(!_queuedChange) 
          { 
          _queuedChange = true; 
          Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, (Action)(() => 
          { 
           _queuedChange = false; 
           HandleSelectionChange(); 
          })); 
          } 
      } 
      

      에.

      추가 속도 향상이 가능

      위의 코드는 여전히 "지연"만큼 네 개의 통화를 할 수 있다는 것을 의미 하나의 DispatcherOperation에 네 GetPropertyValue한다. 지연을 4 배 더 줄이려면 DispatcherOperation 당 하나의 GetPropertyValue 만 작성하십시오. 예를 들어 첫 번째 DispatcherOperation은 GetPropertyValue (FontFamilyProperty)를 호출하고 필드에 결과를 저장하며 다음 DispatcherOperation을 예약하여 글꼴 가중치를 가져옵니다. 이후의 각 DispatcherOperation은 동일한 작업을 수행합니다.

      이 추가 속도 향상이 아직 충분하지 않은 경우 다음 단계는 선택 항목을 더 작은 조각으로 분할하고 별도의 DispatcherOperation에서 각 조각에 대해 GetPropertyValue를 호출 한 다음 얻은 결과를 결합하는 것입니다.

      절대적인 최대 부드러움을 얻으려면 점진적으로 작동하는 GetPropertyValue (선택 항목의 ContentElements 만 반복)에 대한 코드를 구현하고 100 개의 요소를 확인한 후에 반환 할 수 있습니다.다음에 전화를 걸면 중단 된 부분부터 다시 시작합니다. 이렇게하면 DispatcherOperation 당 수행되는 작업량을 다양 화하여 식별 가능한 지연을 방지 할 수 있습니다.

      스레딩 도움이 되겠습니까?

      스레딩 사용이 가능한지 여부는 주석에서 질문하십시오. 그 대답은 스레드를 사용하여 작업을 조정할 수 있지만 항상 GetPropertyValue를 호출하기 위해 주 스레드로 Dispatcher.Invoke를 수행해야하므로 각 GetPropertyValue 호출의 전체 기간 동안 UI 스레드를 차단하므로 그 세분성 여전히 문제입니다. 다른 말로하면 스레딩은 일을 한 입 크기 덩어리로 나눌 수있는 상태 시스템을 사용하지 않는 기능을 제외하고는 실제로 아무것도 구입하지 않습니다.

  • +0

    귀하의 코드 주셔서 감사합니다. 실제로 속도가 빨라졌지만, RTB에 텍스트가 적당 할 때 (여전히 15 페이지 정도) 꽤 가끔 뒤떨어져 있습니다. 모든 텍스트를 강조 표시하고 화살표 키를 사용하여 줄/단어의 선택을 취소하면 여전히 눈에 띄지 않을 정도로 지연됩니다. 그래서 더 낫지 만 아직 거기에 없습니다. 이와 비슷한 것을 스레드에 넣을 수 있습니까? – Scott

    +0

    나는 더 빠른 속도 향상을 위해 무엇이 필요한지, 그리고 스레드가 도움이되는지 아닌지에 대한 아이디어를 내 대답으로 확장했다. –

    +0

    훌륭한 조언, 감사합니다. 레이. 나는 당신에게 제안을 좀 더 자세히 살펴볼 것입니다. – Scott