2012-02-29 15 views
7

Ho un po 'di problemi a capire questo, speriamo che qualcuno possa aiutare.Mantenere il cursore di FlowDocument centrato verticalmente nel RichTextBox

Ho un progetto WPF con un RichTextBox.

Mentre modifico il testo, desidero che il cursore nel documento rimanga sempre centrato verticalmente.

Ad esempio come una spinta verso l'alto o verso il basso durante la modifica, piuttosto che il cursore in su, vorrei che il testo venisse giù. Ciò dovrebbe dare l'impressione che il cursore stia fermo.

Grazie mille.

+0

Come si può fare questo .. L'applicazione e testo sono fissi. se devi cambiare la posizione del testo devi continuare ad aggiungere spazio all'inizio e mentre recuperi solo tagliare il testo – om471987

+0

È sicuramente possibile, penso di avere quasi una soluzione funzionante ma un po 'più di armeggiare prima di postare qualsiasi cosa, inoltre piace vedere approcci migliori – shenku

+0

Puoi mostrarmi il campione? Voglio provare a capire come funziona – om471987

risposta

0

Così sto cercando qualcosa in questo senso, ma non hanno ottenuto che funziona ancora, appena occupato con qualcos'altro:

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

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

Non so se questo è ciò che si aveva in mente, ma ecco una prova di concetto per un RichTextBox che mantiene il punto di riferimento centrato dove l'utente lo posiziona (clic nella casella).

Anche se Omkar ha detto che è necessario aggiungere spazi bianchi se il documento è stato spostato all'inizio o alla fine è necessario aggiungere del bianco per consentire al testo di scorrere.

<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> 

nel codice dietro:

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: Questo approccio sembra funzionare. L'altezza della linea viene determinata utilizzando la differenza tra la parte superiore di una linea e quella successiva, evitando il problema di avere un'interruzione di riga che influisce sull'offset.

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

nel codice dietro:

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

Salve, l'evento KeyDown non viene attivato per la chiave 'Key.Down' che stai verificando. Non sembra avere un effetto se passo il codice neanche. – shenku

+0

@shenku: Sì, RichTextBox inghiotte l'evento KeyDown, ecco perché utilizzo l'evento PreviewKeyDown, si tratta di un evento di tunneling che lo ha generato prima dell'esecuzione del gestore KeyDown di RichTextBox e non viene inghiottito dall'RTB. Controlla la dichiarazione xaml per RichTextBox. –

+0

quasi lì, funziona ma con il passare del tempo mentre lo si scorre guadagna lentamente, ovvero sposta il documento anziché rimanere al suo posto. Sembra saltare le interruzioni di linea. : S – shenku