2012-02-29 17 views
7

Tengo algunos problemas para descifrar este, espero que alguien pueda ayudar.Manteniendo el cursor FlowDocument centrado verticalmente en RichTextBox

Tengo un proyecto WPF con un RichTextBox.

Al editar el texto, me gustaría que el cursor en el documento permanezca siempre centrado verticalmente.

Por ejemplo, al presionar hacia arriba o hacia abajo durante la edición, en lugar de que el cursor vaya hacia arriba, me gustaría que el texto baje. Esto debería dar como resultado la impresión de que el cursor permanece quieto.

Muchas gracias.

+0

¿Cómo se puede hacer eso .. Su aplicación y de texto son fijos. si necesita cambiar la posición del texto, necesita seguir agregando espacio al inicio y al recuperar solo recortar el texto – om471987

+0

Definitivamente es posible, creo que casi tengo una solución de trabajo pero un poco más de retoque antes de publicar algo, también lo haría desea ver mejores enfoques – shenku

+0

¿Me puede mostrar una muestra? Trataré de descubrir la forma en que – om471987

Respuesta

0

así que estoy tratando algo en este sentido, pero no han conseguido todavía trabajando, sólo ocupado con otra cosa:

TextPointer start = flowDocument.ContentStart; 
     TextPointer caretPosition = RichTextBox1.CaretPosition; 

     var offset = start.GetOffsetToPosition(caretPosition); 
     RichTextBox1.ScrollToVerticalOffset(offset); 
3

No estoy seguro si esto es lo que tenía en mente, pero aquí es una prueba de concepto para un RichTextBox que mantiene el cursor centrado donde el usuario lo coloca (hace clic en el cuadro).

Aunque, como dijo Omkar, tendrá que agregar espacios en blanco si el documento ha sido desplazado al principio o al final, necesita agregar blanco para permitir que el texto se desplace.

<RichTextBox HorizontalAlignment="Left" Height="311" VerticalAlignment="Top" Width="509" PreviewKeyDown="HandleKeyDownEvent"> 
      <FlowDocument> 
       <Paragraph Margin="0"> 
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla turpis sem, tincidunt id vestibulum venenatis, fermentum eget orci. Donec mollis neque ac leo tincidunt tempus. Pellentesque mollis, nunc sit amet fermentum rutrum, lectus augue ultrices nibh, at lacinia est est ut justo. Cras non quam eu enim vulputate porttitor eu sit amet lectus. Suspendisse potenti. Maecenas metus nunc, dapibus id dapibus rhoncus, semper quis leo. Pellentesque eget risus magna, dignissim aliquam diam. Morbi. 
       </Paragraph> 
      </FlowDocument> 
     </RichTextBox> 

En el código subyacente:

private void HandleKeyDownEvent(object sender, KeyEventArgs e) 
     { 
      RichTextBox rtb = sender as RichTextBox; 
      if (rtb != null) 
      { 
       //text to scroll up relative to caret 
       if (e.Key == Key.Down) 
       { 
        Block paragraph; 

        //get the whitespace paragraph at end of documnent 
        paragraph = 
          rtb.Document.Blocks 
           .Where(x => x.Name == "lastParagraph") 
           .FirstOrDefault(); 

        // if there is no white space paragraph create it 
        if (paragraph == null) 
        { 
         paragraph = new Paragraph { Name = "lastParagraph", Margin = new Thickness(0) }; 

         //add to the end of the document 
         rtb.Document.Blocks.InsertAfter(rtb.Document.Blocks.LastBlock, paragraph); 
        } 

        // if viewport larger than document, add whitespace content to fill view port 
        if (rtb.ExtentHeight < rtb.ViewportHeight) 
        { 
         Thickness margin = new Thickness() { Top = rtb.ViewportHeight - rtb.ExtentHeight }; 
           margin.Bottom = rtb.ViewportHeight - rtb.ExtentHeight; 
           paragraph.Margin = margin; 

        } 

        // if the document has been scrolled to the end or doesn't fill the view port 
        if (rtb.VerticalOffset + rtb.ViewportHeight == rtb.ExtentHeight) 
        { 
         // and a line to the white paragraph 
         paragraph.ContentEnd.InsertLineBreak(); 
        } 

        //move the text up relative to caret 
        rtb.LineDown(); 
       } 
       // text is to scroll download relative to caret 
       if (e.Key == Key.Up) 
       { 
        // get whitespace at start of document 
        Block paragraph; 
        paragraph = 
          rtb.Document.Blocks 
           .Where(x => x.Name == "firstParagraph") 
           .FirstOrDefault(); 

        //if whitespace paragraph is null append a new one 
        if (paragraph == null) 
        { 
         paragraph = new Paragraph { Name = "firstParagraph", Margin = new Thickness(0) }; 
         rtb.Document.Blocks.InsertBefore(rtb.Document.Blocks.FirstBlock, paragraph); 
        } 

        // up document is at top add white space 
        if (rtb.VerticalOffset == 0.0) 
        { 
         paragraph.ContentStart.InsertLineBreak(); 
        } 

        //move text one line down relative to caret 
        rtb.LineUp(); 
       } 
      } 
     } 

EDIT: Este enfoque parece funcionar. La altura de la línea se determina usando la diferencia entre la parte superior de una línea a la siguiente, evitando el problema de tener un salto de línea que afecta al desplazamiento.

<RichTextBox 
     PreviewKeyDown="PreviewKeyDownHandler"> 
     <FlowDocument> 
      <!-- Place content here --> 
     </FlowDocument> 
    </RichTextBox> 

En el código subyacente:

private void PreviewKeyDownHandler(object sender, KeyEventArgs e) 
    {    
     RichTextBox rtb = sender as RichTextBox; 
     if (rtb != null) 
     { 
      if (e.Key == Key.Down) 
      { 
       // if there is another line below current 
       if (rtb.CaretPosition.GetLineStartPosition(0) != rtb.CaretPosition.GetLineStartPosition(1)) 
       { 
        // find the FlowDocumentView through reflection 
        FrameworkElement flowDocumentView = GetFlowDocument(rtb); 

        // get the content bounds of the current line 
        Rect currentLineBounds = rtb.CaretPosition.GetCharacterRect(LogicalDirection.Forward); 

        // move the caret down to next line 
        EditingCommands.MoveDownByLine.Execute(null, rtb); 

        // get the content bounds of the new line 
        Rect nextLineBounds = rtb.CaretPosition.GetCharacterRect(LogicalDirection.Forward); 

        // get the offset the document 
        double currentDocumentOffset = flowDocumentView.Margin.Top; 

        // add the height of the previous line to the offset 
        // the character rect of a line doesn't include the baseline offset so the actual height of line has to be determined 
        // from the difference in the offset between the tops of the character rects of the consecutive lines 
        flowDocumentView.Margin = new Thickness { Top = currentDocumentOffset + currentLineBounds.Top - nextLineBounds.Top }; 
       } 

       // prevent default behavior 
       e.Handled = true; 
      } 
      if (e.Key == Key.Up) 
      { 
       if (rtb.CaretPosition.GetLineStartPosition(0) != rtb.CaretPosition.GetLineStartPosition(-1)) 
       { 
        FrameworkElement flowDocumentView = GetFlowDocument(rtb); 

        Rect currentLineBounds = rtb.CaretPosition.GetCharacterRect(LogicalDirection.Forward); 

        EditingCommands.MoveUpByLine.Execute(null, rtb); 

        Rect nextLineBounds = rtb.CaretPosition.GetCharacterRect(LogicalDirection.Forward); 

        double currentDocumentOffset = flowDocumentView.Margin.Top; 

        flowDocumentView.Margin = new Thickness { Top = currentDocumentOffset + currentLineBounds.Top - nextLineBounds.Top }; 
       } 

       e.Handled = true; 
      } 
     } 
    } 

    protected FrameworkElement GetFlowDocument(RichTextBox textBox) 
    { 
     FrameworkElement flowDocumentVisual = 
      GetChildByTypeName(textBox, "FlowDocumentView") as FrameworkElement; 

     return flowDocumentVisual; 
    } 

    protected DependencyObject GetChildByTypeName(DependencyObject dependencyObject, string name) 
    { 
     if (dependencyObject.GetType().Name == name) 
     { 
      return dependencyObject; 
     } 
     else 
     { 
      if (VisualTreeHelper.GetChildrenCount(dependencyObject) > 0) 
      { 
       int childCount = VisualTreeHelper.GetChildrenCount(dependencyObject); 

       for (int idx = 0; idx < childCount; idx++) 
       { 
        var dp = GetChildByTypeName(VisualTreeHelper.GetChild(dependencyObject, idx), name); 
        if (dp != null) 
         return dp; 
       } 

       return null; 
      } 
      else 
      { 
       return null; 
      } 
     } 
    } 
+0

Hola, KeyDown Event no se activa para la clave 'Key.Down' con la que está comprobando. No parece tener un efecto si paso al código tampoco. – shenku

+0

@shenku: Sí, el RichTextBox se traga el evento KeyDown, es por eso que utilizo el evento PreviewKeyDown, es un evento de túnel que significa que se activó antes de ejecutar el controlador KeyDown de RichTextBox y no es absorbido por el RTB. Verifique la declaración xaml para RichTextBox. –

+0

casi allí, esto funciona pero con el tiempo a medida que avanzas lentamente gana, también sube el documento en lugar de permanecer en su lugar. Parece saltar por los saltos de línea. : S – shenku

Cuestiones relacionadas